move upgrades to tank, serialize objects directly
This commit is contained in:
		
							parent
							
								
									b1df817ece
								
							
						
					
					
						commit
						827b3a9330
					
				
					 16 changed files with 135 additions and 180 deletions
				
			
		|  | @ -46,29 +46,35 @@ export default function PlayerInfo({player}: { player: string }) { | |||
|     if (!lastJsonMessage || readyState !== ReadyState.OPEN) | ||||
|         return <></>; | ||||
| 
 | ||||
|     let position = ''; | ||||
|     if (lastJsonMessage.tank) | ||||
|         position = `(${Math.round(lastJsonMessage.tank.position.x)}|${Math.round(lastJsonMessage.tank.position.y)})`; | ||||
| 
 | ||||
|     return <Column className="PlayerInfo"> | ||||
|         <h3> | ||||
|             Playing as {lastJsonMessage.name} | ||||
|             Playing as {lastJsonMessage.player.name} | ||||
|         </h3> | ||||
|         <table> | ||||
|             <tbody> | ||||
|             <ScoreRow name="magazine" value={lastJsonMessage.tank?.magazine}/> | ||||
|             <ScoreRow name="controls" value={lastJsonMessage.controls}/> | ||||
|             <ScoreRow name="position" value={lastJsonMessage.tank?.position}/> | ||||
|             <ScoreRow name="position" value={position}/> | ||||
|             <ScoreRow name="orientation" value={lastJsonMessage.tank?.orientation}/> | ||||
|             <ScoreRow name="bullet speed" value={lastJsonMessage.tank?.bulletStats.speed}/> | ||||
|             <ScoreRow name="bullet acceleration" value={lastJsonMessage.tank?.bulletStats.acceleration}/> | ||||
|             <ScoreRow name="smart bullets" value={lastJsonMessage.tank?.bulletStats.smart}/> | ||||
|             <ScoreRow name="explosive bullets" value={lastJsonMessage.tank?.bulletStats.explosive}/> | ||||
| 
 | ||||
|             <ScoreRow name="kills" value={lastJsonMessage.player.scores.kills}/> | ||||
|             <ScoreRow name="deaths" value={lastJsonMessage.player.scores.deaths}/> | ||||
|             <ScoreRow name="walls destroyed" value={lastJsonMessage.player.scores.wallsDestroyed}/> | ||||
|             <ScoreRow name="bullets fired" value={lastJsonMessage.player.scores.shotsFired}/> | ||||
|             <ScoreRow name="power ups collected" value={lastJsonMessage.player.scores.powerUpsCollected}/> | ||||
|             <ScoreRow name="pixels moved" value={lastJsonMessage.player.scores.pixelsMoved}/> | ||||
|             <ScoreRow name="score" value={lastJsonMessage.player.scores.overallScore}/> | ||||
| 
 | ||||
|             <ScoreRow name="moving" value={lastJsonMessage.tank?.moving}/> | ||||
| 
 | ||||
|             <ScoreRow name="kills" value={lastJsonMessage.scores.kills}/> | ||||
|             <ScoreRow name="deaths" value={lastJsonMessage.scores.deaths}/> | ||||
| 
 | ||||
|             <ScoreRow name="walls destroyed" value={lastJsonMessage.scores.wallsDestroyed}/> | ||||
|             <ScoreRow name="bullets fired" value={lastJsonMessage.scores.shotsFired}/> | ||||
|             <ScoreRow name="power ups collected" value={lastJsonMessage.scores.powerUpsCollected}/> | ||||
|             <ScoreRow name="pixels moved" value={lastJsonMessage.scores.pixelsMoved}/> | ||||
| 
 | ||||
|             <ScoreRow name="score" value={lastJsonMessage.scores.overallScore}/> | ||||
| 
 | ||||
|             <ScoreRow name="connections" value={lastJsonMessage.openConnections}/> | ||||
|             <ScoreRow name="connections" value={lastJsonMessage.player.openConnections}/> | ||||
|             </tbody> | ||||
|         </table> | ||||
|     </Column>; | ||||
|  |  | |||
|  | @ -14,24 +14,32 @@ export type Scores = { | |||
|     readonly pixelsMoved: number; | ||||
| }; | ||||
| 
 | ||||
| export type Player = { | ||||
|     readonly name: string; | ||||
|     readonly scores: Scores; | ||||
| }; | ||||
| 
 | ||||
| type TankInfo = { | ||||
|     readonly magazine: string; | ||||
| type Tank = { | ||||
|     readonly position: { x: number; y: number }; | ||||
|     readonly orientation: number; | ||||
|     readonly moving: boolean; | ||||
|     readonly bulletStats: BulletStats; | ||||
|     readonly reloadingUntil: string; | ||||
|     readonly nextShotAfter: string; | ||||
|     readonly magazine: { | ||||
|         readonly empty: boolean; | ||||
|         readonly  usedBullets: number; | ||||
|         readonly maxBullets: number; | ||||
|         readonly displayString: string; | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| export type Player = { | ||||
|     readonly name: string; | ||||
|     readonly scores: Scores; | ||||
|     readonly openConnections: number; | ||||
|     readonly lastInput: string; | ||||
| } | ||||
| 
 | ||||
| export type PlayerInfoMessage = { | ||||
|     readonly name: string; | ||||
|     readonly scores: Scores; | ||||
|     readonly player: Player; | ||||
|     readonly controls: string; | ||||
|     readonly tank?: TankInfo; | ||||
|     readonly openConnections: number; | ||||
|     readonly tank?: Tank; | ||||
| } | ||||
| 
 | ||||
| export type MapInfo = { | ||||
|  | @ -40,6 +48,13 @@ export type MapInfo = { | |||
|     readonly preview: string; | ||||
| } | ||||
| 
 | ||||
| export type BulletStats = { | ||||
|     speed: number; | ||||
|     acceleration: number, | ||||
|     explosive: boolean, | ||||
|     smart: boolean | ||||
| }; | ||||
| 
 | ||||
| export function useMyWebSocket<T = unknown>(url: string, options: Options = {}) { | ||||
|     return useWebSocket<T>(url, { | ||||
|         shouldReconnect: () => true, | ||||
|  |  | |||
|  | @ -2,19 +2,27 @@ using System.Diagnostics; | |||
| 
 | ||||
| namespace TanksServer.GameLogic; | ||||
| 
 | ||||
| internal sealed class CollectPowerUp( | ||||
|     MapEntityManager entityManager | ||||
| ) : ITickStep | ||||
| internal sealed class CollectPowerUp : ITickStep | ||||
| { | ||||
|     private readonly Predicate<PowerUp> _collectPredicate = b => TryCollect(b, entityManager.Tanks); | ||||
|     private readonly Predicate<PowerUp> _collectPredicate; | ||||
|     private readonly GameRules _rules; | ||||
|     private readonly MapEntityManager _entityManager; | ||||
| 
 | ||||
|     public CollectPowerUp(MapEntityManager entityManager, | ||||
|         IOptions<GameRules> options) | ||||
|     { | ||||
|         _entityManager = entityManager; | ||||
|         _rules = options.Value; | ||||
|         _collectPredicate = b => TryCollect(b, entityManager.Tanks); | ||||
|     } | ||||
| 
 | ||||
|     public ValueTask TickAsync(TimeSpan delta) | ||||
|     { | ||||
|         entityManager.RemoveWhere(_collectPredicate); | ||||
|         _entityManager.RemoveWhere(_collectPredicate); | ||||
|         return ValueTask.CompletedTask; | ||||
|     } | ||||
| 
 | ||||
|     private static bool TryCollect(PowerUp powerUp, IEnumerable<Tank> tanks) | ||||
|     private bool TryCollect(PowerUp powerUp, IEnumerable<Tank> tanks) | ||||
|     { | ||||
|         var position = powerUp.Position; | ||||
|         foreach (var tank in tanks) | ||||
|  | @ -34,32 +42,38 @@ internal sealed class CollectPowerUp( | |||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     private static void ApplyPowerUpEffect(PowerUp powerUp, Tank tank) | ||||
|     private void ApplyPowerUpEffect(PowerUp powerUp, Tank tank) | ||||
|     { | ||||
|         switch (powerUp.Type) | ||||
|         { | ||||
|             case PowerUpType.MagazineType: | ||||
|                 if (powerUp.MagazineType == null) | ||||
|                     throw new UnreachableException(); | ||||
| 
 | ||||
|                 tank.Magazine = tank.Magazine with | ||||
|                 { | ||||
|                     Type = tank.Magazine.Type | powerUp.MagazineType.Value, | ||||
|                     UsedBullets = 0 | ||||
|                 }; | ||||
| 
 | ||||
|                 if (tank.ReloadingUntil >= DateTime.Now) | ||||
|                     tank.ReloadingUntil = DateTime.Now; | ||||
| 
 | ||||
|                 break; | ||||
|             case PowerUpType.MagazineSize: | ||||
|                 tank.Magazine = tank.Magazine with | ||||
|                 tank.MaxBullets = (byte)int.Clamp(tank.MaxBullets + 1, 1, 32); | ||||
|                 break; | ||||
| 
 | ||||
|             case PowerUpType.BulletAcceleration: | ||||
|                 tank.BulletStats = tank.BulletStats with | ||||
|                 { | ||||
|                     MaxBullets = (byte)int.Clamp(tank.Magazine.MaxBullets + 1, 1, 32) | ||||
|                     Acceleration = tank.BulletStats.Acceleration * _rules.BulletAccelerationUpgradeStrength | ||||
|                 }; | ||||
|                 break; | ||||
| 
 | ||||
|             case PowerUpType.ExplosiveBullets: | ||||
|                 tank.BulletStats = tank.BulletStats with { Explosive = true }; | ||||
|                 break; | ||||
| 
 | ||||
|             case PowerUpType.SmartBullets: | ||||
|                 tank.BulletStats = tank.BulletStats with { Smart = true }; | ||||
|                 break; | ||||
| 
 | ||||
|             case PowerUpType.BulletSpeed: | ||||
|                 tank.BulletStats = tank.BulletStats with | ||||
|                 { | ||||
|                     Speed = tank.BulletStats.Speed * _rules.BulletSpeedUpgradeStrength | ||||
|                 }; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 throw new UnreachableException(); | ||||
|                 throw new NotImplementedException($"unknown type {powerUp.Type}"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ internal sealed class CollideBullets : ITickStep | |||
|         if (bullet.Timeout > DateTime.Now) | ||||
|             return false; | ||||
| 
 | ||||
|         ExplodeAt(bullet.Position.ToPixelPosition(), bullet.IsExplosive, bullet.Owner); | ||||
|         ExplodeAt(bullet.Position.ToPixelPosition(), bullet.Stats.Explosive, bullet.Owner); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -45,7 +45,7 @@ internal sealed class CollideBullets : ITickStep | |||
|         if (!_map.Current.IsWall(pixel)) | ||||
|             return false; | ||||
| 
 | ||||
|         ExplodeAt(pixel, bullet.IsExplosive, bullet.Owner); | ||||
|         ExplodeAt(pixel, bullet.Stats.Explosive, bullet.Owner); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -55,7 +55,7 @@ internal sealed class CollideBullets : ITickStep | |||
|         if (hitTank == null) | ||||
|             return false; | ||||
| 
 | ||||
|         ExplodeAt(bullet.Position.ToPixelPosition(), bullet.IsExplosive, bullet.Owner); | ||||
|         ExplodeAt(bullet.Position.ToPixelPosition(), bullet.Stats.Explosive, bullet.Owner); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -28,5 +28,7 @@ internal sealed class GameRules | |||
| 
 | ||||
|     public double SmartBulletInertia { get; set; } = 1; | ||||
| 
 | ||||
|     public double FastBulletAcceleration { get; set; } = 0.25; | ||||
|     public double BulletAccelerationUpgradeStrength { get; set; } = 0.1; | ||||
| 
 | ||||
|     public double BulletSpeedUpgradeStrength { get; set; } = 0.1; | ||||
| } | ||||
|  |  | |||
|  | @ -15,19 +15,17 @@ internal sealed class MapEntityManager( | |||
|     public IEnumerable<Tank> Tanks => _playerTanks.Values; | ||||
|     public IEnumerable<PowerUp> PowerUps => _powerUps; | ||||
| 
 | ||||
|     public void SpawnBullet(Player tankOwner, FloatPosition position, double rotation, MagazineType type) | ||||
|     public void SpawnBullet(Player tankOwner, FloatPosition position, double rotation, BulletStats stats) | ||||
|     { | ||||
|         _bullets.Add(new Bullet | ||||
|         { | ||||
|             Owner = tankOwner, | ||||
|             Position = position, | ||||
|             Rotation = rotation, | ||||
|             IsExplosive = type.HasFlag(MagazineType.Explosive), | ||||
|             Timeout = DateTime.Now + _bulletTimeout, | ||||
|             OwnerCollisionAfter = DateTime.Now + TimeSpan.FromSeconds(1), | ||||
|             Speed = _rules.BulletSpeed, | ||||
|             IsSmart = type.HasFlag(MagazineType.Smart), | ||||
|             Acceleration = type.HasFlag(MagazineType.Fast) ? _rules.FastBulletAcceleration : 0d | ||||
|             Stats = stats | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|  | @ -35,24 +33,23 @@ internal sealed class MapEntityManager( | |||
| 
 | ||||
|     public void SpawnTank(Player player, FloatPosition position) | ||||
|     { | ||||
|         var tank = new Tank | ||||
|         var tank = new Tank(player) | ||||
|         { | ||||
|             Owner = player, | ||||
|             Position = position, | ||||
|             Rotation = Random.Shared.NextDouble(), | ||||
|             Magazine = new Magazine(MagazineType.Basic, 0, _rules.MagazineSize) | ||||
|             MaxBullets = _rules.MagazineSize, | ||||
|             BulletStats =new BulletStats(_rules.BulletSpeed, 0, false, false) | ||||
|         }; | ||||
|         _playerTanks[player] = tank; | ||||
|         logger.LogInformation("Tank added for player {}", player.Name); | ||||
|     } | ||||
| 
 | ||||
|     public void SpawnPowerUp(FloatPosition position, PowerUpType type, MagazineType? magazineType) | ||||
|     public void SpawnPowerUp(FloatPosition position, PowerUpType type) | ||||
|     { | ||||
|         var powerUp = new PowerUp | ||||
|         { | ||||
|             Position = position, | ||||
|             Type = type, | ||||
|             MagazineType = magazineType | ||||
|             Type = type | ||||
|         }; | ||||
|         _powerUps.Add(powerUp); | ||||
|     } | ||||
|  |  | |||
|  | @ -17,14 +17,15 @@ internal sealed class MoveBullets( | |||
| 
 | ||||
|     private void MoveBullet(Bullet bullet, TimeSpan delta) | ||||
|     { | ||||
|         if (bullet.IsSmart && TryGetSmartRotation(bullet.Position, bullet.Owner, out var wantedRotation)) | ||||
|         if (bullet.Stats.Smart && TryGetSmartRotation(bullet.Position, bullet.Owner, out var wantedRotation)) | ||||
|         { | ||||
|             var inertiaFactor = _smartBulletInertia * delta.TotalSeconds; | ||||
|             var difference = wantedRotation - bullet.Rotation; | ||||
|             bullet.Rotation += difference * inertiaFactor; | ||||
|         } | ||||
| 
 | ||||
|         bullet.Speed *= 1 + (bullet.Acceleration * delta.TotalSeconds); | ||||
|         bullet.Speed = double.Clamp(bullet.Speed * (1 + (bullet.Stats.Acceleration * delta.TotalSeconds)), 0d, | ||||
|             MapService.TileSize * 10); | ||||
| 
 | ||||
|         var speed = bullet.Speed * delta.TotalSeconds; | ||||
|         var angle = bullet.Rotation * 2 * Math.PI; | ||||
|  |  | |||
|  | @ -26,24 +26,17 @@ internal sealed class ShootFromTanks( | |||
|         if (tank.ReloadingUntil >= now) | ||||
|             return; | ||||
| 
 | ||||
|         if (tank.Magazine.Empty) | ||||
|         if (tank.UsedBullets >= tank.MaxBullets) | ||||
|         { | ||||
|             tank.ReloadingUntil = now.AddMilliseconds(_config.ReloadDelayMs); | ||||
|             tank.Magazine = tank.Magazine with | ||||
|             { | ||||
|                 UsedBullets = 0, | ||||
|                 Type = MagazineType.Basic | ||||
|             }; | ||||
|             tank.UsedBullets = 0; | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         tank.NextShotAfter = now.AddMilliseconds(_config.ShootDelayMs); | ||||
|         tank.Magazine = tank.Magazine with | ||||
|         { | ||||
|             UsedBullets = (byte)(tank.Magazine.UsedBullets + 1) | ||||
|         }; | ||||
|         tank.UsedBullets++; | ||||
| 
 | ||||
|         tank.Owner.Scores.ShotsFired++; | ||||
|         entityManager.SpawnBullet(tank.Owner, tank.Position, tank.Orientation / 16d, tank.Magazine.Type); | ||||
|         entityManager.SpawnBullet(tank.Owner, tank.Position, tank.Orientation / 16d, tank.BulletStats); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -18,25 +18,9 @@ internal sealed class SpawnPowerUp( | |||
|         if (Random.Shared.NextDouble() > _spawnChance * delta.TotalSeconds) | ||||
|             return ValueTask.CompletedTask; | ||||
| 
 | ||||
| 
 | ||||
|         var type = Random.Shared.Next(4) == 0 | ||||
|             ? PowerUpType.MagazineSize | ||||
|             : PowerUpType.MagazineType; | ||||
| 
 | ||||
|         MagazineType? magazineType = type switch | ||||
|         { | ||||
|             PowerUpType.MagazineType => Random.Shared.Next(0, 3) switch | ||||
|             { | ||||
|                 0 => MagazineType.Fast, | ||||
|                 1 => MagazineType.Explosive, | ||||
|                 2 => MagazineType.Smart, | ||||
|                 _ => throw new UnreachableException() | ||||
|             }, | ||||
|             _ => null | ||||
|         }; | ||||
| 
 | ||||
|         var type = (PowerUpType)Random.Shared.Next((int)Enum.GetValues<PowerUpType>().Max()); | ||||
|         var position = emptyTileFinder.ChooseEmptyTile().GetCenter().ToFloatPosition(); | ||||
|         entityManager.SpawnPowerUp(position, type, magazineType); | ||||
|         entityManager.SpawnPowerUp(position, type); | ||||
|         return ValueTask.CompletedTask; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -14,12 +14,12 @@ internal sealed class DrawPowerUpsStep(MapEntityManager entityManager) : IDrawSt | |||
|     { | ||||
|         foreach (var powerUp in entityManager.PowerUps) | ||||
|         { | ||||
|             var sprite = powerUp switch | ||||
|             var sprite = powerUp.Type switch | ||||
|             { | ||||
|                 { Type: PowerUpType.MagazineSize } => _magazineSprite, | ||||
|                 { Type: PowerUpType.MagazineType, MagazineType: MagazineType.Smart } => _smartSprite, | ||||
|                 { Type: PowerUpType.MagazineType, MagazineType: MagazineType.Explosive } => _explosiveSprite, | ||||
|                 { Type: PowerUpType.MagazineType, MagazineType: MagazineType.Fast } => _fastSprite, | ||||
|                 PowerUpType.MagazineSize => _magazineSprite, | ||||
|                 PowerUpType.BulletAcceleration or PowerUpType.BulletSpeed => _fastSprite, | ||||
|                 PowerUpType.SmartBullets => _smartSprite, | ||||
|                 PowerUpType.ExplosiveBullets => _explosiveSprite, | ||||
|                 _ => _genericSprite | ||||
|             }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -47,20 +47,7 @@ internal sealed class PlayerInfoConnection | |||
|     private async ValueTask<IMemoryOwner<byte>?> GenerateMessageAsync() | ||||
|     { | ||||
|         var tank = _entityManager.GetCurrentTankOfPlayer(_player); | ||||
| 
 | ||||
|         TankInfo? tankInfo = null; | ||||
|         if (tank != null) | ||||
|         { | ||||
|             var magazine = tank.ReloadingUntil > DateTime.Now ? "[ RELOADING ]" : tank.Magazine.ToDisplayString(); | ||||
|             tankInfo = new TankInfo(tank.Orientation, magazine, tank.Position.ToPixelPosition(), tank.Moving); | ||||
|         } | ||||
| 
 | ||||
|         var info = new PlayerInfo( | ||||
|             _player.Name, | ||||
|             _player.Scores, | ||||
|             _player.Controls.ToDisplayString(), | ||||
|             tankInfo, | ||||
|             _player.OpenConnections); | ||||
|         var info = new PlayerInfo(_player, _player.Controls.ToDisplayString(), tank); | ||||
| 
 | ||||
|         _tempStream.Position = 0; | ||||
|         await JsonSerializer.SerializeAsync(_tempStream, info, AppSerializerContext.Default.PlayerInfo); | ||||
|  | @ -85,3 +72,9 @@ internal sealed class PlayerInfoConnection | |||
|         Interlocked.Exchange(ref _lastMessage, data)?.Dispose(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| internal record struct PlayerInfo( | ||||
|     Player Player, | ||||
|     string Controls, | ||||
|     Tank? Tank | ||||
| ); | ||||
|  |  | |||
|  | @ -8,8 +8,6 @@ internal sealed class Bullet : IMapEntity | |||
| 
 | ||||
|     public required FloatPosition Position { get; set; } | ||||
| 
 | ||||
|     public required bool IsExplosive { get; init; } | ||||
| 
 | ||||
|     public required DateTime Timeout { get; init; } | ||||
| 
 | ||||
|     public PixelBounds Bounds => new(Position.ToPixelPosition(), Position.ToPixelPosition()); | ||||
|  | @ -18,7 +16,5 @@ internal sealed class Bullet : IMapEntity | |||
| 
 | ||||
|     public required double Speed { get; set; } | ||||
| 
 | ||||
|     public required double Acceleration { get; init; } | ||||
| 
 | ||||
|     public required bool IsSmart { get; init; } | ||||
|     public required BulletStats Stats { get; init; } | ||||
| } | ||||
|  |  | |||
|  | @ -1,38 +0,0 @@ | |||
| using System.Text; | ||||
| 
 | ||||
| namespace TanksServer.Models; | ||||
| 
 | ||||
| [Flags] | ||||
| internal enum MagazineType | ||||
| { | ||||
|     Basic = 0, | ||||
|     Fast = 1 << 0, | ||||
|     Explosive = 1 << 1, | ||||
|     Smart = 1 << 2, | ||||
| } | ||||
| 
 | ||||
| internal readonly record struct Magazine(MagazineType Type, byte UsedBullets, byte MaxBullets) | ||||
| { | ||||
|     public bool Empty => UsedBullets >= MaxBullets; | ||||
| 
 | ||||
|     public string ToDisplayString() | ||||
|     { | ||||
|         var sb = new StringBuilder(); | ||||
| 
 | ||||
|         if (Type.HasFlag(MagazineType.Fast)) | ||||
|             sb.Append("» "); | ||||
|         if (Type.HasFlag(MagazineType.Explosive)) | ||||
|             sb.Append("* "); | ||||
|         if (Type.HasFlag(MagazineType.Smart)) | ||||
|             sb.Append("@ "); | ||||
| 
 | ||||
|         sb.Append("[ "); | ||||
|         for (var i = 0; i < UsedBullets; i++) | ||||
|             sb.Append("\u25cb "); | ||||
|         for (var i = UsedBullets; i < MaxBullets; i++) | ||||
|             sb.Append("• "); | ||||
|         sb.Append(']'); | ||||
| 
 | ||||
|         return sb.ToString(); | ||||
|     } | ||||
| } | ||||
|  | @ -1,16 +0,0 @@ | |||
| namespace TanksServer.Models; | ||||
| 
 | ||||
| internal record struct TankInfo( | ||||
|     int Orientation, | ||||
|     string Magazine, | ||||
|     PixelPosition Position, | ||||
|     bool Moving | ||||
| ); | ||||
| 
 | ||||
| internal record struct PlayerInfo( | ||||
|     string Name, | ||||
|     Scores Scores, | ||||
|     string Controls, | ||||
|     TankInfo? Tank, | ||||
|     int OpenConnections | ||||
| ); | ||||
|  | @ -4,8 +4,11 @@ namespace TanksServer.Models; | |||
| 
 | ||||
| internal enum PowerUpType | ||||
| { | ||||
|     MagazineType, | ||||
|     MagazineSize | ||||
|     MagazineSize, | ||||
|     BulletSpeed, | ||||
|     BulletAcceleration, | ||||
|     ExplosiveBullets, | ||||
|     SmartBullets, | ||||
| } | ||||
| 
 | ||||
| internal sealed class PowerUp: IMapEntity | ||||
|  | @ -15,6 +18,4 @@ internal sealed class PowerUp: IMapEntity | |||
|     public PixelBounds Bounds => Position.GetBoundsForCenter(MapService.TileSize); | ||||
| 
 | ||||
|     public required PowerUpType Type { get; init; } | ||||
| 
 | ||||
|     public MagazineType? MagazineType { get; init; } | ||||
| } | ||||
|  |  | |||
|  | @ -1,13 +1,14 @@ | |||
| using System.Diagnostics; | ||||
| using System.Text.Json.Serialization; | ||||
| using TanksServer.GameLogic; | ||||
| 
 | ||||
| namespace TanksServer.Models; | ||||
| 
 | ||||
| internal sealed class Tank : IMapEntity | ||||
| internal sealed class Tank(Player owner) : IMapEntity | ||||
| { | ||||
|     private double _rotation; | ||||
| 
 | ||||
|     public required Player Owner { get; init; } | ||||
|     [JsonIgnore] public Player Owner { get; } = owner; | ||||
| 
 | ||||
|     public double Rotation | ||||
|     { | ||||
|  | @ -26,11 +27,17 @@ internal sealed class Tank : IMapEntity | |||
| 
 | ||||
|     public required FloatPosition Position { get; set; } | ||||
| 
 | ||||
|     public PixelBounds Bounds => Position.GetBoundsForCenter(MapService.TileSize); | ||||
|     [JsonIgnore] public PixelBounds Bounds => Position.GetBoundsForCenter(MapService.TileSize); | ||||
| 
 | ||||
|     public int Orientation => (int)Math.Round(Rotation * 16) % 16; | ||||
| 
 | ||||
|     public required Magazine Magazine { get; set; } | ||||
|     public int UsedBullets { get; set; } | ||||
| 
 | ||||
|     public int MaxBullets { get; set; } | ||||
| 
 | ||||
|     public DateTime ReloadingUntil { get; set; } | ||||
| 
 | ||||
|     public required BulletStats BulletStats { get; set; } | ||||
| } | ||||
| 
 | ||||
| internal sealed record class BulletStats(double Speed, double Acceleration, bool Explosive, bool Smart); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter