Merge pull request #4 from kaesaecracker/more-stats

more scores
This commit is contained in:
RobbersDaughter 2024-04-29 16:41:45 +02:00 committed by GitHub
commit 88f2708d94
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 60 additions and 32 deletions

View file

@ -74,6 +74,8 @@ export default function PlayerInfo({player}: { player: string }) {
<ScoreRow name="walls destroyed" value={lastJsonMessage.scores.wallsDestroyed}/> <ScoreRow name="walls destroyed" value={lastJsonMessage.scores.wallsDestroyed}/>
<ScoreRow name="bullets fired" value={lastJsonMessage.scores.shotsFired}/> <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="score" value={lastJsonMessage.scores.overallScore}/>
</tbody> </tbody>

View file

@ -9,7 +9,7 @@ function numberSorter(a: number, b: number) {
export default function Scoreboard({}: {}) { export default function Scoreboard({}: {}) {
const query = useQuery({ const query = useQuery({
queryKey: ['scores'], queryKey: ['scores'],
refetchInterval: 1000, refetchInterval: 5000,
queryFn: async () => { queryFn: async () => {
const url = makeApiUrl('/scores'); const url = makeApiUrl('/scores');
const response = await fetch(url, {method: 'GET'}); const response = await fetch(url, {method: 'GET'});
@ -53,6 +53,16 @@ export default function Scoreboard({}: {}) {
visualize: p => p.scores.shotsFired.toString(), visualize: p => p.scores.shotsFired.toString(),
sorter: (a, b) => numberSorter(a.scores.shotsFired, b.scores.shotsFired) 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', field: 'score',
visualize: p => p.scores.overallScore.toString(), visualize: p => p.scores.overallScore.toString(),

View file

@ -8,6 +8,8 @@ export type Scores = {
readonly wallsDestroyed: number; readonly wallsDestroyed: number;
readonly shotsFired: number; readonly shotsFired: number;
readonly overallScore: number; readonly overallScore: number;
readonly powerUpsCollected: number;
readonly pixelsMoved: number;
}; };
export type Player = { export type Player = {

View file

@ -20,8 +20,9 @@ internal sealed class CollectPowerUp(
position.Y < topLeft.Y || position.Y > bottomRight.Y) position.Y < topLeft.Y || position.Y > bottomRight.Y)
continue; 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.ExplosiveBullets += 10;
tank.Owner.Scores.PowerUpsCollected++;
return true; return true;
} }

View file

@ -54,6 +54,7 @@ internal sealed class MoveTanks(
if (HitsTank(tank, newPosition)) if (HitsTank(tank, newPosition))
return false; return false;
tank.Owner.Scores.DistanceMoved += newPosition.Distance(tank.Position);
tank.Position = newPosition; tank.Position = newPosition;
return true; return true;
} }

View file

@ -5,7 +5,7 @@ namespace TanksServer.GameLogic;
internal sealed class TankSpawnQueue( internal sealed class TankSpawnQueue(
IOptions<GameRules> options, IOptions<GameRules> options,
MapEntityManager entityManager MapEntityManager entityManager
): ITickStep ) : ITickStep
{ {
private readonly ConcurrentQueue<Player> _queue = new(); private readonly ConcurrentQueue<Player> _queue = new();
private readonly ConcurrentDictionary<Player, DateTime> _spawnTimes = new(); private readonly ConcurrentDictionary<Player, DateTime> _spawnTimes = new();
@ -16,31 +16,8 @@ internal sealed class TankSpawnQueue(
public void EnqueueForDelayedSpawn(Player player) public void EnqueueForDelayedSpawn(Player player)
{ {
_queue.Enqueue(player);
_spawnTimes.AddOrUpdate(player, DateTime.MinValue, (_, _) => DateTime.Now + _spawnDelay); _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); _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;
} }
public ValueTask TickAsync(TimeSpan _) public ValueTask TickAsync(TimeSpan _)
@ -51,4 +28,29 @@ internal sealed class TankSpawnQueue(
entityManager.SpawnTank(player); entityManager.SpawnTank(player);
return ValueTask.CompletedTask; 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;
}
} }

View file

@ -4,8 +4,6 @@ namespace TanksServer.Interactivity;
[JsonSerializable(typeof(Player))] [JsonSerializable(typeof(Player))]
[JsonSerializable(typeof(IEnumerable<Player>))] [JsonSerializable(typeof(IEnumerable<Player>))]
[JsonSerializable(typeof(Guid))]
[JsonSerializable(typeof(NameId))]
[JsonSerializable(typeof(IEnumerable<string>))] [JsonSerializable(typeof(IEnumerable<string>))]
[JsonSerializable(typeof(PlayerInfo))] [JsonSerializable(typeof(PlayerInfo))]
internal sealed partial class AppSerializerContext : JsonSerializerContext; internal sealed partial class AppSerializerContext : JsonSerializerContext;

View file

@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
namespace TanksServer.Models; namespace TanksServer.Models;
internal sealed record class Scores internal sealed record class Scores
@ -22,5 +24,17 @@ internal sealed record class Scores
public int ShotsFired { get; set; } 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
);
} }

View file

@ -10,8 +10,6 @@ using TanksServer.Interactivity;
namespace TanksServer; namespace TanksServer;
internal sealed record class NameId(string Name, Guid Id);
public static class Program public static class Program
{ {
public static async Task Main(string[] args) public static async Task Main(string[] args)