2025-09-19 12:51:25 +02:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Minigames.DivingForPictures
|
|
|
|
|
|
{
|
|
|
|
|
|
public class Monster : MonoBehaviour
|
|
|
|
|
|
{
|
|
|
|
|
|
[Header("References")]
|
|
|
|
|
|
[SerializeField] private CircleCollider2D detectionCollider;
|
|
|
|
|
|
|
|
|
|
|
|
private bool pictureAlreadyTaken = false;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
private bool photoSequenceInProgress = false;
|
|
|
|
|
|
private UnityEngine.Camera mainCamera;
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
2025-10-10 14:31:51 +02:00
|
|
|
|
// Track if player is in detection range
|
|
|
|
|
|
private bool playerInDetectionRange = false;
|
|
|
|
|
|
|
2025-09-19 12:51:25 +02:00
|
|
|
|
// Events
|
|
|
|
|
|
public event Action<Monster> OnMonsterDespawned;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
public event Action<Monster> OnPlayerEnterDetectionRange;
|
|
|
|
|
|
public event Action<Monster> OnPlayerExitDetectionRange;
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
|
|
|
|
|
// Properties
|
|
|
|
|
|
public float PictureRadius => detectionCollider != null ? detectionCollider.radius : 0f;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
public bool IsPhotoSequenceInProgress => photoSequenceInProgress;
|
|
|
|
|
|
public bool IsPlayerInDetectionRange => playerInDetectionRange;
|
|
|
|
|
|
public bool IsPictureTaken => pictureAlreadyTaken;
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
|
|
{
|
2025-10-14 07:09:16 +00:00
|
|
|
|
Debug.Log("Monster created: " + gameObject.name);
|
|
|
|
|
|
|
2025-09-19 12:51:25 +02:00
|
|
|
|
if (detectionCollider == null)
|
|
|
|
|
|
detectionCollider = GetComponent<CircleCollider2D>();
|
|
|
|
|
|
|
2025-10-10 14:31:51 +02:00
|
|
|
|
mainCamera = UnityEngine.Camera.main;
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
|
|
|
|
|
// Start checking if monster is off-screen
|
|
|
|
|
|
StartCoroutine(CheckIfOffScreen());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnEnable()
|
|
|
|
|
|
{
|
|
|
|
|
|
pictureAlreadyTaken = false;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
photoSequenceInProgress = false;
|
2025-09-19 12:51:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 07:09:16 +00:00
|
|
|
|
private void OnDestroy()
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log("Monster destroyed: " + gameObject.name);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-19 12:51:25 +02:00
|
|
|
|
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)
|
2025-10-10 14:31:51 +02:00
|
|
|
|
mainCamera = UnityEngine.Camera.main;
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
|
|
|
|
|
if (mainCamera == null)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2025-09-19 13:46:24 +02:00
|
|
|
|
// Get the world position (will account for parent movement)
|
|
|
|
|
|
Vector3 worldPosition = transform.position;
|
|
|
|
|
|
Vector3 viewportPoint = mainCamera.WorldToViewportPoint(worldPosition);
|
|
|
|
|
|
|
2025-10-14 07:09:16 +00:00
|
|
|
|
// 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)
|
2025-09-19 13:46:24 +02:00
|
|
|
|
float bufferSides = 0.2f;
|
2025-10-14 07:09:16 +00:00
|
|
|
|
bool withinHorizontalBounds = viewportPoint.x > -bufferSides && viewportPoint.x < 1 + bufferSides;
|
2025-09-19 13:46:24 +02:00
|
|
|
|
|
2025-10-14 07:09:16 +00:00
|
|
|
|
return withinHorizontalBounds;
|
2025-09-19 12:51:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnTriggerEnter2D(Collider2D other)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check if it's the player
|
|
|
|
|
|
if (other.CompareTag("Player") && !pictureAlreadyTaken)
|
|
|
|
|
|
{
|
2025-10-10 14:31:51 +02:00
|
|
|
|
playerInDetectionRange = true;
|
|
|
|
|
|
// Fire the event so the game manager can display the viewfinder without pausing
|
|
|
|
|
|
OnPlayerEnterDetectionRange?.Invoke(this);
|
2025-09-19 12:51:25 +02:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-10 14:31:51 +02:00
|
|
|
|
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()
|
2025-09-19 12:51:25 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (pictureAlreadyTaken) return;
|
|
|
|
|
|
|
|
|
|
|
|
pictureAlreadyTaken = true;
|
2025-10-10 14:31:51 +02:00
|
|
|
|
photoSequenceInProgress = false;
|
|
|
|
|
|
}
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
2025-10-10 14:31:51 +02:00
|
|
|
|
/// <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
|
2025-09-19 12:51:25 +02:00
|
|
|
|
DespawnMonster();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Public method to despawn this monster
|
|
|
|
|
|
public void DespawnMonster()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (gameObject.activeSelf)
|
|
|
|
|
|
{
|
|
|
|
|
|
OnMonsterDespawned?.Invoke(this);
|
|
|
|
|
|
gameObject.SetActive(false);
|
|
|
|
|
|
Destroy(gameObject);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-10 14:31:51 +02:00
|
|
|
|
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
|
// Update collider radius in editor
|
|
|
|
|
|
private void OnValidate()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (detectionCollider == null)
|
|
|
|
|
|
detectionCollider = GetComponent<CircleCollider2D>();
|
|
|
|
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|