Files
AppleHillsProduction/Assets/Scripts/Minigames/DivingForPictures/Monster/Monster.cs

195 lines
6.0 KiB
C#

using UnityEngine;
using System;
using System.Collections;
using Core;
namespace Minigames.DivingForPictures
{
public class Monster : MonoBehaviour
{
[Header("References")]
[SerializeField] private CircleCollider2D detectionCollider;
private bool pictureAlreadyTaken = false;
private bool photoSequenceInProgress = false;
private UnityEngine.Camera mainCamera;
// Track if player is in detection range
private bool playerInDetectionRange = false;
// Events
public event Action<Monster> OnMonsterDespawned;
public event Action<Monster> OnPlayerEnterDetectionRange;
public event Action<Monster> OnPlayerExitDetectionRange;
// Properties
public float PictureRadius => detectionCollider != null ? detectionCollider.radius : 0f;
public bool IsPhotoSequenceInProgress => photoSequenceInProgress;
public bool IsPlayerInDetectionRange => playerInDetectionRange;
public bool IsPictureTaken => pictureAlreadyTaken;
private void Awake()
{
Logging.Debug("Monster created: " + gameObject.name);
if (detectionCollider == null)
detectionCollider = GetComponent<CircleCollider2D>();
mainCamera = UnityEngine.Camera.main;
// Start checking if monster is off-screen
StartCoroutine(CheckIfOffScreen());
}
private void OnEnable()
{
pictureAlreadyTaken = false;
photoSequenceInProgress = false;
}
private void OnDestroy()
{
Logging.Debug("Monster destroyed: " + gameObject.name);
}
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)
{
// Check if it's the player
if (other.CompareTag("Player") && !pictureAlreadyTaken)
{
playerInDetectionRange = true;
// Fire the event so the game manager can display the viewfinder without pausing
OnPlayerEnterDetectionRange?.Invoke(this);
}
}
private void OnTriggerExit2D(Collider2D other)
{
// 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);
}
}
/// <summary>
/// Mark this monster as having its photo sequence in progress
/// </summary>
public void SetPhotoSequenceInProgress(bool inProgress = true)
{
if (pictureAlreadyTaken) return;
photoSequenceInProgress = inProgress;
}
/// <summary>
/// Notify that a picture has been taken of this monster
/// This is now called by DivingGameManager instead of handling the logic internally
/// </summary>
public void NotifyPictureTaken()
{
if (pictureAlreadyTaken) return;
pictureAlreadyTaken = true;
photoSequenceInProgress = false;
}
/// <summary>
/// Despawn this monster (can be called externally)
/// </summary>
/// <param name="immediate">Whether to despawn immediately or after a delay</param>
public void Despawn(bool immediate = false)
{
if (immediate)
{
DespawnMonster();
}
else
{
// Add small delay before despawning
StartCoroutine(DelayedDespawn());
}
}
/// <summary>
/// Coroutine to despawn after a short delay
/// </summary>
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);
}
}
#if UNITY_EDITOR
// Update collider radius in editor
private void OnValidate()
{
if (detectionCollider == null)
detectionCollider = GetComponent<CircleCollider2D>();
}
#endif
}
}