using UnityEngine; using System.Collections; using Unity.Cinemachine; using Data.CardSystem; using Pixelplacement; using Core; using UI; namespace Interactions { /// /// Special pickup that plays a fancy sequence when collected: /// - Follower picks up normally /// - Pauses follower movement /// - Zooms camera in /// - Grows/pulses the booster pack /// - Animates to backpack icon /// - Gives player a booster pack /// - Restores camera and resumes movement /// public class BoosterPackPickup : Pickup { [Header("Booster Pack Sequence")] [SerializeField] private GameObject glowVisualPrefab; [SerializeField] private CinemachineCamera zoomCamera; [SerializeField] private int boosterPackCount = 1; [Header("Sequence Timing")] [SerializeField] private float cameraBlendWait = 0.3f; [SerializeField] private float growDuration = 0.25f; [SerializeField] private float pulseDuration = 0.3f; [SerializeField] private int pulseCount = 3; [SerializeField] private float disappearDuration = 0.5f; [Header("Animation Settings")] [SerializeField] private float growScale = 1.5f; [SerializeField] private float pulseScale = 1.2f; private GameObject glowInstance; private bool sequencePlaying; protected override bool DoInteraction() { // Let the normal pickup flow happen first bool success = base.DoInteraction(); // If pickup was successful and we're not already playing the sequence, start it // IMPORTANT: We start the coroutine on the FollowerController, not on this object, // because this object gets disabled immediately after pickup if (success && !sequencePlaying) { var follower = FollowerController.FindInstance(); if (follower != null) { follower.StartCoroutine(BoosterSequence(follower)); } else { Logging.Warning("[BoosterPackPickup] No follower found, cannot start sequence"); } } return success; } private IEnumerator BoosterSequence(FollowerController follower) { sequencePlaying = true; // IMPORTANT: Disable all children on the original pickup GameObject // This prevents glow effects and other child objects from appearing in Pulver's hand foreach (Transform child in transform) { child.gameObject.SetActive(false); } // Wait one frame to ensure pickup is fully processed yield return null; // Verify follower still exists (safety check) if (follower == null) { Logging.Warning("[BoosterPackPickup] Follower destroyed during sequence, aborting"); sequencePlaying = false; yield break; } Transform heldItemTransform = follower.GetHeldItemTransform(); if (heldItemTransform == null) { Logging.Warning("[BoosterPackPickup] No held item transform found, skipping sequence"); sequencePlaying = false; yield break; } // 1. Pause follower movement follower.PauseMovement(); // 2. Activate zoom camera if assigned if (zoomCamera != null) { // Unparent the camera from the booster pack (if it's parented) zoomCamera.transform.SetParent(null); // Position camera at follower's location zoomCamera.transform.position = new Vector3( follower.transform.position.x, follower.transform.position.y, zoomCamera.transform.position.z ); // Make the blend instant by setting a custom blend hint var brain = Camera.main?.GetComponent(); CinemachineBlendDefinition originalBlend = default; if (brain != null) { originalBlend = brain.DefaultBlend; brain.DefaultBlend = new CinemachineBlendDefinition(CinemachineBlendDefinition.Styles.EaseInOut, 1); } zoomCamera.Priority = 20; zoomCamera.gameObject.SetActive(true); // Wait a frame for the cut to take effect yield return null; // Restore original blend settings if (brain != null) { brain.DefaultBlend = originalBlend; } } // 3. Grow animation (no glow during this) Vector3 originalScale = heldItemTransform.localScale; Tween.LocalScale(heldItemTransform, originalScale * growScale, growDuration, 0f, Tween.EaseOutBack); yield return new WaitForSeconds(growDuration); // 4. Instantiate and pulse glow if (glowVisualPrefab != null) { glowInstance = Instantiate(glowVisualPrefab, heldItemTransform); glowInstance.transform.localPosition = Vector3.zero; glowInstance.transform.localScale = Vector3.one; for (int i = 0; i < pulseCount; i++) { Tween.LocalScale(glowInstance.transform, Vector3.one * pulseScale, pulseDuration, 0f, Tween.EaseIn); yield return new WaitForSeconds(pulseDuration); Tween.LocalScale(glowInstance.transform, Vector3.one, pulseDuration, 0f, Tween.EaseInOut); yield return new WaitForSeconds(pulseDuration); } // Delete glow before flying to button Destroy(glowInstance); glowInstance = null; } // 5. Disappear to scrapbook button (backpack icon) - straight line movement GameObject scrabookButton = PlayerHudManager.Instance?.GetScrabookButton(); if (scrabookButton != null) { // Get start position in world space Vector3 startPos = heldItemTransform.position; // Convert UI button position to world space RectTransform buttonRect = scrabookButton.GetComponent(); Vector3 targetWorldPos; if (buttonRect != null) { // UI element - get screen position correctly Vector3[] corners = new Vector3[4]; buttonRect.GetWorldCorners(corners); // Get center of the button in screen space Vector3 buttonCenter = (corners[0] + corners[2]) / 2f; // For Screen Space Overlay, the corners are already in screen space Canvas canvas = buttonRect.GetComponentInParent(); Vector3 buttonScreenPos; if (canvas != null && canvas.renderMode == RenderMode.ScreenSpaceOverlay) { // Already in screen space buttonScreenPos = buttonCenter; } else { // Convert from world to screen space buttonScreenPos = RectTransformUtility.WorldToScreenPoint(canvas?.worldCamera ?? Camera.main, buttonCenter); } // Convert screen position to world space at the same Z as the booster pack targetWorldPos = Camera.main.ScreenToWorldPoint(new Vector3(buttonScreenPos.x, buttonScreenPos.y, Mathf.Abs(Camera.main.transform.position.z - startPos.z))); Logging.Debug($"[BoosterPackPickup] Button screen pos: {buttonScreenPos}, converted world pos: {targetWorldPos}"); } else { // Not a UI element, use direct world position targetWorldPos = scrabookButton.transform.position; } Logging.Debug($"[BoosterPackPickup] Flying from {startPos} to {targetWorldPos} (button: {scrabookButton.name})"); // Use custom tween with linear interpolation for straight line float elapsed = 0f; Vector3 startScale = heldItemTransform.localScale; while (elapsed < disappearDuration) { elapsed += Time.deltaTime; float t = Mathf.Clamp01(elapsed / disappearDuration); // Linear interpolation for straight line movement heldItemTransform.position = Vector3.Lerp(startPos, targetWorldPos, t); // Ease in for scale (gets smaller as it approaches) float scaleT = t * t; // Simple ease-in curve heldItemTransform.localScale = Vector3.Lerp(startScale, Vector3.zero, scaleT); yield return null; } // Ensure final position/scale heldItemTransform.position = targetWorldPos; heldItemTransform.localScale = Vector3.zero; } else { Logging.Warning("[BoosterPackPickup] No scrapbook button found from PlayerHudManager, skipping fly-to animation"); } // 7. Give booster pack to player if (CardSystemManager.Instance != null) { CardSystemManager.Instance.AddBoosterPack(boosterPackCount); Logging.Debug($"[BoosterPackPickup] Gave {boosterPackCount} booster pack(s) to player"); } else { Logging.Warning("[BoosterPackPickup] CardSystemManager not found, cannot give booster pack"); } // 8. Cleanup glow if (glowInstance != null) { Destroy(glowInstance); } // 9. Blend back to main camera and cleanup if (zoomCamera != null) { // Lower priority to blend back to main camera zoomCamera.Priority = 0; // Wait for blend back to complete (using default blend settings) var brain = Camera.main?.GetComponent(); if (brain != null && brain.IsBlending) { // Wait for the blend to finish while (brain.IsBlending) { yield return null; } } else { // If not blending or no brain, just wait a short moment yield return new WaitForSeconds(0.3f); } // Destroy the zoom camera Destroy(zoomCamera.gameObject); } // 10. Clear the follower's held item since we "consumed" it follower.ClearHeldItem(); // 11. Resume follower movement follower.ResumeMovement(); sequencePlaying = false; } internal override void OnManagedDestroy() { base.OnManagedDestroy(); // Cleanup glow if still exists if (glowInstance != null) { Destroy(glowInstance); } } } }