fix bullet cannot hit close to tank

This commit is contained in:
Vinzenz Schroeter 2024-04-21 20:20:30 +02:00
parent 6045de0c7d
commit 19e1792307
6 changed files with 30 additions and 39 deletions

View file

@ -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

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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<Bullet> predicate) => _bullets.RemoveWhere(predicate);

View file

@ -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);
}
}

View file

@ -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; }
}