mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 17:43:52 +00:00
feat: separate cast/impact kit paths in spell visual DBC lookup
loadSpellVisualDbc() now builds two distinct maps: spellVisualCastPath_ — visualId → M2 via SpellVisual.CastKit chain spellVisualImpactPath_ — visualId → M2 via SpellVisual.ImpactKit chain playSpellVisual() accepts useImpactKit=false (default, cast) / true (impact). SMSG_PLAY_SPELL_IMPACT passes useImpactKit=true so impact effects (explosions, debuff indicators) use the ImpactKit model instead of the CastKit model. Added ImpactKit field to all four dbc_layouts.json files.
This commit is contained in:
parent
d558e3a927
commit
36fed15d43
3 changed files with 51 additions and 38 deletions
|
|
@ -154,8 +154,10 @@ public:
|
||||||
void triggerLevelUpEffect(const glm::vec3& position);
|
void triggerLevelUpEffect(const glm::vec3& position);
|
||||||
void cancelEmote();
|
void cancelEmote();
|
||||||
|
|
||||||
// Spell visual effects (SMSG_PLAY_SPELL_VISUAL)
|
// Spell visual effects (SMSG_PLAY_SPELL_VISUAL / SMSG_PLAY_SPELL_IMPACT)
|
||||||
void playSpellVisual(uint32_t visualId, const glm::vec3& worldPosition);
|
// useImpactKit=false → CastKit path; useImpactKit=true → ImpactKit path
|
||||||
|
void playSpellVisual(uint32_t visualId, const glm::vec3& worldPosition,
|
||||||
|
bool useImpactKit = false);
|
||||||
bool isEmoteActive() const { return emoteActive; }
|
bool isEmoteActive() const { return emoteActive; }
|
||||||
static std::string getEmoteText(const std::string& emoteName, const std::string* targetName = nullptr);
|
static std::string getEmoteText(const std::string& emoteName, const std::string* targetName = nullptr);
|
||||||
static uint32_t getEmoteDbcId(const std::string& emoteName);
|
static uint32_t getEmoteDbcId(const std::string& emoteName);
|
||||||
|
|
@ -328,11 +330,12 @@ private:
|
||||||
|
|
||||||
pipeline::AssetManager* cachedAssetManager = nullptr;
|
pipeline::AssetManager* cachedAssetManager = nullptr;
|
||||||
|
|
||||||
// Spell visual effects — transient M2 instances spawned by SMSG_PLAY_SPELL_VISUAL
|
// Spell visual effects — transient M2 instances spawned by SMSG_PLAY_SPELL_VISUAL/IMPACT
|
||||||
struct SpellVisualInstance { uint32_t instanceId; float elapsed; };
|
struct SpellVisualInstance { uint32_t instanceId; float elapsed; };
|
||||||
std::vector<SpellVisualInstance> activeSpellVisuals_;
|
std::vector<SpellVisualInstance> activeSpellVisuals_;
|
||||||
std::unordered_map<uint32_t, std::string> spellVisualModelPath_; // visualId → resolved M2 path
|
std::unordered_map<uint32_t, std::string> spellVisualCastPath_; // visualId → cast M2 path
|
||||||
std::unordered_map<std::string, uint32_t> spellVisualModelIds_; // M2 path → M2Renderer modelId
|
std::unordered_map<uint32_t, std::string> spellVisualImpactPath_; // visualId → impact M2 path
|
||||||
|
std::unordered_map<std::string, uint32_t> spellVisualModelIds_; // M2 path → M2Renderer modelId
|
||||||
uint32_t nextSpellVisualModelId_ = 999000; // Reserved range 999000-999799
|
uint32_t nextSpellVisualModelId_ = 999000; // Reserved range 999000-999799
|
||||||
bool spellVisualDbcLoaded_ = false;
|
bool spellVisualDbcLoaded_ = false;
|
||||||
void loadSpellVisualDbc();
|
void loadSpellVisualDbc();
|
||||||
|
|
|
||||||
|
|
@ -7431,7 +7431,7 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
glm::vec3 canonical(entity->getLatestX(), entity->getLatestY(), entity->getLatestZ());
|
glm::vec3 canonical(entity->getLatestX(), entity->getLatestY(), entity->getLatestZ());
|
||||||
spawnPos = core::coords::canonicalToRender(canonical);
|
spawnPos = core::coords::canonicalToRender(canonical);
|
||||||
}
|
}
|
||||||
renderer->playSpellVisual(impVisualId, spawnPos);
|
renderer->playSpellVisual(impVisualId, spawnPos, /*useImpactKit=*/true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2644,11 +2644,13 @@ void Renderer::loadSpellVisualDbc() {
|
||||||
const pipeline::DBCFieldMap* fxLayout = layout ? layout->getLayout("SpellVisualEffectName") : nullptr;
|
const pipeline::DBCFieldMap* fxLayout = layout ? layout->getLayout("SpellVisualEffectName") : nullptr;
|
||||||
|
|
||||||
uint32_t svCastKitField = svLayout ? (*svLayout)["CastKit"] : 2;
|
uint32_t svCastKitField = svLayout ? (*svLayout)["CastKit"] : 2;
|
||||||
|
uint32_t svImpactKitField = svLayout ? (*svLayout)["ImpactKit"] : 3;
|
||||||
uint32_t svMissileField = svLayout ? (*svLayout)["MissileModel"] : 8;
|
uint32_t svMissileField = svLayout ? (*svLayout)["MissileModel"] : 8;
|
||||||
uint32_t kitSpecial0Field = kitLayout ? (*kitLayout)["SpecialEffect0"] : 11;
|
uint32_t kitSpecial0Field = kitLayout ? (*kitLayout)["SpecialEffect0"] : 11;
|
||||||
uint32_t kitBaseField = kitLayout ? (*kitLayout)["BaseEffect"] : 5;
|
uint32_t kitBaseField = kitLayout ? (*kitLayout)["BaseEffect"] : 5;
|
||||||
uint32_t fxFilePathField = fxLayout ? (*fxLayout)["FilePath"] : 2;
|
uint32_t fxFilePathField = fxLayout ? (*fxLayout)["FilePath"] : 2;
|
||||||
|
|
||||||
|
// Helper to look up effectName path from a kit ID
|
||||||
// Load SpellVisualEffectName.dbc — ID → M2 path
|
// Load SpellVisualEffectName.dbc — ID → M2 path
|
||||||
auto fxDbc = cachedAssetManager->loadDBC("SpellVisualEffectName.dbc");
|
auto fxDbc = cachedAssetManager->loadDBC("SpellVisualEffectName.dbc");
|
||||||
if (!fxDbc || !fxDbc->isLoaded() || fxDbc->getFieldCount() <= fxFilePathField) {
|
if (!fxDbc || !fxDbc->isLoaded() || fxDbc->getFieldCount() <= fxFilePathField) {
|
||||||
|
|
@ -2679,50 +2681,57 @@ void Renderer::loadSpellVisualDbc() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load SpellVisual.dbc — visualId → M2 path via kit chain
|
// Helper: resolve path for a given kit ID
|
||||||
|
auto kitPath = [&](uint32_t kitId) -> std::string {
|
||||||
|
if (!kitId) return {};
|
||||||
|
auto kitIt = kitToEffectName.find(kitId);
|
||||||
|
if (kitIt == kitToEffectName.end()) return {};
|
||||||
|
auto fxIt = effectPaths.find(kitIt->second);
|
||||||
|
return (fxIt != effectPaths.end()) ? fxIt->second : std::string{};
|
||||||
|
};
|
||||||
|
auto missilePath = [&](uint32_t effId) -> std::string {
|
||||||
|
if (!effId) return {};
|
||||||
|
auto fxIt = effectPaths.find(effId);
|
||||||
|
return (fxIt != effectPaths.end()) ? fxIt->second : std::string{};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Load SpellVisual.dbc — visualId → cast/impact M2 paths via kit chain
|
||||||
auto svDbc = cachedAssetManager->loadDBC("SpellVisual.dbc");
|
auto svDbc = cachedAssetManager->loadDBC("SpellVisual.dbc");
|
||||||
if (!svDbc || !svDbc->isLoaded()) {
|
if (!svDbc || !svDbc->isLoaded()) {
|
||||||
LOG_DEBUG("SpellVisual: SpellVisual.dbc unavailable");
|
LOG_DEBUG("SpellVisual: SpellVisual.dbc unavailable");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
uint32_t svFc = svDbc->getFieldCount();
|
uint32_t svFc = svDbc->getFieldCount();
|
||||||
uint32_t loaded = 0;
|
uint32_t loadedCast = 0, loadedImpact = 0;
|
||||||
for (uint32_t i = 0; i < svDbc->getRecordCount(); ++i) {
|
for (uint32_t i = 0; i < svDbc->getRecordCount(); ++i) {
|
||||||
uint32_t vid = svDbc->getUInt32(i, 0);
|
uint32_t vid = svDbc->getUInt32(i, 0);
|
||||||
if (!vid) continue;
|
if (!vid) continue;
|
||||||
|
|
||||||
std::string path;
|
// Cast path: CastKit → SpecialEffect0/BaseEffect, fallback to MissileModel
|
||||||
|
{
|
||||||
// Try CastKit → SpellVisualKit → SpecialEffect0 path
|
std::string path;
|
||||||
if (svCastKitField < svFc) {
|
if (svCastKitField < svFc)
|
||||||
uint32_t kitId = svDbc->getUInt32(i, svCastKitField);
|
path = kitPath(svDbc->getUInt32(i, svCastKitField));
|
||||||
if (kitId) {
|
if (path.empty() && svMissileField < svFc)
|
||||||
auto kitIt = kitToEffectName.find(kitId);
|
path = missilePath(svDbc->getUInt32(i, svMissileField));
|
||||||
if (kitIt != kitToEffectName.end()) {
|
if (!path.empty()) { spellVisualCastPath_[vid] = path; ++loadedCast; }
|
||||||
auto fxIt = effectPaths.find(kitIt->second);
|
|
||||||
if (fxIt != effectPaths.end()) path = fxIt->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Fallback: MissileModel directly references SpellVisualEffectName
|
// Impact path: ImpactKit → SpecialEffect0/BaseEffect, fallback to MissileModel
|
||||||
if (path.empty() && svMissileField < svFc) {
|
{
|
||||||
uint32_t missileEff = svDbc->getUInt32(i, svMissileField);
|
std::string path;
|
||||||
if (missileEff) {
|
if (svImpactKitField < svFc)
|
||||||
auto fxIt = effectPaths.find(missileEff);
|
path = kitPath(svDbc->getUInt32(i, svImpactKitField));
|
||||||
if (fxIt != effectPaths.end()) path = fxIt->second;
|
if (path.empty() && svMissileField < svFc)
|
||||||
}
|
path = missilePath(svDbc->getUInt32(i, svMissileField));
|
||||||
}
|
if (!path.empty()) { spellVisualImpactPath_[vid] = path; ++loadedImpact; }
|
||||||
|
|
||||||
if (!path.empty()) {
|
|
||||||
spellVisualModelPath_[vid] = path;
|
|
||||||
++loaded;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
LOG_INFO("SpellVisual: loaded ", loaded, " visual→M2 mappings (of ",
|
LOG_INFO("SpellVisual: loaded cast=", loadedCast, " impact=", loadedImpact,
|
||||||
svDbc->getRecordCount(), " records)");
|
" visual→M2 mappings (of ", svDbc->getRecordCount(), " records)");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::playSpellVisual(uint32_t visualId, const glm::vec3& worldPosition) {
|
void Renderer::playSpellVisual(uint32_t visualId, const glm::vec3& worldPosition,
|
||||||
|
bool useImpactKit) {
|
||||||
if (!m2Renderer || visualId == 0) return;
|
if (!m2Renderer || visualId == 0) return;
|
||||||
|
|
||||||
if (!cachedAssetManager)
|
if (!cachedAssetManager)
|
||||||
|
|
@ -2731,9 +2740,10 @@ void Renderer::playSpellVisual(uint32_t visualId, const glm::vec3& worldPosition
|
||||||
|
|
||||||
if (!spellVisualDbcLoaded_) loadSpellVisualDbc();
|
if (!spellVisualDbcLoaded_) loadSpellVisualDbc();
|
||||||
|
|
||||||
// Find the M2 path for this visual
|
// Select cast or impact path map
|
||||||
auto pathIt = spellVisualModelPath_.find(visualId);
|
auto& pathMap = useImpactKit ? spellVisualImpactPath_ : spellVisualCastPath_;
|
||||||
if (pathIt == spellVisualModelPath_.end()) return; // No model for this visual
|
auto pathIt = pathMap.find(visualId);
|
||||||
|
if (pathIt == pathMap.end()) return; // No model for this visual
|
||||||
|
|
||||||
const std::string& modelPath = pathIt->second;
|
const std::string& modelPath = pathIt->second;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue