improve spawn position checks

This commit is contained in:
Vinzenz Schroeter 2024-04-11 20:48:21 +02:00
parent 91ab911f9c
commit ad211433fb
16 changed files with 147 additions and 80 deletions

View file

@ -1,7 +1,7 @@
namespace TanksServer.GameLogic;
internal sealed class CollideBulletsWithTanks(
BulletManager bullets, TankManager tanks, SpawnQueueProvider spawnQueueProvider
BulletManager bullets, TankManager tanks, SpawnQueue spawnQueue
) : ITickStep
{
public Task TickAsync()
@ -24,7 +24,7 @@ internal sealed class CollideBulletsWithTanks(
tank.Owner.Deaths++;
tanks.Remove(tank);
spawnQueueProvider.Queue.Enqueue(tank.Owner);
spawnQueue.EnqueueForDelayedSpawn(tank.Owner);
return true;
}

View file

@ -0,0 +1,6 @@
namespace TanksServer.GameLogic;
public class PlayersConfiguration
{
public int SpawnDelayMs { get; set; }
}

View file

@ -1,25 +1,29 @@
namespace TanksServer.GameLogic;
internal sealed class SpawnNewTanks(TankManager tanks, MapService map, SpawnQueueProvider queueProvider) : ITickStep
internal sealed class SpawnNewTanks(
TankManager tanks,
MapService map,
SpawnQueue queue,
BulletManager bullets
) : ITickStep
{
public Task TickAsync()
{
while (queueProvider.Queue.TryDequeue(out var player))
if (!queue.TryDequeueNext(out var player))
return Task.CompletedTask;
tanks.Add(new Tank(player, ChooseSpawnPosition())
{
var tank = new Tank(player, ChooseSpawnPosition())
{
Rotation = Random.Shared.Next(0, 16)
};
tanks.Add(tank);
}
Rotation = Random.Shared.Next(0, 16)
});
return Task.CompletedTask;
}
private FloatPosition ChooseSpawnPosition()
{
List<TilePosition> candidates = new();
Dictionary<TilePosition, double> candidates = [];
for (var x = 0; x < MapService.TilesPerRow; x++)
for (var y = 0; y < MapService.TilesPerColumn; y++)
{
@ -27,15 +31,24 @@ internal sealed class SpawnNewTanks(TankManager tanks, MapService map, SpawnQueu
if (map.IsCurrentlyWall(tile))
continue;
var tilePixelCenter = tile.GetPixelRelative(4, 4);
var minDistance = bullets.GetAll()
.Cast<IMapEntity>()
.Concat(tanks)
.Select(entity => Math.Sqrt(
Math.Pow(entity.Position.X - tilePixelCenter.X, 2) +
Math.Pow(entity.Position.Y - tilePixelCenter.Y, 2)))
.Aggregate(double.MaxValue, Math.Min);
// TODO: check tanks and bullets
candidates.Add(tile);
candidates.Add(tile, minDistance);
}
var chosenTile = candidates[Random.Shared.Next(candidates.Count)];
var min = candidates.MaxBy(kvp => kvp.Value).Key;
return new FloatPosition(
chosenTile.X * MapService.TileSize,
chosenTile.Y * MapService.TileSize
min.X * MapService.TileSize,
min.Y * MapService.TileSize
);
}
}
}

View file

@ -0,0 +1,36 @@
using System.Diagnostics.CodeAnalysis;
namespace TanksServer.GameLogic;
internal sealed class SpawnQueue(
IOptions<PlayersConfiguration> options
)
{
private ConcurrentQueue<Player> _queue = new();
private ConcurrentDictionary<Player, DateTime> _spawnTimes = new();
private readonly TimeSpan _spawnDelay = TimeSpan.FromMilliseconds(options.Value.SpawnDelayMs);
public void EnqueueForImmediateSpawn(Player player)
{
_queue.Enqueue(player);
}
public void EnqueueForDelayedSpawn(Player player)
{
_queue.Enqueue(player);
_spawnTimes.AddOrUpdate(player, DateTime.MinValue, (_, _) => DateTime.Now + _spawnDelay);
}
public bool TryDequeueNext([MaybeNullWhen(false)] out Player player)
{
if (!_queue.TryDequeue(out player))
return false;
var now = DateTime.Now;
if (_spawnTimes.GetOrAdd(player, DateTime.MinValue) <= now)
return true;
_queue.Enqueue(player);
return false;
}
}

View file

@ -1,6 +0,0 @@
namespace TanksServer.GameLogic;
internal sealed class SpawnQueueProvider
{
public ConcurrentQueue<Player> Queue { get; } = new();
}

View file

@ -0,0 +1,9 @@
namespace TanksServer.GameLogic;
public class TanksConfiguration
{
public double MoveSpeed { get; set; }
public double TurnSpeed { get; set; }
public double ShootDelayMs { get; set; }
public double BulletSpeed { get; set; }
}