载入中...
搜索中...
未找到
SkillSystem.cpp
浏览该文件的文档.
1#include "rpg/SkillSystem.h"
3#include "rpg/RPGActor.h"
4#include "rpg/Skill.h"
6#include "rpg/StatusSystem.h"
7
8#include <unordered_map>
9
10namespace eve::rpg {
11
12namespace {
13
14std::unordered_map<std::string, SkillSystem::CastCondition> &conditions() {
15 static std::unordered_map<std::string, SkillSystem::CastCondition> c;
16 return c;
17}
18
19std::vector<SkillCastEvent> &castQueue() {
20 static std::vector<SkillCastEvent> q;
21 return q;
22}
23
24bool checkCostsAffordable(RPGActor *actor, const SkillDefinition &def) {
25 if (!def.cost) return true;
26 for (const auto &cost : def.cost->items())
27 if (AttributeSystem::getFinal(actor, cost.resource.value()) < static_cast<double>(cost.amount.value()))
28 return false;
29 return true;
30}
31
32void deductCosts(RPGActor *actor, const SkillDefinition &def) {
33 if (!def.cost) return;
34 for (const auto &cost : def.cost->items())
35 AttributeSystem::modifyBase(actor, cost.resource.value(), -static_cast<double>(cost.amount.value()));
36}
37
38void resolveCast(RPGActor *actor, const SkillDefinition &def, RPGActor *target) {
39 RPGActor *effectTarget = target ? target : actor;
40 for (const std::string &effectId : def.grantedEffects)
41 StatusSystem::apply(effectTarget, effectId, "skill:" + def.id)
42 .ignore("skill effect application is reported through cast events");
43
44 SkillCastEvent evt;
45 evt.caster = actor;
46 evt.target = target;
47 evt.skillId = def.id;
48 castQueue().push_back(std::move(evt));
49}
50
51} // namespace
52
54 conditions()[name] = std::move(fn);
55}
56
57void SkillSystem::unregisterCastCondition(const std::string &name) { conditions().erase(name); }
58
59void SkillSystem::clearCastConditions() { conditions().clear(); }
60
61void SkillSystem::learn(RPGActor *actor, const std::string &skillId) {
62 if (!actor || skillId.empty()) return;
63 actor->skills()->known[skillId]; // default-constructs SkillRuntime if absent
64}
65
66bool SkillSystem::knows(RPGActor *actor, const std::string &skillId) {
67 if (!actor) return false;
68 auto &known = actor->skills()->known;
69 return known.find(skillId) != known.end();
70}
71
72bool SkillSystem::forget(RPGActor *actor, const std::string &skillId) {
73 if (!actor) return false;
74 return actor->skills()->known.erase(skillId) > 0;
75}
76
77std::string SkillSystem::getTargetType(RPGActor *actor, const std::string &skillId) {
78 if (!actor || !knows(actor, skillId)) return {};
79 const SkillDefinition *def = SkillRegistry::find(skillId);
80 return def ? def->targetType : std::string{};
81}
82
83float SkillSystem::getCooldownRemaining(RPGActor *actor, const std::string &skillId) {
84 if (!actor) return 0.f;
85 auto &known = actor->skills()->known;
86 auto it = known.find(skillId);
87 return it == known.end() ? 0.f : it->second.cooldownRemaining;
88}
89
90void SkillSystem::setCooldownRemaining(RPGActor *actor, const std::string &skillId, float seconds) {
91 if (!actor) return;
92 actor->skills()->known[skillId].cooldownRemaining = seconds;
93}
94
95bool SkillSystem::canCast(RPGActor *actor, const std::string &skillId, std::string *reason) {
96 auto fail = [&](const std::string &r) {
97 if (reason) *reason = r;
98 return false;
99 };
100 if (!actor) return fail("no_actor");
101 const SkillDefinition *def = SkillRegistry::find(skillId);
102 if (!def) return fail("unknown_skill");
103 if (!knows(actor, skillId)) return fail("not_learned");
104 if (isCasting(actor)) return fail("already_casting");
105 if (getCooldownRemaining(actor, skillId) > 0.f) return fail("on_cooldown");
106 if (!checkCostsAffordable(actor, *def)) return fail("insufficient_cost");
107
108 for (auto &kv : conditions()) {
109 std::string r;
110 if (!kv.second(actor, *def, r)) return fail(r.empty() ? kv.first : r);
111 }
112 const auto condition = SkillConditionAdapter::evaluate(actor, *def, def->castCondition);
113 if (!condition.passed()) return fail(decision::conditionReasonCodeName(condition.reasonCode()));
114 return true;
115}
116
117bool SkillSystem::beginCast(RPGActor *actor, const std::string &skillId, RPGActor *target,
118 std::string *reason) {
119 if (!canCast(actor, skillId, reason)) return false;
120 const SkillDefinition *def = SkillRegistry::find(skillId);
121
122 deductCosts(actor, *def);
123 setCooldownRemaining(actor, skillId, def->cooldown);
124
125 if (def->castTime <= 0.f) {
126 resolveCast(actor, *def, target);
127 return true;
128 }
129
130 CastingState &cs = actor->skills()->casting;
131 cs.active = true;
132 cs.skillId = skillId;
133 cs.remaining = def->castTime;
134 cs.totalCastTime = def->castTime;
135 cs.target = target;
136 return true;
137}
138
140 if (!actor) return;
141 actor->skills()->casting = CastingState{};
142}
143
144bool SkillSystem::isCasting(RPGActor *actor) { return actor && actor->skills()->casting.active; }
145
147 return actor ? actor->skills()->casting.skillId : std::string{};
148}
149
151 if (!actor) return 0.f;
152 const CastingState &cs = actor->skills()->casting;
153 if (!cs.active || cs.totalCastTime <= 0.f) return 0.f;
154 float p = 1.f - (cs.remaining / cs.totalCastTime);
155 return p < 0.f ? 0.f : (p > 1.f ? 1.f : p);
156}
157
158void SkillSystem::update(float dt) {
159 for (RPGActor *actor : RPGActor::liveActors()) {
160 for (auto &kv : actor->skills()->known)
161 if (kv.second.cooldownRemaining > 0.f) kv.second.cooldownRemaining -= dt;
162
163 CastingState &cs = actor->skills()->casting;
164 if (!cs.active) continue;
165 cs.remaining -= dt;
166 if (cs.remaining <= 0.f) {
168 RPGActor *target = cs.target;
169 cs.active = false; // clear before resolveCast in case it re-enters casting
170 if (def) resolveCast(actor, *def, target);
171 }
172 }
173}
174
175void SkillSystem::pollCastEvents(std::vector<SkillCastEvent> &out) {
176 auto &q = castQueue();
177 for (auto &e : q) out.push_back(std::move(e));
178 q.clear();
179}
180
181} // namespace eve::rpg
LogicalId target
double value
RPG's thin compatibility facade over the attributes module.
glm::vec4 p[6]
eve::resource::CostSpec cost
eve::Value condition
std::array< double, 10 > q
double r
std::int32_t c
std::string name
std::string id
Definition PlayHost.cpp:108
std::string resource
RPG Skill adapter for the renderer-independent decision protocol.
属性 / 状态 / 技能三表合一的 ECS 实体。
Definition RPGActor.h:34
static const std::vector< RPGActor * > & liveActors()
返回所有通过 createActor() 创建、且当前仍存活的 actor。 StatusSystem::update / SkillSystem::update 用它遍历所有 actor 逐帧推进。 ...
Definition RPGActor.cpp:42
static decision::ConditionResult evaluate(const RPGActor *actor, const SkillDefinition &definition, const decision::Condition &condition, SkillConditionQueries queries={})
Evaluate one skill condition against an actor and definition.
static const SkillDefinition * find(const std::string &id)
Find a registered skill definition by id.
Definition Skill.cpp:90
static bool forget(RPGActor *actor, const std::string &skillId)
Forget.
static void update(float dt)
遍历 RPGActor::liveActors():冷却倒计时 + 读条推进/结算。
static void cancelCast(RPGActor *actor)
打断当前读条(不退还已扣除的消耗/冷却)。
static std::string getCastingSkillId(RPGActor *actor)
Returns the casting skill id.
static float getCastProgress(RPGActor *actor)
Returns the cast progress.
static void setCooldownRemaining(RPGActor *actor, const std::string &skillId, float seconds)
Sets the cooldown remaining.
static std::string getTargetType(RPGActor *actor, const std::string &skillId)
技能的目标类型字符串("" 表示未学会/未知)。
static bool isCasting(RPGActor *actor)
True when casting.
static void clearCastConditions()
Clears cast conditions.
static void unregisterCastCondition(const std::string &name)
Unregisters cast condition.
static float getCooldownRemaining(RPGActor *actor, const std::string &skillId)
Returns the cooldown remaining.
static void learn(RPGActor *actor, const std::string &skillId)
Learn.
std::function< bool(RPGActor *actor, const SkillDefinition &def, std::string &outReason)> CastCondition
返回 false 表示禁止释放;通过 outReason 说明原因。
Definition SkillSystem.h:28
static bool knows(RPGActor *actor, const std::string &skillId)
Knows.
static void registerCastCondition(const std::string &name, CastCondition fn)
Registers cast condition.
static bool canCast(RPGActor *actor, const std::string &skillId, std::string *reason=nullptr)
检查冷却/消耗/学会状态/自定义条件,不产生任何副作用。
static bool beginCast(RPGActor *actor, const std::string &skillId, RPGActor *target=nullptr, std::string *reason=nullptr)
尝试释放技能:通过 canCast 检查后立即扣除消耗并进入冷却; castTime<=0 时立即结算(授予效果 + 产生 SkillCastEvent), 否则进入读条状态,由 update() 在读...
static void pollCastEvents(std::vector< SkillCastEvent > &out)
Polls cast events.
const char * conditionReasonCodeName(ConditionReasonCode code) noexcept
Return the stable lowercase spelling of a condition reason code. @ownership Borrowed process-static t...
Definition Condition.cpp:82
RPG 模块的基础实体:把属性 / 状态 / 技能三张表挂到一个 ECS 实体上。
SettlementPipeline::Stage fn
施法状态:同一时间每个 actor 只有一个进行中的引导/读条(可按需扩展为多槽)。
Definition SkillTypes.h:16
class RPGActor * target
Definition SkillTypes.h:21
SkillDefinition public API.
Definition Skill.h:28
float castTime
0 = 瞬发
Definition Skill.h:31
std::string targetType
Definition Skill.h:32
eve::decision::Condition castCondition
Side-effect-free condition tree required before this skill can cast.
Definition Skill.h:38