diff --git a/README.md b/README.md index 20b4f72..53a7b30 100644 --- a/README.md +++ b/README.md @@ -108,4 +108,3 @@ There are other commands implemented as well, e.g. for changing the brightness. ## Backlog: Bugs, Wishes, Ideas - Generalize drawing of entities as there are multiple classes with pretty much the same code - Generalize hit box collision -- BUG: when standing next to a wall, the bullet sometimes misses the first pixel diff --git a/tanks-backend/TanksServer.sln b/tanks-backend/TanksServer.sln index 8f288c4..fc3a074 100644 --- a/tanks-backend/TanksServer.sln +++ b/tanks-backend/TanksServer.sln @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "shared", "shared", "{12DB7D ProjectSection(SolutionItems) = preProject global.json = global.json shared.props = shared.props + Dockerfile = Dockerfile EndProjectSection EndProject Global diff --git a/tanks-backend/TanksServer/GameLogic/CollideBullets.cs b/tanks-backend/TanksServer/GameLogic/CollideBullets.cs index 1ec17cf..5368ed6 100644 --- a/tanks-backend/TanksServer/GameLogic/CollideBullets.cs +++ b/tanks-backend/TanksServer/GameLogic/CollideBullets.cs @@ -41,7 +41,7 @@ internal sealed class CollideBullets( private bool BulletHitsTank(Bullet bullet) { - if (!TryHitTankAt(bullet.Position, bullet.Owner)) + if (!TryHitTankAt(bullet.Position, bullet.Owner, DateTime.Now > bullet.OwnerCollisionAfter)) return false; if (bullet.IsExplosive) @@ -49,16 +49,20 @@ internal sealed class CollideBullets( return true; } - private bool TryHitTankAt(FloatPosition position, Player owner) + private bool TryHitTankAt(FloatPosition position, Player owner, bool canHitOwnTank) { foreach (var tank in entityManager.Tanks) { + var hitsOwnTank = owner == tank.Owner; + if (hitsOwnTank && !canHitOwnTank) + continue; + var (topLeft, bottomRight) = tank.Bounds; if (position.X < topLeft.X || position.X > bottomRight.X || position.Y < topLeft.Y || position.Y > bottomRight.Y) continue; - if (owner != tank.Owner) + if (!hitsOwnTank) owner.Scores.Kills++; tank.Owner.Scores.Deaths++; @@ -79,7 +83,7 @@ internal sealed class CollideBullets( if (options.Value.DestructibleWalls && map.Current.TryDestroyWallAt(offsetPixel)) owner.Scores.WallsDestroyed++; - TryHitTankAt(offsetPixel.ToFloatPosition(), owner); + TryHitTankAt(offsetPixel.ToFloatPosition(), owner, true); } } } diff --git a/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs b/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs index 5c2cbfc..46d6fc4 100644 --- a/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs +++ b/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs @@ -21,7 +21,15 @@ internal sealed class MapEntityManager( .Concat(PowerUps); public void SpawnBullet(Player tankOwner, FloatPosition position, double rotation, bool isExplosive) - => _bullets.Add(new Bullet(tankOwner, position, rotation, isExplosive, DateTime.Now + _bulletTimeout)); + => _bullets.Add(new Bullet + { + Owner = tankOwner, + Position = position, + Rotation = rotation, + IsExplosive = isExplosive, + Timeout = DateTime.Now + _bulletTimeout, + OwnerCollisionAfter = DateTime.Now + TimeSpan.FromSeconds(1), + }); public void RemoveBulletsWhere(Predicate predicate) => _bullets.RemoveWhere(predicate); diff --git a/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs b/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs index ec93503..9920c72 100644 --- a/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs +++ b/tanks-backend/TanksServer/GameLogic/ShootFromTanks.cs @@ -26,33 +26,10 @@ internal sealed class ShootFromTanks( tank.NextShotAfter = DateTime.Now.AddMilliseconds(_config.ShootDelayMs); - var rotation = tank.Orientation / 16d; - var angle = rotation * 2d * Math.PI; - - /* When standing next to a wall, the bullet sometimes misses the first pixel. - Spawning the bullet to close to the tank instead means the tank instantly hits itself. - Because the tank has a float position, but hit boxes are based on pixels, this problem has been deemed complex - enough to do later. These values mostly work. */ - var distance = (tank.Orientation % 4) switch - { - 0 => 4.4d, - 1 or 3 => 5.4d, - 2 => 6d, - _ => throw new UnreachableException("this should not be possible") - }; - - var position = new FloatPosition( - tank.Position.X + Math.Sin(angle) * distance, - tank.Position.Y - Math.Cos(angle) * distance - ); - - var explosive = false; - if (tank.ExplosiveBullets > 0) - { + var explosive = tank.ExplosiveBullets > 0; + if (explosive) tank.ExplosiveBullets--; - explosive = true; - } - entityManager.SpawnBullet(tank.Owner, position, rotation, explosive); + entityManager.SpawnBullet(tank.Owner, tank.Position, tank.Orientation / 16d, explosive); } } diff --git a/tanks-backend/TanksServer/Models/Bullet.cs b/tanks-backend/TanksServer/Models/Bullet.cs index 2558c7b..c6e87d9 100644 --- a/tanks-backend/TanksServer/Models/Bullet.cs +++ b/tanks-backend/TanksServer/Models/Bullet.cs @@ -1,16 +1,18 @@ namespace TanksServer.Models; -internal sealed class Bullet(Player tankOwner, FloatPosition position, double rotation, bool isExplosive, DateTime timeout) : IMapEntity +internal sealed class Bullet : IMapEntity { - public Player Owner { get; } = tankOwner; + public required Player Owner { get; init; } - public double Rotation { get; } = rotation; + public required double Rotation { get; init; } - public FloatPosition Position { get; set; } = position; + public required FloatPosition Position { get; set; } - public bool IsExplosive { get; } = isExplosive; + public required bool IsExplosive { get; init; } - public DateTime Timeout { get; } = timeout; + public required DateTime Timeout { get; init; } - public PixelBounds Bounds => new (Position.ToPixelPosition(), Position.ToPixelPosition()); + public PixelBounds Bounds => new(Position.ToPixelPosition(), Position.ToPixelPosition()); + + internal required DateTime OwnerCollisionAfter { get; init; } }