载入中...
搜索中...
未找到
ParticlesAttackVfx.cpp
浏览该文件的文档.
1#include "common/Capability.h"
2#include "common/Module.h"
8
9#include <ECS.hpp>
10
11#include <cstdint>
12#include <memory>
13#include <string>
14#include <unordered_map>
15#include <utility>
16
17namespace eve::particles {
18namespace {
19
20float layerParam(const eve::stylize::AttackVfxLayerStartRequest& request, const char* key,
21 float fallback) {
22 if (request.layer) {
23 const auto found = request.layer->floatParams.find(key);
24 if (found != request.layer->floatParams.end()) return found->second;
25 }
26 return fallback;
27}
28
29bool effectHasLiveParticles(const ParticleEffect& effect) {
30 const int count = effect.getEmitterCount();
31 for (int i = 0; i < count; ++i) {
32 auto* emitter = effect.getEmitter(i);
33 if (!emitter) continue;
34 if (emitter->getCount() > 0) return true;
35 if (!emitter->isStopped()) return true;
36 }
37 return false;
38}
39
40class ParticlesAttackVfxExecutor final : public eve::stylize::IAttackVfxLayerExecutor {
41public:
52 ~ParticlesAttackVfxExecutor() { abandonEffects(); }
53
55 void clearEffects() { effects_.clear(); }
56
58 void abandonEffects() noexcept {
59 for (auto& entry : effects_) {
60 if (entry.second.effect) (void)entry.second.effect.release();
61 }
62 effects_.clear();
63 }
64
65 eve::stylize::AttackVfxLayerRole role() const noexcept override {
67 }
68
71 if (!request.layer)
73 eve::DiagnosticCode::InvalidArgument, "AttackVfx particles layer is null", "layer"));
74 auto* particles = eve::ModuleManager::getInstance<Particles>("Particles");
75 if (!particles)
77 eve::DiagnosticCode::NotFound, "Particles module is unavailable", "particles"));
78
79 std::unique_ptr<ParticleEffect> effect;
80 const std::string& uri = request.layer->uri;
81 if (uri.rfind("json:", 0) == 0) {
82 effect.reset(particles->newEffectFromText(uri.substr(5)));
83 } else {
84 effect.reset(particles->newEffectFromFile(uri));
85 }
86 if (!effect) {
87 const auto error = particles->getLastEffectError();
89 eve::DiagnosticCode::Failed, error.empty() ? "particle effect could not be loaded" : error, "uri"));
90 }
91
92 // Placement uses authored floatParams; entity ids are not world coordinates.
93 effect->setPosition(layerParam(request, "x", 0.f), layerParam(request, "y", 0.f));
94 if (const auto intensity = request.layer->floatParams.find("intensity");
95 intensity != request.layer->floatParams.end()) {
96 effect->setFloatParameter("intensity", intensity->second);
97 }
98 effect->start();
99
100 Live owned;
101 owned.effect = std::move(effect);
102 const auto id = ++nextId_;
103 effects_.emplace(id, std::move(owned));
106 }
107
110 const auto found = effects_.find(handle.id);
111 if (found == effects_.end())
113 eve::DiagnosticCode::StaleHandle, "particle AttackVfx layer handle is stale", "handle"));
114 if (found->second.draining) {
115 if (!found->second.effect || !effectHasLiveParticles(*found->second.effect)) {
116 effects_.erase(found);
118 }
119 }
121 }
122
124 eve::stylize::AttackVfxStopBehavior behavior) override {
125 const auto found = effects_.find(handle.id);
126 if (found == effects_.end())
128 eve::DiagnosticCode::StaleHandle, "particle AttackVfx layer handle is stale", "handle"));
129 if (found->second.effect) found->second.effect->stop();
131 if (found->second.effect) found->second.effect->reset();
132 effects_.erase(found);
134 }
135 // StopEmitting keeps the effect so residual particles can age out.
136 found->second.draining = true;
138 }
139
140private:
141 struct Live {
142 std::unique_ptr<ParticleEffect> effect;
143 bool draining = false;
144 };
145
146 std::uint64_t nextId_ = 1;
147 std::unordered_map<std::uint64_t, Live> effects_;
148};
149
150ParticlesAttackVfxExecutor& executor() {
151 static ParticlesAttackVfxExecutor instance;
152 return instance;
153}
154
155bool gRegistered = false;
156
157} // namespace
158
160 if (gRegistered) return;
161 // Construct the process ECS Table before this function-local static so
162 // atexit destroys the executor (and any leftover effects) while managers
163 // are still alive. register() runs before any emitter is created.
164 (void)ecs::default_table();
165 eve::cap::addListener<eve::stylize::IAttackVfxLayerExecutor>(&executor());
166 gRegistered = true;
167}
168
170 if (!gRegistered) return;
171 eve::cap::removeListener<eve::stylize::IAttackVfxLayerExecutor>(&executor());
172 // Destroy live ParticleEffects while ECS ComponentManagers still exist.
173 executor().clearEffects();
174 gRegistered = false;
175}
176
177} // namespace eve::particles
Duration start
Pluggable AttackVfx layer backends registered via capability listeners.
ActiveSource owned
bool draining
const GltfImportRequest & request
std::uint32_t key
std::string error
Definition Package.cpp:60
std::unique_ptr< ParticleEffect > effect
std::string uri
PrimitiveHandle handle
bool found
std::uint32_t count
std::unique_ptr< TrailEmitter > emitter
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
Optional open backend for one AttackVfxLayerRole.
void registerParticlesAttackVfxExecutor()
Registers particles attack vfx executor.
void unregisterParticlesAttackVfxExecutor()
Unregisters particles attack vfx executor.
AttackVfxLayerRole
Which existing presentation backend a layer binds to.
AttackVfxStopBehavior
Exit policy when a phase or instance stops.
Opaque executor-owned layer instance identity.
Borrowed start context for one layer inside an entering phase.