public Tween AnimateAppearance(ChainSegment previousSegment)
        {
            Sequence seq = DOTween.Sequence();

            seq.Join(wideLinkTop.DOFade(1f, fadeInDuration));
            seq.Join(wideLinkBottom.DOFade(1f, fadeInDuration));

            var topRectTransform    = wideLinkTop.GetComponent <RectTransform>();
            var bottomRectTransform = wideLinkBottom.GetComponent <RectTransform>();

            seq.Join(topRectTransform.DOAnchorPosY(0, snapDuration).SetEase(Ease.InBack));
            seq.Join(bottomRectTransform.DOAnchorPosY(0, snapDuration).SetEase(Ease.InBack));
            seq.Join(flatLink.DOFade(1f, snapDuration).SetEase(Ease.InBack));
            if (previousSegment)
            {
                seq.Join(previousSegment.AddShadow());
            }

            seq.AppendCallback(() =>
            {
                wideLinkTop.DeactivateObject();
                wideLinkBottom.DeactivateObject();
                wideLink.ActivateObject();
            });

            return(seq);
        }
        public Tween AddChain()
        {
            Sequence seq = DOTween.Sequence();

            if (currentChainLength >= maxVisibleChains)
            {
                float chainTargetPos    = (maxVisibleChains - currentChainLength - 1) * segmentWidth;
                float chainMoveDistance = chainSegmentRoot.anchoredPosition.x - chainTargetPos;

                seq.Append(chainSegmentRoot
                           .DOAnchorPosX(chainTargetPos, chainMoveDistance / chainSlideSpeed * 2));
            }

            ChainSegment  newSegment    = Instantiate(chainSegmentPrefab, chainSegmentRoot);
            RectTransform rectTransform = newSegment.GetComponent <RectTransform>();

            rectTransform.anchoredPosition = new Vector2(currentChainLength * segmentWidth, 0);

            seq.Append(newSegment.AnimateAppearance(lastSegment));

            if (spikeBallRoot.childCount > 0)
            {
                var spikeBall = spikeBallRoot.GetChild(0).GetComponent <SpikeBall>();
                seq.Append(spikeBall.AnimateDestruction());
            }

            lastSegment = newSegment;
            ++currentChainLength;

            return(seq);
        }
        public Tween ResetChain()
        {
            // This can happen when there are fewer unbroken/nonempty vials than spike balls.
            foreach (Transform spikeBall in spikeBallRoot)
            {
                Destroy(spikeBall.gameObject);
            }

            Sequence seq = DOTween.Sequence();

            float chainTargetPos    = -currentChainLength * segmentWidth;
            float chainMoveDistance = chainSegmentRoot.anchoredPosition.x - chainTargetPos;

            seq.Append(chainSegmentRoot
                       .DOAnchorPosX(chainTargetPos, chainMoveDistance / chainSlideSpeed)
                       .SetEase(Ease.InQuad));

            seq.AppendCallback(() =>
            {
                foreach (Transform segment in chainSegmentRoot)
                {
                    Destroy(segment.gameObject);
                }

                chainSegmentRoot.anchoredPosition *= new Vector2(0, 1);
            });

            float xPos  = spikeBallOffset;
            float delay = seq.Duration() + spikeBallSpawnDelay;

            for (int i = 0; i < maxCracks; ++i)
            {
                float _xPos = xPos;

                var spikeBall = Instantiate(spikeBallPrefab, spikeBallRoot).GetComponent <SpikeBall>();
                seq.Insert(delay, spikeBall.AnimateAppearance(_xPos));

                xPos  += segmentWidth;
                delay += spikeBallSpawnDelay;
            }

            seq.AppendCallback(() =>
            {
                lastSegment        = null;
                currentChainLength = 0;
                nextSpikeBall      = 0;
            });

            return(seq);
        }
        public void RenderInitialChain()
        {
            float xPos = spikeBallOffset;

            for (int i = 0; i < maxCracks; ++i)
            {
                RectTransform spikeBall = Instantiate(spikeBallPrefab, spikeBallRoot);
                spikeBall.anchoredPosition = new Vector2(xPos, 0);
                // ewww
                Image spikeSprite = spikeBall.GetComponent <Image>();
                spikeSprite.SetAlpha(1);
                xPos += segmentWidth;
            }

            lastSegment        = null;
            currentChainLength = 0;
            nextSpikeBall      = 0;
        }