using System; using System.Collections; using Core; using Data.CardSystem; using Pixelplacement; using UnityEngine; using UnityEngine.UI; namespace UI.CardSystem { /// /// Singleton UI component for granting booster packs from minigames. /// Displays a booster pack with glow effect, waits for user to click continue, /// then shows the scrapbook button and animates the pack flying to it before granting the reward. /// The scrapbook button is automatically hidden after the animation completes. /// public class MinigameBoosterGiver : MonoBehaviour { public static MinigameBoosterGiver Instance { get; private set; } [Header("Visual References")] [SerializeField] private GameObject visualContainer; [SerializeField] private RectTransform boosterImage; [SerializeField] private RectTransform glowImage; [SerializeField] private Button continueButton; [Header("Animation Settings")] [SerializeField] private float hoverAmount = 20f; [SerializeField] private float hoverDuration = 1.5f; [SerializeField] private float glowPulseMin = 0.9f; [SerializeField] private float glowPulseMax = 1.1f; [SerializeField] private float glowPulseDuration = 1.2f; [Header("Disappear Animation")] [SerializeField] private Vector2 targetBottomLeftOffset = new Vector2(100f, 100f); [SerializeField] private float disappearDuration = 0.8f; [SerializeField] private float disappearScale = 0.2f; private Vector3 _boosterInitialPosition; private Vector3 _boosterInitialScale; private Vector3 _glowInitialScale; private Coroutine _currentSequence; private Action _onCompleteCallback; private void Awake() { // Singleton pattern if (Instance != null && Instance != this) { Logging.Warning("[MinigameBoosterGiver] Duplicate instance found. Destroying."); Destroy(gameObject); return; } Instance = this; // Cache initial values if (boosterImage != null) { _boosterInitialPosition = boosterImage.localPosition; _boosterInitialScale = boosterImage.localScale; } if (glowImage != null) { _glowInitialScale = glowImage.localScale; } // Setup button listener if (continueButton != null) { continueButton.onClick.AddListener(OnContinueClicked); } // Start hidden if (visualContainer != null) { visualContainer.SetActive(false); } } private void OnDestroy() { if (Instance == this) { Instance = null; } if (continueButton != null) { continueButton.onClick.RemoveListener(OnContinueClicked); } } /// /// Public API to give a booster pack. Displays UI, starts animations, and waits for user interaction. /// /// Optional callback when the sequence completes and pack is granted public void GiveBooster(Action onComplete = null) { if (_currentSequence != null) { Logging.Warning("[MinigameBoosterGiver] Already running a sequence. Ignoring new request."); return; } _onCompleteCallback = onComplete; _currentSequence = StartCoroutine(GiveBoosterSequence()); } private IEnumerator GiveBoosterSequence() { // Show the visual if (visualContainer != null) { visualContainer.SetActive(true); } // Reset positions and scales if (boosterImage != null) { boosterImage.localPosition = _boosterInitialPosition; boosterImage.localScale = _boosterInitialScale; } if (glowImage != null) { glowImage.localScale = _glowInitialScale; } // Enable the continue button if (continueButton != null) { continueButton.interactable = true; } // Start idle hovering animation on booster (ping-pong) if (boosterImage != null) { Vector3 hoverTarget = _boosterInitialPosition + Vector3.up * hoverAmount; Tween.LocalPosition(boosterImage, hoverTarget, hoverDuration, 0f, Tween.EaseLinear, Tween.LoopType.PingPong); } // Start pulsing animation on glow (ping-pong scale) if (glowImage != null) { Vector3 glowPulseScale = _glowInitialScale * glowPulseMax; Tween.LocalScale(glowImage, glowPulseScale, glowPulseDuration, 0f, Tween.EaseOut, Tween.LoopType.PingPong); } // Wait for button click (handled by OnContinueClicked) yield return null; } private void OnContinueClicked() { if (_currentSequence == null) { return; // Not in a sequence } // Disable button to prevent double-clicks if (continueButton != null) { continueButton.interactable = false; } // Stop the ongoing animations by stopping all tweens on these objects if (boosterImage != null) { Tween.Stop(boosterImage.GetInstanceID()); } if (glowImage != null) { Tween.Stop(glowImage.GetInstanceID()); // Fade out the glow Tween.LocalScale(glowImage, Vector3.zero, disappearDuration * 0.5f, 0f, Tween.EaseInBack); } // Start disappear animation StartCoroutine(DisappearSequence()); } private IEnumerator DisappearSequence() { if (boosterImage == null) { yield break; } // Show scrapbook button temporarily using HUD visibility context PlayerHudManager.HudVisibilityContext hudContext = null; GameObject scrapbookButton = null; scrapbookButton = PlayerHudManager.Instance.GetScrabookButton(); if (scrapbookButton != null) { hudContext = PlayerHudManager.Instance.ShowElementTemporarily(scrapbookButton); } else { Logging.Warning("[MinigameBoosterGiver] Scrapbook button not found in PlayerHudManager."); } // Calculate target position - use scrapbook button position if available Vector3 targetPosition; if (scrapbookButton != null) { // Get the scrapbook button's position in the same coordinate space as boosterImage RectTransform scrapbookRect = scrapbookButton.GetComponent(); if (scrapbookRect != null) { // Convert scrapbook button's world position to local position relative to boosterImage's parent Canvas canvas = GetComponentInParent(); if (canvas != null && canvas.renderMode == RenderMode.ScreenSpaceOverlay) { // For overlay canvas, convert screen position to local position Vector2 screenPos = RectTransformUtility.WorldToScreenPoint(null, scrapbookRect.position); RectTransformUtility.ScreenPointToLocalPointInRectangle( boosterImage.parent as RectTransform, screenPos, null, out Vector2 localPoint); targetPosition = localPoint; } else { // For world space or camera canvas targetPosition = boosterImage.parent.InverseTransformPoint(scrapbookRect.position); } } else { Logging.Warning("[MinigameBoosterGiver] Scrapbook button has no RectTransform, using fallback position."); targetPosition = GetFallbackPosition(); } } else { // Fallback to bottom-left corner targetPosition = GetFallbackPosition(); } // Tween to scrapbook button position Tween.LocalPosition(boosterImage, targetPosition, disappearDuration, 0f, Tween.EaseInBack); // Scale down Vector3 targetScale = _boosterInitialScale * disappearScale; Tween.LocalScale(boosterImage, targetScale, disappearDuration, 0f, Tween.EaseInBack); // Wait for animation to complete yield return new WaitForSeconds(disappearDuration); // Grant the booster pack if (CardSystemManager.Instance != null) { CardSystemManager.Instance.AddBoosterPack(1); Logging.Debug("[MinigameBoosterGiver] Booster pack granted!"); } else { Logging.Warning("[MinigameBoosterGiver] CardSystemManager not found, cannot grant booster pack."); } // Hide scrapbook button by disposing the context hudContext?.Dispose(); // Hide the visual if (visualContainer != null) { visualContainer.SetActive(false); } // Invoke completion callback _onCompleteCallback?.Invoke(); _onCompleteCallback = null; // Clear sequence reference _currentSequence = null; } private Vector3 GetFallbackPosition() { RectTransform canvasRect = GetComponentInParent()?.GetComponent(); if (canvasRect != null) { // Convert bottom-left corner with offset to local position Vector2 bottomLeft = new Vector2(-canvasRect.rect.width / 2f, -canvasRect.rect.height / 2f); return bottomLeft + targetBottomLeftOffset; } else { // Ultimate fallback if no canvas found return _boosterInitialPosition + new Vector3(-500f, -500f, 0f); } } } }