Add Hunter NPC and Teleporter features with associated prefabs and effects

This commit is contained in:
2025-12-13 00:01:29 +01:00
parent c0fb207768
commit d8b0583fac
32 changed files with 823 additions and 217 deletions

View File

@@ -9,11 +9,13 @@ namespace Core.Domain
private const string HighScoreKey = "HighScore";
private const float NpcSpawnTime = 4f;
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 event Action<int> OnScoreChanged;
public event Action<string> OnOrbSpawned;
public event Action OnOrbReset;
@@ -29,14 +31,21 @@ namespace Core.Domain
private bool _npcSpawned;
private float _powerUpTimer;
private float _timeSlowTimer;
// Combo System
private float _lastOrbTime;
public int ComboMultiplier { get; private set; } = 1;
public event Action<int> OnComboUpdated;
public GameSession(List<Tile> tiles, IPersistenceService persistenceService)
{
_tiles = tiles;
_persistenceService = persistenceService;
Score = 0;
IsGameOver = false;
HighScore = _persistenceService.Load(HighScoreKey, 0);
}
@@ -45,56 +54,88 @@ namespace Core.Domain
_timeSinceStart = 0f;
_powerUpTimer = 0f;
_npcSpawned = false;
TimeDilation = 1.0f;
ComboMultiplier = 1;
_lastOrbTime = -999f;
SpawnNextOrb();
}
public void Tick(float deltaTime)
{
if (IsGameOver) return;
_timeSinceStart += deltaTime;
if (!_npcSpawned && _timeSinceStart >= NpcSpawnTime)
{
_npcSpawned = true;
OnSpawnNpc?.Invoke();
}
_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;
Score += 10;
// 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 &&
{
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);
}
@@ -102,39 +143,56 @@ namespace Core.Domain
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);
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 type = _rng.Next(0, 2) == 0 ? PowerUpType.LightFooted : PowerUpType.SpeedBoost;
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 2: type = PowerUpType.Hover; break;
case 3: type = PowerUpType.TimeSlow; break;
}
OnSpawnPowerUp?.Invoke(type, tile.Id);
}
}