51 lines
1.5 KiB
C#
51 lines
1.5 KiB
C#
using MessagePack;
|
|
using OECS;
|
|
|
|
namespace TicTacToe;
|
|
|
|
/// <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)
|
|
{
|
|
ref var state = ref world.GetSingleton<GameState>();
|
|
|
|
if (state.Status != GameStatus.Playing)
|
|
return;
|
|
|
|
// Find the cell entity at (Row, Col) that has no Mark.
|
|
// Use the iterator API with early-exit via break.
|
|
var query = world.Query().With<Cell>().Without<Mark>().Build();
|
|
Entity? target = null;
|
|
|
|
using var iter = world.Select<Cell>(query);
|
|
while (iter.MoveNext())
|
|
{
|
|
if (iter.Current1.Row == Row && iter.Current1.Col == Col)
|
|
{
|
|
target = iter.CurrentEntity;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (target == null)
|
|
return; // Cell already occupied or invalid position.
|
|
|
|
// Place the mark. ComponentAdded is sufficient — MarkModified
|
|
// is only needed when mutating an existing component via GetComponent<T>.
|
|
world.AddComponent(target.Value, new Mark { Player = state.CurrentPlayer });
|
|
|
|
// Advance turn.
|
|
state.MoveCount++;
|
|
state.CurrentPlayer = state.CurrentPlayer == Player.X ? Player.O : Player.X;
|
|
world.MarkModified<GameState>(World.SingletonEntity);
|
|
}
|
|
}
|