2026-07-18 20:43:13 +08:00
|
|
|
using MessagePack;
|
|
|
|
|
using OECS;
|
|
|
|
|
|
2026-07-20 16:41:10 +08:00
|
|
|
namespace Game.TicTacToe;
|
2026-07-18 20:43:13 +08:00
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Places the current player's mark on the cell at (Row, Col).
|
|
|
|
|
/// Validates that the cell is empty and the game is still playing.
|
|
|
|
|
/// </summary>
|
|
|
|
|
[MessagePackObject]
|
|
|
|
|
public struct PlaceMarkCommand : ICommand
|
|
|
|
|
{
|
|
|
|
|
[Key(0)] public int Row;
|
|
|
|
|
[Key(1)] public int Col;
|
|
|
|
|
|
|
|
|
|
public void Execute(World world)
|
|
|
|
|
{
|
2026-07-21 09:15:27 +08:00
|
|
|
if (world.ReadSingleton<GameState>().Status != GameStatus.Playing)
|
2026-07-18 20:43:13 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
// Find the cell entity at (Row, Col) that has no Mark.
|
|
|
|
|
Entity? target = null;
|
|
|
|
|
|
2026-07-21 09:15:27 +08:00
|
|
|
ref var state = ref world.GetSingleton<GameState>();
|
2026-07-21 11:09:55 +08:00
|
|
|
foreach(var iter in world.Select(new Query<Cell>().Without<Mark>())){
|
|
|
|
|
if (iter.Val1.Row != Row || iter.Val1.Col != Col) continue;
|
|
|
|
|
target = iter.Entity;
|
|
|
|
|
break;
|
2026-07-18 21:22:23 +08:00
|
|
|
}
|
2026-07-18 20:43:13 +08:00
|
|
|
|
|
|
|
|
if (target == null)
|
2026-07-21 11:09:55 +08:00
|
|
|
return;
|
2026-07-18 20:43:13 +08:00
|
|
|
|
|
|
|
|
world.AddComponent(target.Value, new Mark { Player = state.CurrentPlayer });
|
|
|
|
|
|
|
|
|
|
state.MoveCount++;
|
|
|
|
|
state.CurrentPlayer = state.CurrentPlayer == Player.X ? Player.O : Player.X;
|
|
|
|
|
}
|
|
|
|
|
}
|