载入中...
搜索中...
未找到
BattleVictory.cpp
浏览该文件的文档.
1#include "rpg/BattleVictory.h"
2
3#include "rpg/ClassSystem.h"
4#include "rpg/GameState.h"
5#include "rpg/LevelSystem.h"
6#include "rpg/RPGActor.h"
7#include "rpg/Tracker.h"
8#include "rpg/WorldState.h"
9
10#include <cmath>
11
12namespace eve::rpg {
13namespace {
14
16 std::string path,
17 const BattleVictoryRequest &request) {
20 {{"mapId", request.mapId}, {"objectId", request.objectId}}, "rpg.battle-victory"));
21}
22
23struct TrackerRollback {
24 std::unordered_map<std::string, QuestRuntime> entries;
25 std::vector<std::string> order;
26 std::vector<QuestEvent> pending;
27 std::vector<QuestEvent> polled;
28};
29
30void restoreTracker(Tracker &tracker, TrackerRollback rollback) {
31 tracker.entries = std::move(rollback.entries);
32 tracker.order = std::move(rollback.order);
33 tracker.pending = std::move(rollback.pending);
34 tracker.polled = std::move(rollback.polled);
35}
36
37bool finiteAddition(const GameState &state, const std::string &id, double amount) {
38 return id.empty() ? amount == 0.0 : std::isfinite(amount) &&
39 std::isfinite(state.getVariable(id) + amount);
40}
41
42} // namespace
43
45BattleVictory::settle(RPGActor &actor, GameState &gameState, Tracker &tracker,
47 if (request.requiredQuestId.empty() || tracker.getState(request.requiredQuestId) != "active")
49 "required quest must be active before victory settlement",
50 "requiredQuestId", request);
51 if (request.notifyTopic.empty() || request.notifyTarget.empty() || request.notifyAmount <= 0)
52 return victoryFailure(eve::DiagnosticCode::InvalidArgument,
53 "victory quest notification must be complete and positive",
54 "notification", request);
55 if (request.attributeId.empty() || !std::isfinite(request.attributeAmount) ||
56 request.attributeAmount < 0.0 ||
57 !finiteAddition(gameState, request.attributeId, request.attributeAmount))
58 return victoryFailure(eve::DiagnosticCode::InvalidArgument,
59 "victory attribute reward is invalid or non-finite", "attribute", request);
60 if (request.defeatCounterId.empty() || request.defeatCounterAmount <= 0 ||
61 !finiteAddition(gameState, request.defeatCounterId,
62 static_cast<double>(request.defeatCounterAmount)))
63 return victoryFailure(eve::DiagnosticCode::InvalidArgument,
64 "victory counter increment is invalid", "defeatCounter", request);
65 if (request.levelPointAttributeId.empty() || request.pointsPerLevel < 0)
66 return victoryFailure(eve::DiagnosticCode::InvalidArgument,
67 "level-point reward configuration is invalid", "levelPoints", request);
68
69 auto preparedXp = LevelSystem::prepareGainXp(&actor, request.xpAmount, request.xpGrowth);
70 if (!preparedXp.ok())
71 return eve::Result<BattleVictoryReceipt>::failure(preparedXp.status());
72 PreparedProgressionGain xp = std::move(preparedXp).takeValue();
73 const int levelsGained = xp.levelsGained();
74 const double levelPoints = static_cast<double>(levelsGained) * request.pointsPerLevel;
75 if (!finiteAddition(gameState, request.levelPointAttributeId, levelPoints))
76 return victoryFailure(eve::DiagnosticCode::InvalidArgument,
77 "level-point reward would produce a non-finite value", "levelPoints", request);
78
79 auto gameStateBefore = gameState.snapshotJson();
80 if (!gameStateBefore.ok())
81 return eve::Result<BattleVictoryReceipt>::failure(gameStateBefore.status());
82 std::string gameStateSnapshot = std::move(gameStateBefore).takeValue();
83 GameState candidateState;
84 auto candidateRestore = candidateState.restoreSnapshotJson(gameStateSnapshot);
85 if (!candidateRestore.ok())
86 return eve::Result<BattleVictoryReceipt>::failure(candidateRestore.status());
87 WorldState candidateWorld(candidateState);
88 if (candidateWorld.isObjectConsumed(request.mapId, request.objectId))
89 return victoryFailure(eve::DiagnosticCode::Conflict,
90 "encounter was already consumed", "objectId", request);
91 auto candidateConsume = candidateWorld.consumeObject(request.mapId, request.objectId);
92 if (!candidateConsume.ok())
93 return eve::Result<BattleVictoryReceipt>::failure(candidateConsume.status());
94
95 TrackerRollback trackerBefore{tracker.entries, tracker.order, tracker.pending, tracker.polled};
96 gameState.addVariable(request.attributeId, request.attributeAmount);
97 gameState.addVariable(request.defeatCounterId, request.defeatCounterAmount);
98 if (levelPoints > 0.0) gameState.addVariable(request.levelPointAttributeId, levelPoints);
99 tracker.notify(request.notifyTopic, request.notifyTarget, request.notifyAmount);
100 WorldState world(gameState);
101 auto consumed = world.consumeObject(request.mapId, request.objectId);
102 if (!consumed.ok() || consumed.code() != eve::StatusCode::Applied) {
103 auto restored = gameState.restoreSnapshotJson(gameStateSnapshot);
104 const bool restoredOk = restored.ok();
105 restoreTracker(tracker, std::move(trackerBefore));
106 if (!restoredOk)
107 return victoryFailure(eve::DiagnosticCode::InvariantViolation,
108 "victory rollback could not restore GameState", "rollback", request);
109 return victoryFailure(eve::DiagnosticCode::Conflict,
110 "encounter changed before victory commit", "objectId", request);
111 }
112
113 auto committedXp = LevelSystem::commitGainXp(std::move(xp));
114 if (!committedXp.ok()) {
115 auto restored = gameState.restoreSnapshotJson(gameStateSnapshot);
116 const bool restoredOk = restored.ok();
117 restoreTracker(tracker, std::move(trackerBefore));
118 if (!restoredOk)
119 return victoryFailure(eve::DiagnosticCode::InvariantViolation,
120 "victory rollback could not restore GameState", "rollback", request);
121 return eve::Result<BattleVictoryReceipt>::failure(committedXp.status());
122 }
123
124 BattleVictoryReceipt receipt;
125 receipt.levelsGained = std::move(committedXp).takeValue();
129}
130
131} // namespace eve::rpg
std::vector< QuestEvent > pending
std::unordered_map< std::string, QuestRuntime > entries
std::vector< QuestEvent > polled
Atomic post-battle progression and persistent-world settlement.
职业系统:设定职业、施加职业基础特征、按等级自动学习技能。
全局游戏状态:开关(bool)、变量(数字)、独立变量(按作用域)。
std::string message
DiagnosticCode code
const GltfImportRequest & request
角色成长:等级 / 经验 / 升级。
std::vector< std::int32_t > order
World3D * world
std::string path
Definition PlayHost.cpp:110
目标追踪器运行时实例(玩家日志 / 成就 / 教程可各建一份)。
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
bool ok() const noexcept
Whether this result represents a non-failure outcome.
Definition Result.h:480
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 eve::Result< BattleVictoryReceipt > settle(RPGActor &actor, GameState &gameState, Tracker &tracker, const BattleVictoryRequest &request)
Atomically settle XP, derived class skills, attributes, quest progress, and encounter consumption.
static int checkLevelSkills(RPGActor *actor)
按当前等级补学尚未学过的升级技能。
一份游戏状态(开关/变量/独立变量)。
Definition GameState.h:25
void addVariable(const std::string &name, double delta)
Adds variable.
Definition GameState.cpp:67
eve::Result< void > restoreSnapshotJson(std::string_view json)
Validate and atomically restore a canonical RPG game-state JSON document.
eve::Result< std::string > snapshotJson() const
Serialize this state as the canonical versioned RPG game-state JSON document.
static eve::Result< PreparedProgressionGain > prepareGainXp(RPGActor *actor, double amount, double xpGrowth=1.2)
Validate an XP gain and calculate its complete progression candidate without mutation/events.
static eve::Result< int > commitGainXp(PreparedProgressionGain prepared)
Commit a prepared XP gain if actor progression is still unchanged.
Move-only progression gain prepared against one actor revision.
Definition LevelSystem.h:25
int levelsGained() const noexcept
Number of level transitions contained in this prepared candidate.
Definition LevelSystem.h:30
属性 / 状态 / 技能三表合一的 ECS 实体。
Definition RPGActor.h:34
一份任务进度 + 本实例事件队列。
Definition Tracker.h:26
std::unordered_map< std::string, QuestRuntime > entries
运行时条目(QuestSystem 读写;公开以便测试与脚本查询)。
Definition Tracker.h:123
std::vector< std::string > order
Definition Tracker.h:124
std::vector< QuestEvent > pending
Definition Tracker.h:125
void notify(const std::string &topic, const std::string &target, int amount)
Notify.
Definition Tracker.cpp:25
std::string getState(const std::string &id) const
Returns the state.
Definition Tracker.cpp:172
std::vector< QuestEvent > polled
Definition Tracker.h:126
Non-owning typed adapter for persistent per-map object lifecycle state.
Definition WorldState.h:19
bool isObjectConsumed(std::string_view mapId, std::string_view objectId) const
Query whether a stable object has been consumed in one map.
eve::Result< void > consumeObject(std::string_view mapId, std::string_view objectId)
Idempotently publish the consumed fact for one stable map object.
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
Observable summary returned after a complete victory settlement.
Complete data needed to settle one victorious persistent encounter.
bool consumed
Definition Graphics.cpp:184