2024-04-17 19:34:19 +02:00
|
|
|
namespace TanksServer.GameLogic;
|
|
|
|
|
|
|
|
internal sealed class MapEntityManager(
|
|
|
|
ILogger<MapEntityManager> logger,
|
2024-04-19 13:32:41 +02:00
|
|
|
MapService map,
|
|
|
|
IOptions<GameRules> options
|
2024-04-17 19:34:19 +02:00
|
|
|
)
|
|
|
|
{
|
|
|
|
private readonly HashSet<Bullet> _bullets = [];
|
|
|
|
private readonly HashSet<PowerUp> _powerUps = [];
|
2024-04-22 19:44:28 +02:00
|
|
|
private readonly Dictionary<Player, Tank> _playerTanks = [];
|
2024-04-19 13:32:41 +02:00
|
|
|
private readonly TimeSpan _bulletTimeout = TimeSpan.FromMilliseconds(options.Value.BulletTimeoutMs);
|
2024-04-17 19:34:19 +02:00
|
|
|
|
|
|
|
public IEnumerable<Bullet> Bullets => _bullets;
|
2024-04-22 19:59:41 +02:00
|
|
|
public IEnumerable<Tank> Tanks => _playerTanks.Values;
|
2024-04-17 19:34:19 +02:00
|
|
|
public IEnumerable<PowerUp> PowerUps => _powerUps;
|
|
|
|
|
|
|
|
public void SpawnBullet(Player tankOwner, FloatPosition position, double rotation, bool isExplosive)
|
2024-04-21 20:20:30 +02:00
|
|
|
=> _bullets.Add(new Bullet
|
|
|
|
{
|
|
|
|
Owner = tankOwner,
|
|
|
|
Position = position,
|
|
|
|
Rotation = rotation,
|
|
|
|
IsExplosive = isExplosive,
|
|
|
|
Timeout = DateTime.Now + _bulletTimeout,
|
|
|
|
OwnerCollisionAfter = DateTime.Now + TimeSpan.FromSeconds(1),
|
|
|
|
});
|
2024-04-17 19:34:19 +02:00
|
|
|
|
2024-04-22 19:44:28 +02:00
|
|
|
public void RemoveWhere(Predicate<Bullet> predicate) => _bullets.RemoveWhere(predicate);
|
2024-04-17 19:34:19 +02:00
|
|
|
|
|
|
|
public void SpawnTank(Player player)
|
|
|
|
{
|
2024-04-22 19:44:28 +02:00
|
|
|
var tank = new Tank(player, ChooseSpawnPosition())
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
|
|
|
Rotation = Random.Shared.NextDouble()
|
2024-04-22 19:44:28 +02:00
|
|
|
};
|
|
|
|
_playerTanks[player] = tank;
|
2024-04-17 19:34:19 +02:00
|
|
|
logger.LogInformation("Tank added for player {}", player.Id);
|
|
|
|
}
|
|
|
|
|
|
|
|
public void SpawnPowerUp() => _powerUps.Add(new PowerUp(ChooseSpawnPosition()));
|
|
|
|
|
|
|
|
public void RemoveWhere(Predicate<PowerUp> predicate) => _powerUps.RemoveWhere(predicate);
|
|
|
|
|
|
|
|
public void Remove(Tank tank)
|
|
|
|
{
|
|
|
|
logger.LogInformation("Tank removed for player {}", tank.Owner.Id);
|
2024-04-22 19:44:28 +02:00
|
|
|
_playerTanks.Remove(tank.Owner);
|
2024-04-17 19:34:19 +02:00
|
|
|
}
|
|
|
|
|
2024-04-22 19:59:41 +02:00
|
|
|
public Tank? GetCurrentTankOfPlayer(Player player) => _playerTanks.GetValueOrDefault(player);
|
2024-04-17 19:34:19 +02:00
|
|
|
|
2024-04-22 19:59:41 +02:00
|
|
|
private IEnumerable<IMapEntity> AllEntities => Bullets
|
|
|
|
.Cast<IMapEntity>()
|
|
|
|
.Concat(Tanks)
|
|
|
|
.Concat(PowerUps);
|
|
|
|
|
|
|
|
private FloatPosition ChooseSpawnPosition()
|
|
|
|
{
|
|
|
|
var maxMinDistance = 0d;
|
|
|
|
TilePosition spawnTile = default;
|
2024-04-17 19:34:19 +02:00
|
|
|
for (ushort x = 1; x < MapService.TilesPerRow - 1; x++)
|
|
|
|
for (ushort y = 1; y < MapService.TilesPerColumn - 1; y++)
|
|
|
|
{
|
|
|
|
var tile = new TilePosition(x, y);
|
|
|
|
if (map.Current.IsWall(tile))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
var tilePixelCenter = tile.ToPixelPosition().GetPixelRelative(4, 4).ToFloatPosition();
|
2024-04-17 19:46:14 +02:00
|
|
|
var minDistance = AllEntities
|
2024-04-17 19:34:19 +02:00
|
|
|
.Select(entity => entity.Position.Distance(tilePixelCenter))
|
|
|
|
.Aggregate(double.MaxValue, Math.Min);
|
2024-04-22 19:59:41 +02:00
|
|
|
if (minDistance <= maxMinDistance)
|
|
|
|
continue;
|
2024-04-17 19:34:19 +02:00
|
|
|
|
2024-04-22 19:59:41 +02:00
|
|
|
maxMinDistance = minDistance;
|
|
|
|
spawnTile = tile;
|
2024-04-17 19:34:19 +02:00
|
|
|
}
|
|
|
|
|
2024-04-22 19:59:41 +02:00
|
|
|
return spawnTile.ToPixelPosition().GetPixelRelative(4, 4).ToFloatPosition();
|
2024-04-17 19:34:19 +02:00
|
|
|
}
|
|
|
|
}
|