using System.Collections; using Data.CardSystem; using UnityEngine; using UnityEngine.Events; namespace UI.CardSystem { /// /// One-off helper to visually grant a booster pack. /// Place this on a UI GameObject with two Image children (a background "glow" and a booster pack image). /// Access via BoosterPackGiver.Instance and call GiveBoosterPack(). /// The sequence: /// 1) Shows the object (enables children) /// 2) Pulses the glow scale for a fixed duration /// 3) Hides the glow, tweens the booster image towards the backpack icon and scales to zero /// 4) Invokes OnCompleted /// public class BoosterPackGiver : MonoBehaviour { public static BoosterPackGiver Instance { get; private set; } [Header("References")] [Tooltip("Canvas that contains these UI elements. If null, will search up the hierarchy.")] [SerializeField] private Canvas canvas; [Tooltip("Background glow RectTransform (child image)")] [SerializeField] private RectTransform backgroundGlow; [Tooltip("Booster pack image RectTransform (child image)")] [SerializeField] private RectTransform boosterImage; [Tooltip("Target RectTransform for the backpack icon (where the booster flies to)")] [SerializeField] private RectTransform targetBackpackIcon; [Header("Timing")] [Tooltip("How long the glow should pulse before the booster flies to the backpack")] [SerializeField] private float pulseDuration = 2.0f; [Tooltip("Duration of the flight/scale-down animation")] [SerializeField] private float moveDuration = 0.6f; [Header("Glow Pulse")] [Tooltip("Minimum scale during pulse")] [SerializeField] private float glowScaleMin = 0.9f; [Tooltip("Maximum scale during pulse")] [SerializeField] private float glowScaleMax = 1.1f; [Tooltip("Pulse speed in cycles per second")] [SerializeField] private float glowPulseSpeed = 2.0f; [Header("Move/Scale Easing")] [SerializeField] private AnimationCurve moveCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); [SerializeField] private AnimationCurve scaleCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); [Header("Behaviour")] [Tooltip("Hide visuals when the sequence completes")] [SerializeField] private bool hideOnComplete = true; [Header("Events")] public UnityEvent OnCompleted; private Coroutine _sequenceCoroutine; private Vector3 _boosterInitialScale; private Vector2 _boosterInitialAnchoredPos; private IEnumerator Start() { if (Instance != null && Instance != this) { Debug.LogWarning("[BoosterPackGiver] Duplicate instance detected. Destroying this component."); Destroy(this); yield break; } Instance = this; if (canvas == null) { canvas = GetComponentInParent(); } CacheInitialBoosterState(); // Start hidden (keep GameObject active so the singleton remains accessible) SetVisualsActive(false); // yield return new WaitForSeconds(1f); // GiveBoosterPack(); } private void OnDestroy() { if (Instance == this) Instance = null; } private void CacheInitialBoosterState() { if (boosterImage != null) { _boosterInitialScale = boosterImage.localScale; _boosterInitialAnchoredPos = boosterImage.anchoredPosition; } } /// /// Public entry point: run the grant animation. /// public void GiveBoosterPack() { if (backgroundGlow == null || boosterImage == null) { Debug.LogError("[BoosterPackGiver] Missing references. Assign Background Glow and Booster Image in the inspector."); return; } // Reset and start fresh if (_sequenceCoroutine != null) { StopCoroutine(_sequenceCoroutine); _sequenceCoroutine = null; } // Ensure canvas reference if (canvas == null) { canvas = GetComponentInParent(); } // Reset booster transform boosterImage.localScale = _boosterInitialScale; boosterImage.anchoredPosition = _boosterInitialAnchoredPos; // Show visuals SetVisualsActive(true); _sequenceCoroutine = StartCoroutine(RunSequence()); } private IEnumerator RunSequence() { // 1) Pulse the glow float elapsed = 0f; Vector3 baseGlowScale = backgroundGlow.localScale; while (elapsed < pulseDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.Sin(elapsed * Mathf.PI * 2f * glowPulseSpeed) * 0.5f + 0.5f; // 0..1 float s = Mathf.Lerp(glowScaleMin, glowScaleMax, t); backgroundGlow.localScale = baseGlowScale * s; yield return null; } // 2) Hide glow backgroundGlow.gameObject.SetActive(false); // 3) Move booster to backpack icon and scale to zero Vector2 startPos = boosterImage.anchoredPosition; Vector2 targetPos = startPos; // Convert target to booster parent space if available if (targetBackpackIcon != null) { var parentRect = boosterImage.parent as RectTransform; if (parentRect != null) { Vector2 localPoint; Vector2 screenPoint = RectTransformUtility.WorldToScreenPoint(canvas != null ? canvas.worldCamera : null, targetBackpackIcon.position); if (RectTransformUtility.ScreenPointToLocalPointInRectangle(parentRect, screenPoint, canvas != null ? canvas.worldCamera : null, out localPoint)) { targetPos = localPoint; } } } elapsed = 0f; while (elapsed < moveDuration) { elapsed += Time.unscaledDeltaTime; float t = Mathf.Clamp01(elapsed / moveDuration); float mt = moveCurve != null ? moveCurve.Evaluate(t) : t; float st = scaleCurve != null ? scaleCurve.Evaluate(t) : t; boosterImage.anchoredPosition = Vector2.LerpUnclamped(startPos, targetPos, mt); boosterImage.localScale = Vector3.LerpUnclamped(_boosterInitialScale, Vector3.zero, st); yield return null; } // Ensure final state boosterImage.anchoredPosition = targetPos; boosterImage.localScale = Vector3.zero; if (hideOnComplete) { SetVisualsActive(false); // Restore booster for the next run boosterImage.localScale = _boosterInitialScale; boosterImage.anchoredPosition = _boosterInitialAnchoredPos; backgroundGlow.localScale = Vector3.one; // reset pulse scaling } _sequenceCoroutine = null; OnCompleted?.Invoke(); CardSystemManager.Instance.AddBoosterPack(1); } private void SetVisualsActive(bool active) { if (backgroundGlow != null) backgroundGlow.gameObject.SetActive(active); if (boosterImage != null) boosterImage.gameObject.SetActive(active); } // Optional: quick editor hookup to validate references #if UNITY_EDITOR private void OnValidate() { if (canvas == null) { canvas = GetComponentInParent(); } if (boosterImage != null && _boosterInitialScale == Vector3.zero) { _boosterInitialScale = boosterImage.localScale; _boosterInitialAnchoredPos = boosterImage.anchoredPosition; } } #endif } }