8#include <unordered_map>
9#include <unordered_set>
15std::unordered_map<std::string, BattleTacticsDefinition> &
definitions() {
16 static std::unordered_map<std::string, BattleTacticsDefinition>
value;
20bool validId(
const std::string &
value,
bool allowEmpty =
false) {
21 if (
value.empty())
return allowEmpty;
22 if (
value.size() > 256)
return false;
23 for (
unsigned char ch :
value)
24 if (ch < 0x20 || ch == 0x7f) return false;
37bool policyMatchesTargetType(
BattleTargetPolicy policy,
const std::string &targetType) {
47 std::string parseError;
49 if (!document.valid())
51 parseError.empty() ?
"invalid JSON" : parseError,
"$",
52 {},
"rpg.battle-tactics"));
53 const auto root = document.root();
54 if (!
root.isArray() ||
root.size() == 0)
56 "battle tactics catalogue must be a non-empty array",
57 "$", {},
"rpg.battle-tactics"));
58 const std::unordered_set<std::string> definitionFields = {
"id",
"rules"};
59 const std::unordered_set<std::string> ruleFields = {
60 "skillId",
"targetPolicy",
"conditionResource",
"belowRatio"};
61 std::unordered_map<std::string, BattleTacticsDefinition> proposed;
64 const std::string
path =
"$[" + std::to_string(
index) +
"]";
65 if (!
object.isObject())
67 "battle tactics definition must be an object",
path,
68 {},
"rpg.battle-tactics"));
69 for (
const auto &
key :
object.keys())
70 if (!definitionFields.contains(
key))
73 path +
"." +
key, {},
"rpg.battle-tactics"));
74 const auto id =
object.get(
"id");
75 const auto rules =
object.get(
"rules");
76 if (!
id.isString() || !validId(
id.asString()))
78 "battle tactics id must be a stable non-empty id",
79 path +
".id", {},
"rpg.battle-tactics"));
80 if (proposed.contains(
id.asString()))
82 "duplicate battle tactics id",
path +
".id", {},
83 "rpg.battle-tactics"));
84 if (!rules.isArray() || rules.size() == 0 || rules.size() > 16)
86 "rules must contain between 1 and 16 entries",
87 path +
".rules", {},
"rpg.battle-tactics"));
89 definition.id =
id.asString();
90 for (std::size_t ruleIndex = 0; ruleIndex < rules.size(); ++ruleIndex) {
91 const auto rule = rules.at(ruleIndex);
92 const std::string rulePath =
path +
".rules[" + std::to_string(ruleIndex) +
"]";
95 "battle tactic rule must be an object",
96 rulePath, {},
"rpg.battle-tactics"));
97 for (
const auto &
key : rule.keys())
98 if (!ruleFields.contains(
key))
101 rulePath +
"." +
key, {},
"rpg.battle-tactics"));
102 const auto skillId = rule.get(
"skillId");
103 const auto targetPolicy = rule.get(
"targetPolicy");
104 const auto conditionResource = rule.get(
"conditionResource");
105 const auto belowRatio = rule.get(
"belowRatio");
106 if (!skillId.isString() || !validId(skillId.asString(),
true) ||
110 rulePath +
".skillId", {},
"rpg.battle-tactics"));
112 if (!targetPolicy.isString() || !parsePolicy(targetPolicy.asString(), parsedPolicy))
115 rulePath +
".targetPolicy", {},
"rpg.battle-tactics"));
116 const std::string targetType = skillId.asString().empty()
119 if (!policyMatchesTargetType(parsedPolicy, targetType))
122 rulePath +
".targetPolicy", {},
"rpg.battle-tactics"));
123 if (!conditionResource.isString() || !validId(conditionResource.asString(),
true) ||
124 !belowRatio.isNumber() || !std::isfinite(belowRatio.asDouble()) ||
125 belowRatio.asDouble() < 0.0 || belowRatio.asDouble() > 1.0)
127 "conditionResource and belowRatio are invalid",
128 rulePath, {},
"rpg.battle-tactics"));
132 rulePath, {},
"rpg.battle-tactics"));
134 parsed.
skillId = skillId.asString();
138 definition.rules.push_back(std::move(parsed));
140 if (!definition.rules.back().conditionResource.empty())
143 path +
".rules", {},
"rpg.battle-tactics"));
144 proposed.emplace(definition.id, std::move(definition));
154 const std::string &tacticsId) {
155 if (!battle || !actor)
157 "battle and actor must not be null",
"battle",
158 {},
"rpg.battle-tactics"));
162 "battle tactics id is not registered",
163 "tacticsId", {},
"rpg.battle-tactics"));
164 for (
const auto &rule :
found->second.rules) {
165 if (!rule.conditionResource.empty()) {
166 const int side = battle->sideOf(actor);
169 : battle->lowestHealthTarget(
173 if (maximum <= 0.0 || target->getCurrent(rule.conditionResource) /
maximum >= rule.belowRatio)
181 "battle tactics has no applicable default rule",
182 "rules", {},
"rpg.battle-tactics"));
Strict ordered battle-action policies for companions and AI.
const std::map< std::string, Value > & definitions
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.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
static Document parse(const std::string &text, std::string *error=nullptr)
Parse.
static int count()
Returns the number of .
static eve::Result< std::string > queueAction(Battle *battle, RPGActor *actor, const std::string &tacticsId)
Select the first matching rule and queue it through Battle's canonical checked API.
static eve::Result< int > replaceFromJsonStrict(const std::string &json)
Validate and atomically replace all tactic definitions.
一场回合制战斗(任意阵营数)。 @ownership 由调用方创建/销毁;Battle 不持有参与者的所有权(参与者为借用 ECS actor)。 @thread 在单一模拟线程上驱动整场战斗。
eve::Result< void > setActionByPolicyChecked(RPGActor *actor, const std::string &skillId, BattleTargetPolicy policy)
Resolve a deterministic target policy and queue one checked action atomically.
属性 / 状态 / 技能三表合一的 ECS 实体。
double getMax(const std::string &resource)
static const SkillDefinition * find(const std::string &id)
Find a registered skill definition by id.
bool parsePolicy(const std::string &name, StackPolicy &policy)
Parses a lowercase legacy stack policy name.
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
BattleTargetPolicy
Deterministic target-selection policies for queued battle actions.
One ordered tactic rule; an empty condition resource is an unconditional default.
BattleTargetPolicy targetPolicy
std::string conditionResource
One immutable ordered tactic definition.