add power ups collected score
This commit is contained in:
		
							parent
							
								
									bf22fd6c85
								
							
						
					
					
						commit
						259d63d683
					
				
					 8 changed files with 45 additions and 32 deletions
				
			
		|  | @ -74,6 +74,7 @@ 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="score" value={lastJsonMessage.scores.overallScore}/> |             <ScoreRow name="score" value={lastJsonMessage.scores.overallScore}/> | ||||||
|             </tbody> |             </tbody> | ||||||
|  |  | ||||||
|  | @ -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,11 @@ 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: 'score', |                 field: 'score', | ||||||
|                 visualize: p => p.scores.overallScore.toString(), |                 visualize: p => p.scores.overallScore.toString(), | ||||||
|  |  | ||||||
|  | @ -8,6 +8,7 @@ export type Scores = { | ||||||
|     readonly wallsDestroyed: number; |     readonly wallsDestroyed: number; | ||||||
|     readonly shotsFired: number; |     readonly shotsFired: number; | ||||||
|     readonly overallScore: number; |     readonly overallScore: number; | ||||||
|  |     readonly powerUpsCollected: number; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export type Player = { | export type Player = { | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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; | ||||||
|  |  | ||||||
|  | @ -22,5 +22,12 @@ 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; } | ||||||
|  | 
 | ||||||
|  |     public int OverallScore => Math.Max(0, | ||||||
|  |         10000 * Kills | ||||||
|  |         - 1000 * Deaths | ||||||
|  |         + 100 * PowerUpsCollected | ||||||
|  |         + 10 * (ShotsFired + WallsDestroyed) | ||||||
|  |     ); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Vinzenz Schroeter
						Vinzenz Schroeter