Chessistics/chessistics-engine/Commands/CampaignCommands.cs

157 lines
5.6 KiB
C#
Raw Normal View History

using Chessistics.Engine.Events;
using Chessistics.Engine.Model;
namespace Chessistics.Engine.Commands;
/// <summary>
/// Load a campaign: initializes the board with mission 0's terrain, stock, and unlocked pieces.
/// </summary>
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<IWorldEvent> 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));
}
}
/// <summary>
/// Advance to the next mission: applies the terrain patch, adds stock, unlocks pieces.
/// </summary>
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<IWorldEvent> 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));
}
}
/// <summary>
/// Move a piece already on the board (drag & drop). Validates the new placement.
/// </summary>
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<IWorldEvent> 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));
}
}