diff --git a/tank-frontend/src/PlayerInfo.tsx b/tank-frontend/src/PlayerInfo.tsx index 30d9ea0..e6f9b16 100644 --- a/tank-frontend/src/PlayerInfo.tsx +++ b/tank-frontend/src/PlayerInfo.tsx @@ -74,6 +74,8 @@ export default function PlayerInfo({player}: { player: string }) { + + diff --git a/tank-frontend/src/Scoreboard.tsx b/tank-frontend/src/Scoreboard.tsx index 93b8667..821f099 100644 --- a/tank-frontend/src/Scoreboard.tsx +++ b/tank-frontend/src/Scoreboard.tsx @@ -9,7 +9,7 @@ function numberSorter(a: number, b: number) { export default function Scoreboard({}: {}) { const query = useQuery({ queryKey: ['scores'], - refetchInterval: 1000, + refetchInterval: 5000, queryFn: async () => { const url = makeApiUrl('/scores'); const response = await fetch(url, {method: 'GET'}); @@ -53,6 +53,16 @@ export default function Scoreboard({}: {}) { visualize: p => p.scores.shotsFired.toString(), sorter: (a, b) => numberSorter(a.scores.shotsFired, b.scores.shotsFired) }, + { + field: 'powerUps', + visualize: p => p.scores.powerUpsCollected.toString(), + sorter: (a, b) => numberSorter(a.scores.powerUpsCollected, b.scores.powerUpsCollected) + }, + { + field: 'distance', + visualize: p => p.scores.pixelsMoved.toString(), + sorter: (a, b) => numberSorter(a.scores.pixelsMoved, b.scores.pixelsMoved) + }, { field: 'score', visualize: p => p.scores.overallScore.toString(), diff --git a/tank-frontend/src/serverCalls.tsx b/tank-frontend/src/serverCalls.tsx index 5071c46..d50fe87 100644 --- a/tank-frontend/src/serverCalls.tsx +++ b/tank-frontend/src/serverCalls.tsx @@ -8,6 +8,8 @@ export type Scores = { readonly wallsDestroyed: number; readonly shotsFired: number; readonly overallScore: number; + readonly powerUpsCollected: number; + readonly pixelsMoved: number; }; export type Player = { diff --git a/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs b/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs index c5af079..cc74e23 100644 --- a/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs +++ b/tanks-backend/TanksServer/GameLogic/CollectPowerUp.cs @@ -20,8 +20,9 @@ internal sealed class CollectPowerUp( position.Y < topLeft.Y || position.Y > bottomRight.Y) continue; - // this works because now the tank overlaps the power up + // now the tank overlaps the power up by at least 0.5 tiles tank.ExplosiveBullets += 10; + tank.Owner.Scores.PowerUpsCollected++; return true; } diff --git a/tanks-backend/TanksServer/GameLogic/MoveTanks.cs b/tanks-backend/TanksServer/GameLogic/MoveTanks.cs index a007b03..0b36bfd 100644 --- a/tanks-backend/TanksServer/GameLogic/MoveTanks.cs +++ b/tanks-backend/TanksServer/GameLogic/MoveTanks.cs @@ -54,6 +54,7 @@ internal sealed class MoveTanks( if (HitsTank(tank, newPosition)) return false; + tank.Owner.Scores.DistanceMoved += newPosition.Distance(tank.Position); tank.Position = newPosition; return true; } diff --git a/tanks-backend/TanksServer/GameLogic/TankSpawnQueue.cs b/tanks-backend/TanksServer/GameLogic/TankSpawnQueue.cs index 4abb0ae..ef146d2 100644 --- a/tanks-backend/TanksServer/GameLogic/TankSpawnQueue.cs +++ b/tanks-backend/TanksServer/GameLogic/TankSpawnQueue.cs @@ -5,7 +5,7 @@ namespace TanksServer.GameLogic; internal sealed class TankSpawnQueue( IOptions options, MapEntityManager entityManager -): ITickStep +) : ITickStep { private readonly ConcurrentQueue _queue = new(); private readonly ConcurrentDictionary _spawnTimes = new(); @@ -16,31 +16,8 @@ internal sealed class TankSpawnQueue( public void EnqueueForDelayedSpawn(Player player) { - _queue.Enqueue(player); _spawnTimes.AddOrUpdate(player, DateTime.MinValue, (_, _) => DateTime.Now + _spawnDelay); - } - - private bool TryDequeueNext([MaybeNullWhen(false)] out Player player) - { - if (!_queue.TryDequeue(out player)) - return false; // no one on queue - - if (player.LastInput + _idleTimeout < DateTime.Now) - { - // player idle - _queue.Enqueue(player); - return false; - } - - var now = DateTime.Now; - if (_spawnTimes.GetOrAdd(player, DateTime.MinValue) > now) - { - // spawn delay - _queue.Enqueue(player); - return false; - } - - return true; + _queue.Enqueue(player); } public ValueTask TickAsync(TimeSpan _) @@ -51,4 +28,29 @@ internal sealed class TankSpawnQueue( entityManager.SpawnTank(player); return ValueTask.CompletedTask; } + + private bool TryDequeueNext([MaybeNullWhen(false)] out Player player) + { + if (!_queue.TryDequeue(out player)) + return false; // no one on queue + + var now = DateTime.Now; + if (player.LastInput + _idleTimeout < now) + { + // player idle + _queue.Enqueue(player); + player = null; + return false; + } + + if (_spawnTimes.GetOrAdd(player, DateTime.MinValue) > now) + { + // spawn delay + _queue.Enqueue(player); + player = null; + return false; + } + + return true; + } } diff --git a/tanks-backend/TanksServer/Interactivity/AppSerializerContext.cs b/tanks-backend/TanksServer/Interactivity/AppSerializerContext.cs index e5acef1..46d6ba2 100644 --- a/tanks-backend/TanksServer/Interactivity/AppSerializerContext.cs +++ b/tanks-backend/TanksServer/Interactivity/AppSerializerContext.cs @@ -4,8 +4,6 @@ namespace TanksServer.Interactivity; [JsonSerializable(typeof(Player))] [JsonSerializable(typeof(IEnumerable))] -[JsonSerializable(typeof(Guid))] -[JsonSerializable(typeof(NameId))] [JsonSerializable(typeof(IEnumerable))] [JsonSerializable(typeof(PlayerInfo))] internal sealed partial class AppSerializerContext : JsonSerializerContext; diff --git a/tanks-backend/TanksServer/Models/Scores.cs b/tanks-backend/TanksServer/Models/Scores.cs index 79063b6..8eda772 100644 --- a/tanks-backend/TanksServer/Models/Scores.cs +++ b/tanks-backend/TanksServer/Models/Scores.cs @@ -1,3 +1,5 @@ +using System.Text.Json.Serialization; + namespace TanksServer.Models; internal sealed record class Scores @@ -22,5 +24,17 @@ internal sealed record class Scores public int ShotsFired { get; set; } - public int OverallScore => Math.Max(0, 10000 * Kills - 1000 * Deaths + 10 * ShotsFired + 10 * WallsDestroyed); + public int PowerUpsCollected { get; set; } + + [JsonIgnore] public double DistanceMoved { get; set; } + + public int PixelsMoved => (int)DistanceMoved; + + public int OverallScore => Math.Max(0, + 10000 * Kills + - 1000 * Deaths + + 100 * PowerUpsCollected + + 10 * (ShotsFired + WallsDestroyed) + + PixelsMoved + ); } diff --git a/tanks-backend/TanksServer/Program.cs b/tanks-backend/TanksServer/Program.cs index 620222e..edff439 100644 --- a/tanks-backend/TanksServer/Program.cs +++ b/tanks-backend/TanksServer/Program.cs @@ -10,8 +10,6 @@ using TanksServer.Interactivity; namespace TanksServer; -internal sealed record class NameId(string Name, Guid Id); - public static class Program { public static async Task Main(string[] args)