namespace TanksServer.GameLogic; internal sealed class MapEntityManager( ILogger logger, IOptions options ) { private readonly GameRules _rules = options.Value; private readonly HashSet _bullets = []; private readonly HashSet _powerUps = []; private readonly Dictionary _playerTanks = []; private readonly TimeSpan _bulletTimeout = TimeSpan.FromMilliseconds(options.Value.BulletTimeoutMs); public IEnumerable Bullets => _bullets; public IEnumerable Tanks => _playerTanks.Values; public IEnumerable PowerUps => _powerUps; public void SpawnBullet(Player tankOwner, FloatPosition position, double rotation, MagazineType type) { var speed = _rules.BulletSpeed * (type.HasFlag(MagazineType.Fast) ? 2 : 1); _bullets.Add(new Bullet { Owner = tankOwner, Position = position, Rotation = rotation, IsExplosive = type.HasFlag(MagazineType.Explosive), Timeout = DateTime.Now + _bulletTimeout, OwnerCollisionAfter = DateTime.Now + TimeSpan.FromSeconds(1), Speed = speed, IsSmart = type.HasFlag(MagazineType.Smart) }); } public void RemoveWhere(Predicate predicate) => _bullets.RemoveWhere(predicate); public void SpawnTank(Player player, FloatPosition position) { var tank = new Tank { Owner = player, Position = position, Rotation = Random.Shared.NextDouble(), Magazine = new Magazine(MagazineType.Basic, 0, _rules.MagazineSize) }; _playerTanks[player] = tank; logger.LogInformation("Tank added for player {}", player.Name); } public void SpawnPowerUp(FloatPosition position, PowerUpType type, MagazineType? magazineType) { var powerUp = new PowerUp { Position = position, Type = type, MagazineType = magazineType }; _powerUps.Add(powerUp); } public void RemoveWhere(Predicate predicate) => _powerUps.RemoveWhere(predicate); public void Remove(Tank tank) { logger.LogInformation("Tank removed for player {}", tank.Owner.Name); _playerTanks.Remove(tank.Owner); } public Tank? GetCurrentTankOfPlayer(Player player) => _playerTanks.GetValueOrDefault(player); public IEnumerable AllEntities => Bullets .Cast() .Concat(Tanks) .Concat(PowerUps); }