using Chessistics.Engine.Events; using Chessistics.Engine.Model; namespace Chessistics.Engine.Commands; /// /// Load a campaign: initializes the board with mission 0's terrain, stock, and unlocked pieces. /// public class LoadCampaignCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { if (state.Campaign == null) throw new CommandRejectedException( new CommandRejectedEvent(nameof(LoadCampaignCommand), "No campaign defined.")); if (state.Campaign.CampaignDef.Missions.Count == 0) throw new CommandRejectedException( new CommandRejectedEvent(nameof(LoadCampaignCommand), "Campaign has no missions.")); } protected override void DoApply(BoardState state, List changeList) { var campaign = state.Campaign!; var mission = campaign.CurrentMission; // Apply terrain patch for mission 0 state.ApplyTerrainPatch(mission.TerrainPatch, campaign.CurrentMissionIndex); // Add stock state.AddStock(mission.Stock); // Unlock pieces and levels foreach (var kind in mission.UnlockedPieces) { campaign.AvailablePieceKinds.Add(kind); changeList.Add(new PieceUnlockedEvent(kind, 1)); } foreach (var upgrade in mission.UnlockedLevels) { campaign.AvailableLevels.Add(upgrade); changeList.Add(new PieceUnlockedEvent(upgrade.Kind, upgrade.Level)); } state.Phase = SimPhase.Paused; changeList.Add(new CampaignLoadedEvent(campaign.CampaignDef.Name, 0)); changeList.Add(new MissionStartedEvent(0, state.Width, state.Height)); } } /// /// Advance to the next mission: applies the terrain patch, adds stock, unlocks pieces. /// public class AdvanceMissionCommand : WorldCommand { public override void AssertApplicationConditions(BoardState state) { if (state.Campaign == null) throw new CommandRejectedException( new CommandRejectedEvent(nameof(AdvanceMissionCommand), "No campaign defined.")); if (state.Phase != SimPhase.MissionComplete) throw new CommandRejectedException( new CommandRejectedEvent(nameof(AdvanceMissionCommand), "Current mission is not complete.")); if (state.Campaign.IsLastMission) throw new CommandRejectedException( new CommandRejectedEvent(nameof(AdvanceMissionCommand), "No more missions.")); } protected override void DoApply(BoardState state, List changeList) { var campaign = state.Campaign!; campaign.CurrentMissionIndex++; var mission = campaign.CurrentMission; // Apply terrain expansion var oldWidth = state.Width; var oldHeight = state.Height; state.ApplyTerrainPatch(mission.TerrainPatch, campaign.CurrentMissionIndex); if (state.Width != oldWidth || state.Height != oldHeight) changeList.Add(new TerrainExpandedEvent(state.Width, state.Height, mission.TerrainPatch.Cells)); // Add stock state.AddStock(mission.Stock); // Unlock pieces and levels foreach (var kind in mission.UnlockedPieces) { campaign.AvailablePieceKinds.Add(kind); changeList.Add(new PieceUnlockedEvent(kind, 1)); } foreach (var upgrade in mission.UnlockedLevels) { campaign.AvailableLevels.Add(upgrade); changeList.Add(new PieceUnlockedEvent(upgrade.Kind, upgrade.Level)); } state.Phase = SimPhase.Paused; changeList.Add(new MissionStartedEvent(campaign.CurrentMissionIndex, state.Width, state.Height)); } } /// /// Move a piece already on the board (drag & drop). Validates the new placement. /// public class MovePieceCommand : WorldCommand { public int PieceId { get; } public Coords NewStart { get; } public Coords NewEnd { get; } public MovePieceCommand(int pieceId, Coords newStart, Coords newEnd) { PieceId = pieceId; NewStart = newStart; NewEnd = newEnd; } public override void AssertApplicationConditions(BoardState state) { var piece = state.GetPieceById(PieceId); if (piece == null) throw new CommandRejectedException( new CommandRejectedEvent(nameof(MovePieceCommand), $"Piece {PieceId} not found.")); if (!state.IsOnBoard(NewStart) || !state.IsOnBoard(NewEnd)) throw new CommandRejectedException( new CommandRejectedEvent(nameof(MovePieceCommand), "Position is off the board.")); if (state.GetCell(NewStart) == CellType.Wall) throw new CommandRejectedException( new CommandRejectedEvent(nameof(MovePieceCommand), "Cannot place on a wall.")); if (!Rules.MoveValidator.IsLegalPlacement(piece.Kind, NewStart, NewEnd, state)) throw new CommandRejectedException( new CommandRejectedEvent(nameof(MovePieceCommand), "Illegal move for this piece type.")); } protected override void DoApply(BoardState state, List changeList) { var piece = state.GetPieceById(PieceId)!; var oldStart = piece.StartCell; var oldEnd = piece.EndCell; piece.SetPosition(NewStart, NewEnd); piece.Cargo = null; state.OccupiedCells.Add(NewStart); state.OccupiedCells.Add(NewEnd); changeList.Add(new PieceMovedByPlayerEvent(PieceId, oldStart, oldEnd, NewStart, NewEnd)); } }