DemandDef gains optional ConsumptionPerTurn and SustainTurns. When ConsumptionPerTurn > 0 the demand maintains a buffer filled by deliveries and drained each turn. Shortage fires the first turn the buffer can't cover consumption; it clears when the buffer refills. SustainedTurns counts consecutive non-shortage turns, and IsSatisfied flips to true once it meets SustainTurns — so the victory condition becomes "no shortage for N consecutive turns" as soon as a mission opts in. Classic demands (ConsumptionPerTurn = 0) behave exactly as before. TurnExecutor runs the consumption sub-phase after transfers. Two new events (DemandShortageStarted / DemandShortageCleared) let the presentation surface the state later. BoardSnapshot + CampaignLoader carry the new fields; no existing mission opts in yet, so campaign_01.json is unaffected.
82 lines
3.4 KiB
C#
82 lines
3.4 KiB
C#
namespace Chessistics.Engine.Model;
|
|
|
|
public class BoardSnapshot
|
|
{
|
|
public int Width { get; }
|
|
public int Height { get; }
|
|
public CellType[,] Grid { get; }
|
|
public IReadOnlyList<ProductionSnapshot> Productions { get; }
|
|
public IReadOnlyList<DemandSnapshot> Demands { get; }
|
|
public IReadOnlyList<PieceSnapshot> Pieces { get; }
|
|
public IReadOnlyList<TransformerSnapshot> Transformers { get; }
|
|
public SimPhase Phase { get; }
|
|
public int TurnNumber { get; }
|
|
public IReadOnlyDictionary<PieceKind, int> RemainingStock { get; }
|
|
|
|
// Campaign info (null in legacy level mode)
|
|
public CampaignSnapshot? Campaign { get; }
|
|
|
|
public BoardSnapshot(BoardState state)
|
|
{
|
|
Width = state.Width;
|
|
Height = state.Height;
|
|
Phase = state.Phase;
|
|
TurnNumber = state.TurnNumber;
|
|
|
|
// Deep copy grid
|
|
Grid = new CellType[Width, Height];
|
|
Array.Copy(state.Grid, Grid, state.Grid.Length);
|
|
|
|
Productions = state.Productions.Values
|
|
.Select(p => new ProductionSnapshot(p.Position, p.Name, p.Cargo, p.Amount, state.ProductionBuffers[p.Position]))
|
|
.ToList();
|
|
|
|
Demands = state.Demands.Values
|
|
.Select(d => new DemandSnapshot(d.Position, d.Name, d.Cargo, d.Required, d.Deadline,
|
|
d.ReceivedCount, d.IsSatisfied, d.MissionIndex,
|
|
d.Definition.ConsumptionPerTurn, d.Definition.SustainTurns,
|
|
d.Buffer, d.SustainedTurns, d.InShortage))
|
|
.ToList();
|
|
|
|
Pieces = state.Pieces
|
|
.Select(p => new PieceSnapshot(p.Id, p.Kind, p.Level, p.StartCell, p.EndCell, p.CurrentCell, p.Cargo, p.CargoFilter, p.SocialStatus))
|
|
.ToList();
|
|
|
|
Transformers = state.Transformers.Values
|
|
.Select(t => new TransformerSnapshot(t.Position, t.Name, t.InputCargo, t.InputRequired, t.OutputCargo, t.OutputAmount,
|
|
state.TransformerInputBuffers[t.Position], state.TransformerOutputBuffers[t.Position]))
|
|
.ToList();
|
|
|
|
RemainingStock = new Dictionary<PieceKind, int>(state.RemainingStock);
|
|
|
|
if (state.Campaign != null)
|
|
{
|
|
Campaign = new CampaignSnapshot(
|
|
state.Campaign.CampaignDef.Name,
|
|
state.Campaign.CurrentMissionIndex,
|
|
state.Campaign.CompletedMissions.ToList(),
|
|
state.Campaign.AvailablePieceKinds.ToHashSet(),
|
|
state.Campaign.AvailableLevels.ToHashSet()
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public record ProductionSnapshot(Coords Position, string Name, CargoType Cargo, int Amount, int BufferCount);
|
|
public record DemandSnapshot(
|
|
Coords Position,
|
|
string Name,
|
|
CargoType Cargo,
|
|
int Required,
|
|
int Deadline,
|
|
int ReceivedCount,
|
|
bool IsSatisfied,
|
|
int MissionIndex = 0,
|
|
int ConsumptionPerTurn = 0,
|
|
int SustainTurns = 0,
|
|
int Buffer = 0,
|
|
int SustainedTurns = 0,
|
|
bool InShortage = false);
|
|
public record PieceSnapshot(int Id, PieceKind Kind, int Level, Coords StartCell, Coords EndCell, Coords CurrentCell, CargoType? Cargo, CargoType? CargoFilter, int SocialStatus);
|
|
public record TransformerSnapshot(Coords Position, string Name, CargoType InputCargo, int InputRequired, CargoType OutputCargo, int OutputAmount, int InputBufferCount, int OutputBufferCount);
|
|
public record CampaignSnapshot(string Name, int CurrentMissionIndex, IReadOnlyList<int> CompletedMissions, IReadOnlySet<PieceKind> AvailablePieceKinds, IReadOnlySet<PieceUpgrade> AvailableLevels);
|