using System; using System.Collections; using AudioSourceEvents; using Core; using UnityEngine; using UnityEngine.Audio; namespace Minigames.DivingForPictures.Monster { public class Monster : MonoBehaviour { [Header("References")] [SerializeField] private CircleCollider2D detectionCollider; [SerializeField] private GameObject proximityIndicator; // Visual sprite showing detection range private bool _pictureAlreadyTaken = false; private bool _photoSequenceInProgress = false; private Camera _mainCamera; // Track if player is in detection range private bool _playerInDetectionRange = false; // Events public event Action OnMonsterDespawned; public event Action OnPlayerEnterDetectionRange; public event Action OnPlayerExitDetectionRange; // Properties public float PictureRadius => detectionCollider != null ? detectionCollider.radius : 0f; public bool IsPhotoSequenceInProgress => _photoSequenceInProgress; public bool IsPlayerInDetectionRange => _playerInDetectionRange; public bool IsPictureTaken => _pictureAlreadyTaken; public enum MonsterSounds {Hello, RareBeast, Pooping,Smile,Whatsup1, Whatsup2} private AudioSource _audioSource; public AudioResource[] monsterClips; private AudioSource _playerAudioSource; public AudioResource monsterSpottedAudio; private IAudioEventSource _eventSource; private GameObject _playerObject; private void Awake() { Logging.Debug("Monster created: " + gameObject.name); if (detectionCollider == null) detectionCollider = GetComponent(); _mainCamera = UnityEngine.Camera.main; _audioSource = GetComponent(); _playerObject = GameObject.FindGameObjectsWithTag("Player")[0]; _playerAudioSource = _playerObject.GetComponent(); _eventSource = _playerAudioSource.RequestEventHandlers(); _eventSource.AudioStopped += PlayerAudioDone; // Start checking if monster is off-screen StartCoroutine(CheckIfOffScreen()); } private void PlayerAudioDone(object sender, EventArgs e) { _audioSource.Play(); } private void OnEnable() { _playerAudioSource.resource = monsterSpottedAudio; _playerAudioSource.Play(); _pictureAlreadyTaken = false; _photoSequenceInProgress = false; } private void OnDestroy() { Logging.Debug("Monster destroyed: " + gameObject.name); _eventSource.AudioStopped -= PlayerAudioDone; } private IEnumerator CheckIfOffScreen() { WaitForSeconds wait = new WaitForSeconds(0.5f); while (true) { yield return wait; if (!IsVisibleToCamera()) { DespawnMonster(); yield break; } } } private bool IsVisibleToCamera() { if (_mainCamera == null) _mainCamera = UnityEngine.Camera.main; if (_mainCamera == null) return false; // Get the world position (will account for parent movement) Vector3 worldPosition = transform.position; Vector3 viewportPoint = _mainCamera.WorldToViewportPoint(worldPosition); // If z is negative, the object is behind the camera if (viewportPoint.z < 0) return false; // Simple logic: // 1. Allow monsters below the screen (new spawns) // 2. Despawn monsters only when completely above the top of screen // 3. Keep monsters that are on screen // Check if the monster is above the top of the screen if (viewportPoint.y > 1) return false; // Monster is completely above screen, destroy it // Check horizontal bounds (keep moderate buffer so monsters don't disappear at screen edges) float bufferSides = 0.2f; bool withinHorizontalBounds = viewportPoint.x > -bufferSides && viewportPoint.x < 1 + bufferSides; return withinHorizontalBounds; } private void OnTriggerEnter2D(Collider2D other) { // Don't trigger if picture already taken - prevents re-triggering photo sequence if (_pictureAlreadyTaken) return; // Check if it's the player if (other.CompareTag("Player")) { _playerInDetectionRange = true; // Fire the event so the game manager can display the viewfinder without pausing OnPlayerEnterDetectionRange?.Invoke(this); } } private void OnTriggerExit2D(Collider2D other) { // Don't trigger if picture already taken if (_pictureAlreadyTaken) return; // Check if it's the player if (other.CompareTag("Player")) { _playerInDetectionRange = false; // Fire the event so the game manager can hide the viewfinder OnPlayerExitDetectionRange?.Invoke(this); } } /// /// Mark this monster as having its photo sequence in progress /// public void SetPhotoSequenceInProgress(bool inProgress = true) { if (_pictureAlreadyTaken) return; _photoSequenceInProgress = inProgress; } /// /// Notify that a picture has been taken of this monster /// This is now called by DivingGameManager instead of handling the logic internally /// public void NotifyPictureTaken() { if (_pictureAlreadyTaken) return; _pictureAlreadyTaken = true; _photoSequenceInProgress = false; // Destroy the proximity indicator visual if (proximityIndicator != null) { Destroy(proximityIndicator); proximityIndicator = null; } Logging.Debug($"[Monster] {gameObject.name} picture taken, collider still enabled for debugging"); } /// /// Despawn this monster (can be called externally) /// /// Whether to despawn immediately or after a delay public void Despawn(bool immediate = false) { if (immediate) { DespawnMonster(); } else { // Add small delay before despawning StartCoroutine(DelayedDespawn()); } } /// /// Coroutine to despawn after a short delay /// private IEnumerator DelayedDespawn() { yield return new WaitForSeconds(1.0f); // 1-second delay before despawn DespawnMonster(); } // Public method to despawn this monster public void DespawnMonster() { if (gameObject.activeSelf) { OnMonsterDespawned?.Invoke(this); gameObject.SetActive(false); Destroy(gameObject); } } public void PlayAudio(MonsterSounds soundToPlay) { switch (soundToPlay) { case MonsterSounds.Hello: _audioSource.resource = monsterClips[0]; break; case MonsterSounds.RareBeast: _audioSource.resource = monsterClips[1]; break; case MonsterSounds.Pooping: break; case MonsterSounds.Smile: _audioSource.resource = monsterClips[3]; break; case MonsterSounds.Whatsup1: _audioSource.resource = monsterClips[4]; break; case MonsterSounds.Whatsup2: _audioSource.resource = monsterClips[5]; break; } } #if UNITY_EDITOR // Update collider radius in editor private void OnValidate() { if (detectionCollider == null) detectionCollider = GetComponent(); } #endif } }