载入中...
搜索中...
未找到
TacticsReplay.cpp
浏览该文件的文档.
2
3#include <utility>
4
5namespace eve::tactics {
6namespace {
7
8template <class T>
9Result<void> consume(Result<T>&& result) {
10 if (!result) return Result<void>::failure(result.status());
11 std::move(result).takeValue();
12 return Result<void>::success();
13}
14
15Result<void> apply(Battle& battle, const BattleCommand& command) {
16 switch (command.kind) {
17 case BattleCommandKind::Start: return BattleSystem::start(battle, command.policyId);
18 case BattleCommandKind::Advance: return consume(BattleSystem::advance(battle, command.step));
20 return consume(BattleSystem::moveUnit(battle, command.actor, command.cell, command.step.tick));
22 return BattleSystem::faceUnit(battle, command.actor, command.facing, command.step.tick);
23 case BattleCommandKind::Wait: return BattleSystem::waitUnit(battle, command.actor, command.step.tick);
24 case BattleCommandKind::EndTurn: return BattleSystem::endTurn(battle, command.actor);
27 return consume(BattleSystem::openReaction(battle, command.triggerSequence, command.candidates));
29 return consume(BattleSystem::acceptReaction(battle, command.actor, command.action));
31 case BattleCommandKind::DefeatUnit: return BattleSystem::defeatUnit(battle, command.actor);
32 case BattleCommandKind::RollRandom: return consume(BattleSystem::roll(battle, command.action));
34 // The declared target and the opaque payload are part of the command, so a
35 // replay reproduces the same declaration instead of dropping the effect data
36 // the adapter needs.
37 return consume(BattleSystem::useAbility(battle, command.actor, command.action, command.cell,
38 command.targetUnit, command.payload));
39 }
40 return Result<void>::failure(Diagnostic::error(DiagnosticCode::UnknownVersion, "unknown tactics replay command kind", "command.kind"));
41}
42
43} // namespace
44
45std::vector<BattleCommand> BattleReplay::commandsFrom(Battle& battle, Revision fromRevision) {
46 std::vector<BattleCommand> result;
47 for (const auto& command : battle.commands()->values)
48 if (command.resultingRevision > fromRevision) result.push_back(command);
49 return result;
50}
51
52Result<void> BattleReplay::replay(Battle& battle, std::span<const BattleCommand> commands) {
54 std::uint64_t previousSequence = 0;
55 for (std::size_t index = 0; index < commands.size(); ++index) {
56 const BattleCommand& command = commands[index];
57 const std::string path = "commands[" + std::to_string(index) + "]";
58 if (command.sequence == 0 || (index > 0 && command.sequence != previousSequence + 1))
59 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Conflict, "tactics replay command sequence is not contiguous", path));
60 if (battle.commands()->nextSequence != command.sequence)
61 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Conflict, "tactics replay command sequence differs from target", path));
62 if (battle.turn()->revision != command.expectedRevision)
63 return Result<void>::failure(Diagnostic::error(DiagnosticCode::Conflict, "tactics replay expected revision differs from target", path));
64 auto applied = apply(battle, command);
65 if (!applied) return Result<void>::failure(applied.status());
66 if (battle.turn()->revision != command.resultingRevision)
67 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "tactics replay produced a different revision", path));
68 const auto& recorded = battle.commands()->values.back();
69 if (recorded.kind != command.kind || recorded.sequence != command.sequence ||
70 recorded.expectedRevision != command.expectedRevision ||
71 recorded.resultingRevision != command.resultingRevision)
72 return Result<void>::failure(Diagnostic::error(DiagnosticCode::InvariantViolation, "tactics replay recorded a different command", path));
73 previousSequence = command.sequence;
74 }
76}
77
78} // namespace eve::tactics
std::string path
Definition PlayHost.cpp:110
Deterministic accepted-command replay.
uint32_t index
std::vector< VegetationPresetCommand > commands
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
static std::vector< BattleCommand > commandsFrom(Battle &battle, Revision fromRevision)
Return an owning copy of commands whose result is newer than fromRevision.
static Result< void > replay(Battle &battle, std::span< const BattleCommand > commands)
Apply commands in sequence and verify revision/sequence agreement after every commit.
static Result< void > endTurn(Battle &battle, SubjectRef actor)
End the currently acting unit's turn and enter TurnEnd.
static Result< void > defeatUnit(Battle &battle, SubjectRef unit)
Apply a tactical defeat outcome and evaluate affected objectives atomically.
static Result< std::size_t > openReaction(Battle &battle, std::uint64_t triggerSequence, std::vector< ReactionCandidate > candidates)
Open a sorted reaction window for an existing battle event sequence.
static Result< AbilityReceipt > useAbility(Battle &battle, SubjectRef actor, const LogicalId &action, Cell targetCell, SubjectRef targetUnit={}, std::string payload={})
Declare an ability activation by the active unit.
static Result< std::uint64_t > roll(Battle &battle, const LogicalId &stream)
Consume one value from a battle-owned named deterministic stream.
static Result< void > declineReaction(Battle &battle)
Decline and close the top reaction window without consuming resources.
static Result< void > finish(Battle &battle)
End a running battle and enter BattleEnd.
static Result< void > faceUnit(Battle &battle, SubjectRef actor, int facing)
Change the active unit's logical facing without consuming movement points.
static Result< ReactionReceipt > acceptReaction(Battle &battle, SubjectRef reactor, const LogicalId &action)
Accept one eligible reaction, consume one reaction point and close the top window.
static Result< BattlePhase > advance(Battle &battle, const SimulationStep &step)
Advance exactly one observable automatic phase transition.
static Result< void > waitUnit(Battle &battle, SubjectRef actor)
Commit a wait action and enter TurnEnd for the active unit.
static Result< MoveReceipt > moveUnit(Battle &battle, SubjectRef actor, Cell destination)
Validate and atomically commit movement for the active unit.
static Result< void > start(Battle &battle, std::string policyId)
Start a configured battle and emit battle.started.
Battle short-root and authoritative owner of board and turn state.
@ UseAbility
Declare a unit ability activation against a target cell.
One accepted replay command using only persistent identities and values.