2025-09-19 12:51:25 +02:00
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using UnityEngine.Events;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Minigames.DivingForPictures
|
|
|
|
|
|
{
|
|
|
|
|
|
public class DivingGameManager : MonoBehaviour
|
|
|
|
|
|
{
|
|
|
|
|
|
[Header("Monster Prefabs")]
|
|
|
|
|
|
[Tooltip("Array of monster prefabs to spawn randomly")]
|
|
|
|
|
|
[SerializeField] private GameObject[] monsterPrefabs;
|
|
|
|
|
|
|
|
|
|
|
|
[Header("Spawn Probability")]
|
|
|
|
|
|
[Tooltip("Base chance (0-1) of spawning a monster on each tile")]
|
|
|
|
|
|
[SerializeField] private float baseSpawnProbability = 0.2f;
|
|
|
|
|
|
[Tooltip("Maximum chance (0-1) of spawning a monster")]
|
|
|
|
|
|
[SerializeField] private float maxSpawnProbability = 0.5f;
|
|
|
|
|
|
[Tooltip("How fast the probability increases per second")]
|
|
|
|
|
|
[SerializeField] private float probabilityIncreaseRate = 0.01f;
|
|
|
|
|
|
|
|
|
|
|
|
[Header("Spawn Timing")]
|
|
|
|
|
|
[Tooltip("Force a spawn after this many seconds without spawns")]
|
|
|
|
|
|
[SerializeField] private float guaranteedSpawnTime = 30f;
|
|
|
|
|
|
[Tooltip("Minimum time between monster spawns")]
|
|
|
|
|
|
[SerializeField] private float spawnCooldown = 5f;
|
|
|
|
|
|
|
|
|
|
|
|
[Header("Scoring")]
|
|
|
|
|
|
[Tooltip("Base points for taking a picture")]
|
|
|
|
|
|
[SerializeField] private int basePoints = 100;
|
|
|
|
|
|
[Tooltip("Additional points per depth unit")]
|
|
|
|
|
|
[SerializeField] private int depthMultiplier = 10;
|
|
|
|
|
|
|
|
|
|
|
|
// Private state variables
|
|
|
|
|
|
private int playerScore = 0;
|
|
|
|
|
|
private float currentSpawnProbability;
|
|
|
|
|
|
private float lastSpawnTime = -100f;
|
|
|
|
|
|
private float timeSinceLastSpawn = 0f;
|
|
|
|
|
|
private List<Monster> activeMonsters = new List<Monster>();
|
|
|
|
|
|
|
|
|
|
|
|
// Public properties
|
|
|
|
|
|
public int PlayerScore => playerScore;
|
|
|
|
|
|
|
|
|
|
|
|
// Events
|
|
|
|
|
|
public event Action<int> OnScoreChanged;
|
|
|
|
|
|
public event Action<Monster> OnMonsterSpawned;
|
|
|
|
|
|
public event Action<Monster, int> OnPictureTaken;
|
|
|
|
|
|
public event Action<float> OnSpawnProbabilityChanged;
|
|
|
|
|
|
|
|
|
|
|
|
private void Awake()
|
|
|
|
|
|
{
|
|
|
|
|
|
currentSpawnProbability = baseSpawnProbability;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Start()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Subscribe to tile spawned event
|
2025-09-19 13:46:24 +02:00
|
|
|
|
TrenchTileSpawner tileSpawner = FindFirstObjectByType<TrenchTileSpawner>();
|
2025-09-19 12:51:25 +02:00
|
|
|
|
if (tileSpawner != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
tileSpawner.onTileSpawned.AddListener(OnTileSpawned);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("No TrenchTileSpawner found in scene. Monster spawning won't work.");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Update()
|
|
|
|
|
|
{
|
|
|
|
|
|
timeSinceLastSpawn += Time.deltaTime;
|
|
|
|
|
|
|
|
|
|
|
|
// Gradually increase spawn probability over time
|
|
|
|
|
|
float previousProbability = currentSpawnProbability;
|
|
|
|
|
|
if (currentSpawnProbability < maxSpawnProbability)
|
|
|
|
|
|
{
|
|
|
|
|
|
currentSpawnProbability += probabilityIncreaseRate * Time.deltaTime;
|
|
|
|
|
|
currentSpawnProbability = Mathf.Min(currentSpawnProbability, maxSpawnProbability);
|
|
|
|
|
|
|
|
|
|
|
|
// Only fire event if probability changed significantly
|
|
|
|
|
|
if (Mathf.Abs(currentSpawnProbability - previousProbability) > 0.01f)
|
|
|
|
|
|
{
|
|
|
|
|
|
OnSpawnProbabilityChanged?.Invoke(currentSpawnProbability);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnTileSpawned(GameObject tile)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Check for spawn points in the new tile
|
|
|
|
|
|
MonsterSpawnPoint[] spawnPoints = tile.GetComponentsInChildren<MonsterSpawnPoint>();
|
|
|
|
|
|
|
|
|
|
|
|
if (spawnPoints.Length == 0) return;
|
|
|
|
|
|
|
|
|
|
|
|
bool forceSpawn = timeSinceLastSpawn >= guaranteedSpawnTime;
|
|
|
|
|
|
bool onCooldown = timeSinceLastSpawn < spawnCooldown;
|
|
|
|
|
|
|
|
|
|
|
|
// Don't spawn if on cooldown, unless forced
|
|
|
|
|
|
if (onCooldown && !forceSpawn) return;
|
|
|
|
|
|
|
|
|
|
|
|
// Check probability or forced spawn
|
|
|
|
|
|
if (forceSpawn || UnityEngine.Random.value <= currentSpawnProbability)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Pick a random spawn point from this tile
|
|
|
|
|
|
MonsterSpawnPoint spawnPoint = spawnPoints[UnityEngine.Random.Range(0, spawnPoints.Length)];
|
|
|
|
|
|
|
2025-09-19 13:46:24 +02:00
|
|
|
|
// Spawn the monster at the spawn point and parent it
|
|
|
|
|
|
SpawnMonster(spawnPoint.transform);
|
2025-09-19 12:51:25 +02:00
|
|
|
|
|
|
|
|
|
|
// Reset timer and adjust probability
|
|
|
|
|
|
lastSpawnTime = Time.time;
|
|
|
|
|
|
timeSinceLastSpawn = 0f;
|
|
|
|
|
|
currentSpawnProbability = baseSpawnProbability;
|
|
|
|
|
|
OnSpawnProbabilityChanged?.Invoke(currentSpawnProbability);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-19 13:46:24 +02:00
|
|
|
|
private void SpawnMonster(Transform spawnPoint)
|
2025-09-19 12:51:25 +02:00
|
|
|
|
{
|
|
|
|
|
|
if (monsterPrefabs.Length == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogWarning("No monster prefabs assigned to DivingGameManager.");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Select random monster prefab
|
|
|
|
|
|
GameObject prefab = monsterPrefabs[UnityEngine.Random.Range(0, monsterPrefabs.Length)];
|
|
|
|
|
|
|
2025-09-19 13:46:24 +02:00
|
|
|
|
// Instantiate monster at spawn point position
|
|
|
|
|
|
GameObject monsterObj = Instantiate(prefab, spawnPoint.position, Quaternion.identity);
|
2025-09-19 12:51:25 +02:00
|
|
|
|
Monster monster = monsterObj.GetComponent<Monster>();
|
|
|
|
|
|
|
|
|
|
|
|
if (monster != null)
|
|
|
|
|
|
{
|
2025-09-19 13:46:24 +02:00
|
|
|
|
// Parent the monster to the spawn point so it moves with the tile
|
|
|
|
|
|
monsterObj.transform.SetParent(spawnPoint);
|
|
|
|
|
|
|
2025-09-19 12:51:25 +02:00
|
|
|
|
// Subscribe to monster events
|
|
|
|
|
|
monster.OnPictureTaken += OnMonsterPictureTaken;
|
|
|
|
|
|
monster.OnMonsterDespawned += OnMonsterDespawned;
|
|
|
|
|
|
|
|
|
|
|
|
// Add to active monsters list
|
|
|
|
|
|
activeMonsters.Add(monster);
|
|
|
|
|
|
|
|
|
|
|
|
// Fire event
|
|
|
|
|
|
OnMonsterSpawned?.Invoke(monster);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Monster prefab {prefab.name} does not have a Monster component!");
|
|
|
|
|
|
Destroy(monsterObj);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnMonsterPictureTaken(Monster monster)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Calculate points based on depth
|
|
|
|
|
|
int depthBonus = Mathf.FloorToInt(Mathf.Abs(monster.transform.position.y) * depthMultiplier);
|
|
|
|
|
|
int pointsAwarded = basePoints + depthBonus;
|
|
|
|
|
|
|
|
|
|
|
|
// Add score
|
|
|
|
|
|
playerScore += pointsAwarded;
|
|
|
|
|
|
|
|
|
|
|
|
// Fire events
|
|
|
|
|
|
OnScoreChanged?.Invoke(playerScore);
|
|
|
|
|
|
OnPictureTaken?.Invoke(monster, pointsAwarded);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnMonsterDespawned(Monster monster)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Remove from active list
|
|
|
|
|
|
activeMonsters.Remove(monster);
|
|
|
|
|
|
|
|
|
|
|
|
// Unsubscribe from events
|
|
|
|
|
|
monster.OnPictureTaken -= OnMonsterPictureTaken;
|
|
|
|
|
|
monster.OnMonsterDespawned -= OnMonsterDespawned;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Call this when the game ends
|
|
|
|
|
|
public void EndGame()
|
|
|
|
|
|
{
|
|
|
|
|
|
// Clean up active monsters
|
|
|
|
|
|
foreach (var monster in activeMonsters.ToArray())
|
|
|
|
|
|
{
|
|
|
|
|
|
if (monster != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
monster.DespawnMonster();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
activeMonsters.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
// Final score could be saved to player prefs or other persistence
|
|
|
|
|
|
Debug.Log($"Final Score: {playerScore}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|