mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-27 05:23:51 +00:00
feat(animation): 452 named constants, 30-phase character animation state machine
Add animation_ids.hpp/cpp with all 452 WoW animation ID constants (anim::STAND, anim::RUN, anim::FIRE_BOW, ... anim::FLY_BACKWARDS, etc.), nameFromId() O(1) lookup, and flyVariant() compact 218-element ground→FLY_* resolver. Expand AnimationController into a full state machine with 20+ named states: spell cast (directed→omni→cast fallback chain, instant one-shot release), hit reactions (WOUND/CRIT/DODGE/BLOCK/SHIELD_BLOCK), stun, wounded idle, stealth animation substitution, loot, fishing channel, sit/sleep/kneel down→loop→up transitions, sheathe/unsheathe combat enter/exit, ranged weapons (BOW/GUN/CROSSBOW/THROWN with reload states), game object OPEN/CLOSE/DESTROY, vehicle enter/exit, mount flight directionals (FLY_LEFT/RIGHT/UP/DOWN/BACKWARDS), emote state variants, off-hand/pierce/dual-wield alternation, NPC birth/spawn/drown/rise, sprint aura override, totem idle, NPC greeting/farewell. Add spell_defines.hpp with SpellEffect (~45 constants) and SpellMissInfo (12 constants) namespaces; replace all magic numbers in spell_handler.cpp. Add GAMEOBJECT_BYTES_1 to update field table (all 4 expansion JSONs) and wire GameObjectStateCallback. Add DBC cross-validation on world entry. Expand tools/_ANIM_NAMES from ~35 to 452 entries in m2_viewer.py and asset_pipeline_gui.py. Add tests/test_animation_ids.cpp. Bug fixes included: - Stand state 1 was animating READY_2H(27) — fixed to SITTING(97) - Spell casts ended freeze-frame — add one-shot release animation - NPC 2H swing probe chain missing ATTACK_2H_LOOSE (polearm/staff) - Chair sits (states 2/4/5/6) incorrectly played floor-sit transition - STOP(3) used for all spell casts — replaced with model-aware chain
This commit is contained in:
parent
d54e262048
commit
e58f9b4b40
59 changed files with 3903 additions and 483 deletions
|
|
@ -542,6 +542,7 @@ EntityController::UnitFieldIndices EntityController::UnitFieldIndices::resolve()
|
|||
fieldIndex(UF::UNIT_FIELD_DISPLAYID),
|
||||
fieldIndex(UF::UNIT_FIELD_MOUNTDISPLAYID),
|
||||
fieldIndex(UF::UNIT_NPC_FLAGS),
|
||||
fieldIndex(UF::UNIT_NPC_EMOTESTATE),
|
||||
fieldIndex(UF::UNIT_FIELD_BYTES_0),
|
||||
fieldIndex(UF::UNIT_FIELD_BYTES_1)
|
||||
};
|
||||
|
|
@ -697,6 +698,7 @@ bool EntityController::applyUnitFieldsOnCreate(const UpdateBlock& block,
|
|||
}
|
||||
}
|
||||
else if (key == ufi.npcFlags) { unit->setNpcFlags(val); }
|
||||
else if (key == ufi.npcEmoteState) { unit->setNpcEmoteState(val); }
|
||||
else if (key == ufi.dynFlags) {
|
||||
unit->setDynamicFlags(val);
|
||||
if (block.objectType == ObjectType::UNIT &&
|
||||
|
|
@ -795,7 +797,28 @@ EntityController::UnitFieldUpdateResult EntityController::applyUnitFieldsOnUpdat
|
|||
if (!uid.empty())
|
||||
pendingEvents_.emit("UNIT_DISPLAYPOWER", {uid});
|
||||
}
|
||||
} else if (key == ufi.flags) { unit->setUnitFlags(val); }
|
||||
} else if (key == ufi.flags) {
|
||||
uint32_t oldFlags = unit->getUnitFlags();
|
||||
unit->setUnitFlags(val);
|
||||
// Detect stun state change on local player
|
||||
constexpr uint32_t UNIT_FLAG_STUNNED = 0x00040000;
|
||||
if (block.guid == owner_.playerGuid && owner_.stunStateCallback_) {
|
||||
bool wasStunned = (oldFlags & UNIT_FLAG_STUNNED) != 0;
|
||||
bool nowStunned = (val & UNIT_FLAG_STUNNED) != 0;
|
||||
if (wasStunned != nowStunned) {
|
||||
owner_.stunStateCallback_(nowStunned);
|
||||
}
|
||||
}
|
||||
// Detect stealth state change on local player
|
||||
constexpr uint32_t UNIT_FLAG_SNEAKING = 0x02000000;
|
||||
if (block.guid == owner_.playerGuid && owner_.stealthStateCallback_) {
|
||||
bool wasStealth = (oldFlags & UNIT_FLAG_SNEAKING) != 0;
|
||||
bool nowStealth = (val & UNIT_FLAG_SNEAKING) != 0;
|
||||
if (wasStealth != nowStealth) {
|
||||
owner_.stealthStateCallback_(nowStealth);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ufi.bytes1 != 0xFFFF && key == ufi.bytes1 && block.guid == owner_.playerGuid) {
|
||||
uint8_t newForm = static_cast<uint8_t>((val >> 24) & 0xFF);
|
||||
if (newForm != owner_.shapeshiftFormId_) {
|
||||
|
|
@ -863,6 +886,14 @@ EntityController::UnitFieldUpdateResult EntityController::applyUnitFieldsOnUpdat
|
|||
}
|
||||
unit->setMountDisplayId(val);
|
||||
} else if (key == ufi.npcFlags) { unit->setNpcFlags(val); }
|
||||
else if (key == ufi.npcEmoteState) {
|
||||
uint32_t oldEmote = unit->getNpcEmoteState();
|
||||
unit->setNpcEmoteState(val);
|
||||
// Fire emote animation callback so entity_spawner can update the NPC's idle anim
|
||||
if (val != oldEmote && owner_.emoteAnimCallback_) {
|
||||
owner_.emoteAnimCallback_(block.guid, val);
|
||||
}
|
||||
}
|
||||
// Power/maxpower range checks AFTER all specific fields
|
||||
else if (key >= ufi.powerBase && key < ufi.powerBase + 7) {
|
||||
unit->setPowerByType(static_cast<uint8_t>(key - ufi.powerBase), val);
|
||||
|
|
@ -889,6 +920,11 @@ EntityController::UnitFieldUpdateResult EntityController::applyUnitFieldsOnUpdat
|
|||
}
|
||||
}
|
||||
|
||||
// Fire player health callback for wounded-idle animation
|
||||
if (result.healthChanged && block.guid == owner_.playerGuid && owner_.playerHealthCallback_) {
|
||||
owner_.playerHealthCallback_(unit->getHealth(), unit->getMaxHealth());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1632,6 +1668,17 @@ void EntityController::onValuesUpdateGameObject(const UpdateBlock& block, std::s
|
|||
entity->getZ(), entity->getOrientation());
|
||||
}
|
||||
}
|
||||
|
||||
// Detect GO state changes from GAMEOBJECT_BYTES_1 (packed: byte0=state, byte1=type, byte2=artKit, byte3=animProgress)
|
||||
const uint16_t ufGoBytes1 = fieldIndex(UF::GAMEOBJECT_BYTES_1);
|
||||
if (ufGoBytes1 != 0xFFFF) {
|
||||
auto itB = block.fields.find(ufGoBytes1);
|
||||
if (itB != block.fields.end()) {
|
||||
uint8_t goState = static_cast<uint8_t>(itB->second & 0xFF);
|
||||
if (owner_.gameObjectStateCallback_)
|
||||
owner_.gameObjectStateCallback_(block.guid, goState);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue