2024-04-28 18:44:03 +02:00
|
|
|
using TanksServer.Graphics;
|
|
|
|
|
2024-04-17 19:34:19 +02:00
|
|
|
namespace TanksServer.GameLogic;
|
|
|
|
|
2024-05-05 13:01:37 +02:00
|
|
|
internal sealed class CollideBullets : ITickStep
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
2024-04-28 18:44:03 +02:00
|
|
|
private readonly Sprite _explosiveSprite = Sprite.FromImageFile("assets/explosion.png");
|
2024-05-05 13:01:37 +02:00
|
|
|
private readonly Predicate<Bullet> _removeBulletsPredicate;
|
|
|
|
private readonly MapEntityManager _entityManager;
|
|
|
|
private readonly MapService _map;
|
|
|
|
private readonly bool _destructibleWalls;
|
|
|
|
private readonly TankSpawnQueue _tankSpawnQueue;
|
|
|
|
|
|
|
|
public CollideBullets(MapEntityManager entityManager,
|
|
|
|
MapService map,
|
|
|
|
IOptions<GameRules> options,
|
|
|
|
TankSpawnQueue tankSpawnQueue)
|
|
|
|
{
|
|
|
|
_entityManager = entityManager;
|
|
|
|
_map = map;
|
|
|
|
_tankSpawnQueue = tankSpawnQueue;
|
|
|
|
|
|
|
|
_destructibleWalls = options.Value.DestructibleWalls;
|
|
|
|
_removeBulletsPredicate = b => BulletHitsTank(b) || BulletHitsWall(b) || BulletTimesOut(b);
|
|
|
|
}
|
2024-04-17 19:34:19 +02:00
|
|
|
|
2024-04-28 15:34:32 +02:00
|
|
|
public ValueTask TickAsync(TimeSpan _)
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
2024-05-05 13:01:37 +02:00
|
|
|
_entityManager.RemoveWhere(_removeBulletsPredicate);
|
2024-04-28 15:34:32 +02:00
|
|
|
return ValueTask.CompletedTask;
|
2024-04-17 19:34:19 +02:00
|
|
|
}
|
|
|
|
|
2024-04-22 19:44:28 +02:00
|
|
|
private bool BulletTimesOut(Bullet bullet)
|
2024-04-19 13:32:41 +02:00
|
|
|
{
|
|
|
|
if (bullet.Timeout > DateTime.Now)
|
|
|
|
return false;
|
|
|
|
|
2024-05-08 00:29:33 +02:00
|
|
|
ExplodeAt(bullet.Position.ToPixelPosition(), bullet.Stats.Explosive, bullet.Owner);
|
2024-04-19 13:32:41 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-22 19:44:28 +02:00
|
|
|
private bool BulletHitsWall(Bullet bullet)
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
|
|
|
var pixel = bullet.Position.ToPixelPosition();
|
2024-05-05 13:01:37 +02:00
|
|
|
if (!_map.Current.IsWall(pixel))
|
2024-04-17 19:34:19 +02:00
|
|
|
return false;
|
|
|
|
|
2024-05-08 00:29:33 +02:00
|
|
|
ExplodeAt(pixel, bullet.Stats.Explosive, bullet.Owner);
|
2024-04-17 19:34:19 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool BulletHitsTank(Bullet bullet)
|
|
|
|
{
|
2024-04-28 18:44:03 +02:00
|
|
|
var hitTank = GetTankAt(bullet.Position, bullet.Owner, DateTime.Now > bullet.OwnerCollisionAfter);
|
|
|
|
if (hitTank == null)
|
2024-04-17 19:34:19 +02:00
|
|
|
return false;
|
|
|
|
|
2024-05-08 00:29:33 +02:00
|
|
|
ExplodeAt(bullet.Position.ToPixelPosition(), bullet.Stats.Explosive, bullet.Owner);
|
2024-04-17 19:34:19 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-04-28 18:44:03 +02:00
|
|
|
private Tank? GetTankAt(FloatPosition position, Player owner, bool canHitOwnTank)
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
2024-05-05 13:01:37 +02:00
|
|
|
foreach (var tank in _entityManager.Tanks)
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
2024-04-21 20:20:30 +02:00
|
|
|
var hitsOwnTank = owner == tank.Owner;
|
|
|
|
if (hitsOwnTank && !canHitOwnTank)
|
|
|
|
continue;
|
|
|
|
|
2024-04-17 19:34:19 +02:00
|
|
|
var (topLeft, bottomRight) = tank.Bounds;
|
|
|
|
if (position.X < topLeft.X || position.X > bottomRight.X ||
|
|
|
|
position.Y < topLeft.Y || position.Y > bottomRight.Y)
|
|
|
|
continue;
|
|
|
|
|
2024-04-28 18:44:03 +02:00
|
|
|
return tank;
|
2024-04-17 19:34:19 +02:00
|
|
|
}
|
|
|
|
|
2024-04-28 18:44:03 +02:00
|
|
|
return null;
|
2024-04-17 19:34:19 +02:00
|
|
|
}
|
|
|
|
|
2024-04-28 18:44:03 +02:00
|
|
|
private void ExplodeAt(PixelPosition pixel, bool isExplosive, Player owner)
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
2024-04-28 18:44:03 +02:00
|
|
|
if (!isExplosive)
|
|
|
|
{
|
|
|
|
Core(pixel);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pixel = pixel.GetPixelRelative(-4, -4);
|
|
|
|
for (short dx = 0; dx < _explosiveSprite.Width; dx++)
|
2024-11-12 18:27:04 +01:00
|
|
|
for (short dy = 0; dy < _explosiveSprite.Height; dy++)
|
|
|
|
{
|
|
|
|
if (!_explosiveSprite[dx, dy].HasValue)
|
|
|
|
continue;
|
2024-04-28 18:44:03 +02:00
|
|
|
|
2024-11-12 18:27:04 +01:00
|
|
|
Core(pixel.GetPixelRelative(dx, dy));
|
|
|
|
}
|
2024-04-28 18:44:03 +02:00
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
void Core(PixelPosition position)
|
2024-04-17 19:34:19 +02:00
|
|
|
{
|
2024-05-05 13:01:37 +02:00
|
|
|
if (_destructibleWalls && _map.Current.TryDestroyWallAt(position))
|
2024-04-19 13:34:56 +02:00
|
|
|
owner.Scores.WallsDestroyed++;
|
|
|
|
|
2024-04-28 18:44:03 +02:00
|
|
|
var tank = GetTankAt(position.ToFloatPosition(), owner, true);
|
|
|
|
if (tank == null)
|
|
|
|
return;
|
|
|
|
|
2024-05-03 14:51:32 +02:00
|
|
|
if (tank.Owner != owner)
|
2024-04-28 18:44:03 +02:00
|
|
|
owner.Scores.Kills++;
|
|
|
|
tank.Owner.Scores.Deaths++;
|
|
|
|
|
2024-05-05 13:01:37 +02:00
|
|
|
_entityManager.Remove(tank);
|
|
|
|
_tankSpawnQueue.EnqueueForDelayedSpawn(tank.Owner);
|
2024-04-17 19:34:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|