using System; using System.Collections.Generic; using Core.Ports; using UnityEngine; using Random = System.Random; namespace Core.Domain { public class GameSession { private const string HighScoreKey = "HighScore"; private const float PowerUpSpawnInterval = 25f; public int Score { get; private set; } public int HighScore { get; private set; } public bool IsGameOver { get; private set; } public float TimeDilation { get; private set; } = 1.0f; public float NpcSpawnTime { get; private set; } = 30f; public event Action OnScoreChanged; public event Action OnOrbSpawned; public event Action OnOrbReset; public event Action OnGameOver; public event Action OnSpawnNpc; public event Action OnSpawnPowerUp; private readonly List _tiles; private readonly IPersistenceService _persistenceService; private readonly Random _rng = new(); private int _playerFloorIndex = 0; private float _timeSinceStart; private float _npcTimer; private float _powerUpTimer; private float _timeSlowTimer; // Combo System private float _lastOrbTime; public int ComboMultiplier { get; private set; } = 1; public event Action OnComboUpdated; public GameSession(List tiles, IPersistenceService persistenceService) { _tiles = tiles; _persistenceService = persistenceService; Score = 0; IsGameOver = false; HighScore = _persistenceService.Load(HighScoreKey, 0); } public void StartGame() { _timeSinceStart = 0f; _powerUpTimer = 0f; TimeDilation = 1.0f; ComboMultiplier = 1; _lastOrbTime = -999f; SpawnNextOrb(); } public void Tick(float deltaTime) { if (IsGameOver) return; _timeSinceStart += deltaTime; _npcTimer += deltaTime; if (_npcTimer >= NpcSpawnTime) { _npcTimer = 0f; OnSpawnNpc?.Invoke(); NpcSpawnTime = Mathf.Max(5f, NpcSpawnTime * 0.95f); } _powerUpTimer += deltaTime; if (_powerUpTimer >= PowerUpSpawnInterval) { _powerUpTimer = 0f; SpawnRandomPowerUp(); } if (_timeSlowTimer > 0) { _timeSlowTimer -= deltaTime; if (_timeSlowTimer <= 0) { TimeDilation = 1.0f; } } } public void ActivateTimeSlow(float duration) { _timeSlowTimer = duration; TimeDilation = 0.3f; // Slow down to 30% } public void OrbCollected() { if (IsGameOver) return; // Combo Check (2 second window) if (_timeSinceStart - _lastOrbTime < 2.0f) { ComboMultiplier++; } else { ComboMultiplier = 1; } _lastOrbTime = _timeSinceStart; OnComboUpdated?.Invoke(ComboMultiplier); Score += 10 * ComboMultiplier; OnScoreChanged?.Invoke(Score); SpawnNextOrb(); } private void SpawnNextOrb() { var validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable && t.Floor == _playerFloorIndex ); if (validTiles.Count == 0) { validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable); } if (validTiles.Count == 0) { EndGame(); return; } var pick = validTiles[_rng.Next(validTiles.Count)]; OnOrbSpawned?.Invoke(pick.Id); } public void EndGame() { if (IsGameOver) return; IsGameOver = true; if (Score > HighScore) { HighScore = Score; _persistenceService.Save(HighScoreKey, HighScore); } OnGameOver?.Invoke(); } public void SetPlayerFloor(int floorIndex) { if (_playerFloorIndex == floorIndex) return; _playerFloorIndex = floorIndex; if (IsGameOver) return; OnOrbReset?.Invoke(); SpawnNextOrb(); } private void SpawnRandomPowerUp() { var validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable && t.Floor == _playerFloorIndex ); if (validTiles.Count == 0) { validTiles = _tiles.FindAll(t => t.CurrentState == TileState.Stable); } if (validTiles.Count == 0) return; var tile = validTiles[_rng.Next(validTiles.Count)]; var rand = _rng.Next(0, 4); var type = PowerUpType.LightFooted; switch (rand) { case 0: type = PowerUpType.LightFooted; break; case 1: type = PowerUpType.SpeedBoost; break; case 3: type = PowerUpType.TimeSlow; break; } OnSpawnPowerUp?.Invoke(type, tile.Id); } } }