mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-15 08:53:51 +00:00
Merge master into chore/god-object-decomposition-2nd
Resolve conflicts: - audio_callback_handler.cpp: keep PR's animation_controller include - movement_handler.cpp: use PR accessors with master's transportResolved logic - world_packets.cpp: keep PR's decomposed version (functions moved to split files) Apply overkill field fix to world_packets_entity.cpp (WotLK SMSG_ATTACKERSTATEUPDATE missing uint32 overkill between damage and subDamageCount).
This commit is contained in:
commit
e32f4fbff9
9 changed files with 148 additions and 35 deletions
|
|
@ -1,6 +1,7 @@
|
|||
#include "rendering/animation/combat_fsm.hpp"
|
||||
#include "rendering/animation/animation_ids.hpp"
|
||||
#include "game/inventory.hpp"
|
||||
#include "core/logger.hpp"
|
||||
|
||||
namespace wowee {
|
||||
namespace rendering {
|
||||
|
|
@ -378,7 +379,10 @@ AnimOutput CombatFSM::resolve(const Input& in, const AnimCapabilitySet& caps,
|
|||
animId = caps.resolvedMelee1H;
|
||||
}
|
||||
}
|
||||
if (animId == 0) animId = anim::STAND; // Melee must play something
|
||||
if (animId == 0) {
|
||||
LOG_DEBUG("CombatFSM: MELEE_SWING resolved animId=0, falling back to STAND");
|
||||
animId = anim::STAND;
|
||||
}
|
||||
loop = false;
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ static std::vector<std::string> parseEmoteCommands(const std::string& raw) {
|
|||
|
||||
static bool isLoopingEmote(const std::string& command) {
|
||||
static const std::unordered_set<std::string> kLooping = {
|
||||
"dance", "train", "dead", "eat", "work",
|
||||
"dance", "train", "dead", "eat", "work", "sleep",
|
||||
};
|
||||
return kLooping.find(command) != kLooping.end();
|
||||
}
|
||||
|
|
@ -117,6 +117,9 @@ void EmoteRegistry::loadFromDbc() {
|
|||
uint32_t animId = emotesDbc->getUInt32(r, emL ? (*emL)["AnimID"] : 2);
|
||||
if (animId != 0) emoteIdToAnim[emoteId] = animId;
|
||||
}
|
||||
LOG_WARNING("Emotes: loaded ", emoteIdToAnim.size(), " anim mappings from Emotes.dbc");
|
||||
} else {
|
||||
LOG_WARNING("Emotes: Emotes.dbc failed to load — all emotes will use fallback animations");
|
||||
}
|
||||
|
||||
emoteTable_.clear();
|
||||
|
|
@ -128,11 +131,13 @@ void EmoteRegistry::loadFromDbc() {
|
|||
|
||||
uint32_t emoteRef = emotesTextDbc->getUInt32(r, etL ? (*etL)["EmoteRef"] : 2);
|
||||
uint32_t animId = 0;
|
||||
auto animIt = emoteIdToAnim.find(emoteRef);
|
||||
if (animIt != emoteIdToAnim.end()) {
|
||||
animId = animIt->second;
|
||||
} else {
|
||||
animId = emoteRef;
|
||||
if (emoteRef != 0) {
|
||||
auto animIt = emoteIdToAnim.find(emoteRef);
|
||||
if (animIt != emoteIdToAnim.end()) {
|
||||
animId = animIt->second;
|
||||
}
|
||||
// If Emotes.dbc has AnimID=0 for this ref, leave animId=0 (text-only).
|
||||
// Previously fell back to using emoteRef as animId which is wrong.
|
||||
}
|
||||
|
||||
uint32_t senderTargetTextId = emotesTextDbc->getUInt32(r, etL ? (*etL)["SenderTargetTextID"] : 5);
|
||||
|
|
@ -161,11 +166,33 @@ void EmoteRegistry::loadFromDbc() {
|
|||
}
|
||||
}
|
||||
|
||||
// Override emotes whose DBC chain yields animId=0.
|
||||
// /sleep uses the stand-state system in WoW rather than Emotes.dbc AnimID.
|
||||
// /laugh and /flirt should resolve from Emotes.dbc (70 and 83), but these
|
||||
// serve as backup if Emotes.dbc failed to load.
|
||||
// /fart and /stink have EmoteRef=0 in EmotesText.dbc — no Emotes.dbc link.
|
||||
static const std::unordered_map<std::string, uint32_t> kAnimOverrides = {
|
||||
{"sleep", anim::EMOTE_SLEEP}, // 71 — stand-state emote
|
||||
{"laugh", anim::EMOTE_LAUGH}, // 70 — backup
|
||||
{"flirt", anim::EMOTE_SHY}, // 83 — DBC calls it SHY; it's the flirt animation
|
||||
{"fart", anim::EMOTE_TALK}, // 60 — generic gesture (WoW has no dedicated anim)
|
||||
{"stink", anim::EMOTE_TALK}, // 60 — generic gesture (WoW has no dedicated anim)
|
||||
};
|
||||
for (auto& [cmd, info] : emoteTable_) {
|
||||
if (info.animId == 0) {
|
||||
auto ov = kAnimOverrides.find(cmd);
|
||||
if (ov != kAnimOverrides.end()) {
|
||||
LOG_WARNING("Emotes: override /", cmd, " → animId=", ov->second);
|
||||
info.animId = ov->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (emoteTable_.empty()) {
|
||||
LOG_WARNING("Emotes: DBC loaded but no commands parsed, using fallback list");
|
||||
loadFallbackEmotes();
|
||||
} else {
|
||||
LOG_INFO("Emotes: loaded ", emoteTable_.size(), " commands from DBC");
|
||||
LOG_WARNING("Emotes: loaded ", emoteTable_.size(), " commands from DBC");
|
||||
}
|
||||
|
||||
buildDbcIdIndex();
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ void AnimationController::triggerMeleeSwing() {
|
|||
if (durationSec < 0.25f) durationSec = 0.25f;
|
||||
if (durationSec > 1.0f) durationSec = 1.0f;
|
||||
meleeSwingTimer_ = durationSec;
|
||||
|
||||
if (renderer_->getAudioCoordinator()->getActivitySoundManager()) {
|
||||
renderer_->getAudioCoordinator()->getActivitySoundManager()->playMeleeSwing();
|
||||
}
|
||||
|
|
@ -1040,9 +1041,16 @@ void AnimationController::updateCharacterAnimation() {
|
|||
auto* cameraController = renderer_->getCameraController();
|
||||
uint32_t characterInstanceId = renderer_->getCharacterInstanceId();
|
||||
|
||||
// Lazy probe: populate capability set once per model
|
||||
if (!capabilitiesProbed_ && characterRenderer && characterInstanceId != 0) {
|
||||
probeCapabilities();
|
||||
// Lazy probe: populate capability set once per model.
|
||||
// Re-probe if melee capabilities are missing (model may not have been fully
|
||||
// loaded on the first probe attempt).
|
||||
if (characterRenderer && characterInstanceId != 0) {
|
||||
if (!capabilitiesProbed_) {
|
||||
probeCapabilities();
|
||||
} else if (meleeSwingTimer_ > 0.0f && !characterAnimator_.getCapabilities().hasMelee) {
|
||||
capabilitiesProbed_ = false;
|
||||
probeCapabilities();
|
||||
}
|
||||
}
|
||||
|
||||
// When mounted, delegate to MountFSM and handle positioning
|
||||
|
|
|
|||
|
|
@ -1418,6 +1418,17 @@ void WMORenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
// Portal-based visibility — reuse member scratch buffers (avoid per-frame alloc)
|
||||
portalVisibleGroups_.clear();
|
||||
bool usePortalCulling = doPortalCull && !model.portals.empty() && !model.portalRefs.empty();
|
||||
if (usePortalCulling) {
|
||||
// If the actual camera is outside all groups, skip portal culling.
|
||||
// The character position (portalViewerPos) may fall inside a group's
|
||||
// loose AABB while visually outside the WMO, causing the BFS to start
|
||||
// from an interior group whose portals aren't in the frustum — hiding
|
||||
// the entire WMO.
|
||||
glm::vec3 localRealCam = glm::vec3(instance.invModelMatrix * glm::vec4(camPos, 1.0f));
|
||||
if (findContainingGroup(model, localRealCam) < 0) {
|
||||
usePortalCulling = false;
|
||||
}
|
||||
}
|
||||
if (usePortalCulling) {
|
||||
portalVisibleGroupSet_.clear();
|
||||
glm::vec4 localCamPos = instance.invModelMatrix * glm::vec4(portalViewerPos, 1.0f);
|
||||
|
|
@ -2135,6 +2146,23 @@ void WMORenderer::getVisibleGroupsViaPortals(const ModelData& model,
|
|||
}
|
||||
return;
|
||||
}
|
||||
// Best-fit group is indoor-only, but the position might also be inside an
|
||||
// outdoor group's AABB (e.g., standing on a street near a building whose
|
||||
// indoor AABB extends outward). If any outdoor group also contains the
|
||||
// position, treat this as an outdoor location and show all groups.
|
||||
for (size_t gi = 0; gi < model.groups.size(); gi++) {
|
||||
if (static_cast<int>(gi) == cameraGroup) continue;
|
||||
const auto& g = model.groups[gi];
|
||||
if (!(g.groupFlags & WMO_GROUP_FLAG_OUTDOOR)) continue;
|
||||
if (cameraLocalPos.x >= g.boundingBoxMin.x && cameraLocalPos.x <= g.boundingBoxMax.x &&
|
||||
cameraLocalPos.y >= g.boundingBoxMin.y && cameraLocalPos.y <= g.boundingBoxMax.y &&
|
||||
cameraLocalPos.z >= g.boundingBoxMin.z && cameraLocalPos.z <= g.boundingBoxMax.z) {
|
||||
for (size_t gj = 0; gj < model.groups.size(); gj++) {
|
||||
outVisibleGroups.insert(static_cast<uint32_t>(gj));
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the camera group has no portal refs, it's a dead-end group (utility/transition group).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue