From 0e01ff0fb9a19e0120c7b269d4787895bdd381bd Mon Sep 17 00:00:00 2001 From: Vinzenz Schroeter Date: Mon, 29 Apr 2024 21:52:50 +0200 Subject: [PATCH] implement smart bullet --- .../TanksServer/GameLogic/CollectPowerUp.cs | 2 +- .../TanksServer/GameLogic/GameRules.cs | 2 ++ .../TanksServer/GameLogic/MapEntityManager.cs | 3 +- .../TanksServer/GameLogic/MoveBullets.cs | 36 +++++++++++++++++-- .../TanksServer/GameLogic/SpawnPowerUp.cs | 5 ++- tanks-backend/TanksServer/Models/Bullet.cs | 4 ++- tanks-backend/TanksServer/Models/Magazine.cs | 11 +++--- .../TanksServer/Models/PositionHelpers.cs | 1 - tanks-backend/TanksServer/appsettings.json | 11 +++--- 9 files changed, 54 insertions(+), 21 deletions(-) diff --git a/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs b/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs index fe0ea1a..e3a68a5 100644 --- a/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs +++ b/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs @@ -47,7 +47,7 @@ internal sealed class CollectPowerUp( }; break; default: - throw new NotImplementedException(); + throw new UnreachableException(); } tank.Owner.Scores.PowerUpsCollected++; diff --git a/tanks-backend/TanksServer/GameLogic/GameRules.cs b/tanks-backend/TanksServer/GameLogic/GameRules.cs index 5c4a98f..2284464 100644 --- a/tanks-backend/TanksServer/GameLogic/GameRules.cs +++ b/tanks-backend/TanksServer/GameLogic/GameRules.cs @@ -25,4 +25,6 @@ internal sealed class GameRules public byte MagazineSize { get; set; } = 5; public int ReloadDelayMs { get; set; } = 3000; + + public double SmartBulletInertia { get; set; } = 1; } diff --git a/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs b/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs index 77e6f29..42d30ff 100644 --- a/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs +++ b/tanks-backend/TanksServer/GameLogic/MapEntityManager.cs @@ -27,7 +27,8 @@ internal sealed class MapEntityManager( IsExplosive = type.HasFlag(MagazineType.Explosive), Timeout = DateTime.Now + _bulletTimeout, OwnerCollisionAfter = DateTime.Now + TimeSpan.FromSeconds(1), - Speed = speed + Speed = speed, + IsSmart = type.HasFlag(MagazineType.Smart) }); } diff --git a/tanks-backend/TanksServer/GameLogic/MoveBullets.cs b/tanks-backend/TanksServer/GameLogic/MoveBullets.cs index 755e454..96bde60 100644 --- a/tanks-backend/TanksServer/GameLogic/MoveBullets.cs +++ b/tanks-backend/TanksServer/GameLogic/MoveBullets.cs @@ -1,7 +1,12 @@ namespace TanksServer.GameLogic; -internal sealed class MoveBullets(MapEntityManager entityManager) : ITickStep +internal sealed class MoveBullets( + MapEntityManager entityManager, + IOptions options +) : ITickStep { + private readonly double _smartBulletInertia = options.Value.SmartBulletInertia; + public ValueTask TickAsync(TimeSpan delta) { foreach (var bullet in entityManager.Bullets) @@ -10,8 +15,15 @@ internal sealed class MoveBullets(MapEntityManager entityManager) : ITickStep return ValueTask.CompletedTask; } - private static void MoveBullet(Bullet bullet, TimeSpan delta) + private void MoveBullet(Bullet bullet, TimeSpan delta) { + if (bullet.IsSmart && TryGetSmartRotation(bullet.Position, bullet.Owner, out var wantedRotation)) + { + var inertiaFactor = _smartBulletInertia * delta.TotalSeconds; + var difference = wantedRotation - bullet.Rotation; + bullet.Rotation += difference * inertiaFactor; + } + var speed = bullet.Speed * delta.TotalSeconds; var angle = bullet.Rotation * 2 * Math.PI; bullet.Position = new FloatPosition( @@ -19,4 +31,24 @@ internal sealed class MoveBullets(MapEntityManager entityManager) : ITickStep bullet.Position.Y - Math.Cos(angle) * speed ); } + + private bool TryGetSmartRotation(FloatPosition position, Player bulletOwner, out double rotation) + { + var nearestEnemy = entityManager.Tanks + .Where(t => t.Owner != bulletOwner) + .MinBy(t => position.Distance(t.Position)); + + if (nearestEnemy == null) + { + rotation = double.NaN; + return false; + } + + var rotationRadians = Math.Atan2( + y: nearestEnemy.Position.Y - position.Y, + x: nearestEnemy.Position.X - position.X + ) + (Math.PI / 2); + rotation = rotationRadians / (2 * Math.PI); + return true; + } } diff --git a/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs b/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs index 41907a1..56c7ae7 100644 --- a/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs +++ b/tanks-backend/TanksServer/GameLogic/SpawnPowerUp.cs @@ -18,18 +18,17 @@ internal sealed class SpawnPowerUp( return ValueTask.CompletedTask; - var type = Random.Shared.Next(10) < 3 + var type = Random.Shared.Next(4) == 0 ? PowerUpType.MagazineSizeUpgrade : PowerUpType.MagazineTypeUpgrade; MagazineType? magazineType = type switch { - PowerUpType.MagazineTypeUpgrade => Random.Shared.Next(0, 4) switch + PowerUpType.MagazineTypeUpgrade => Random.Shared.Next(0, 3) switch { 0 => MagazineType.Fast, 1 => MagazineType.Explosive, 2 => MagazineType.Smart, - 3 => MagazineType.Mine, _ => throw new UnreachableException() }, _ => null diff --git a/tanks-backend/TanksServer/Models/Bullet.cs b/tanks-backend/TanksServer/Models/Bullet.cs index f685741..9e17066 100644 --- a/tanks-backend/TanksServer/Models/Bullet.cs +++ b/tanks-backend/TanksServer/Models/Bullet.cs @@ -4,7 +4,7 @@ internal sealed class Bullet : IMapEntity { public required Player Owner { get; init; } - public required double Rotation { get; init; } + public required double Rotation { get; set; } public required FloatPosition Position { get; set; } @@ -17,4 +17,6 @@ internal sealed class Bullet : IMapEntity internal required DateTime OwnerCollisionAfter { get; init; } public required double Speed { get; init; } + + public required bool IsSmart { get; init; } } diff --git a/tanks-backend/TanksServer/Models/Magazine.cs b/tanks-backend/TanksServer/Models/Magazine.cs index 8994e61..b3143b5 100644 --- a/tanks-backend/TanksServer/Models/Magazine.cs +++ b/tanks-backend/TanksServer/Models/Magazine.cs @@ -21,16 +21,13 @@ internal readonly record struct Magazine(MagazineType Type, byte UsedBullets, by var sb = new StringBuilder(); if (Type.HasFlag(MagazineType.Fast)) - sb.Append('»'); + sb.Append("» "); if (Type.HasFlag(MagazineType.Explosive)) - sb.Append('*'); + sb.Append("* "); if (Type.HasFlag(MagazineType.Smart)) - sb.Append('@'); + sb.Append("@ "); if (Type.HasFlag(MagazineType.Mine)) - sb.Append('\u263c'); - - if (sb.Length > 0) - sb.Append(' '); + sb.Append("\u263c "); sb.Append("[ "); for (var i = 0; i < UsedBullets; i++) diff --git a/tanks-backend/TanksServer/Models/PositionHelpers.cs b/tanks-backend/TanksServer/Models/PositionHelpers.cs index 5af5073..223a472 100644 --- a/tanks-backend/TanksServer/Models/PositionHelpers.cs +++ b/tanks-backend/TanksServer/Models/PositionHelpers.cs @@ -22,7 +22,6 @@ internal static class PositionHelpers public static FloatPosition ToFloatPosition(this PixelPosition position) => new(position.X, position.Y); - public static double Distance(this FloatPosition p1, FloatPosition p2) => Math.Sqrt( Math.Pow(p1.X - p2.X, 2) + diff --git a/tanks-backend/TanksServer/appsettings.json b/tanks-backend/TanksServer/appsettings.json index 045fd04..dd8aa6d 100644 --- a/tanks-backend/TanksServer/appsettings.json +++ b/tanks-backend/TanksServer/appsettings.json @@ -21,15 +21,16 @@ }, "GameRules": { "DestructibleWalls": true, - "PowerUpSpawnChance": 0.1, - "MaxPowerUpCount": 15, - "BulletTimeoutMs": 30000, + "PowerUpSpawnChance": 0.2, + "MaxPowerUpCount": 5, + "BulletTimeoutMs": 20000, "SpawnDelayMs": 3000, "IdleTimeoutMs": 30000, - "MoveSpeed": 37.5, + "MoveSpeed": 40, "TurnSpeed": 0.5, "ShootDelayMs": 450, - "BulletSpeed": 75 + "BulletSpeed": 75, + "SmartBulletHomingSpeed": 1.5 }, "Host": { "EnableServicePointDisplay": true,