Kelsidavis-WoWee/include/core/appearance_composer.hpp
Paul e58f9b4b40 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
2026-04-04 23:02:53 +03:00

102 lines
4.2 KiB
C++

#pragma once
#include "game/character.hpp"
#include <string>
#include <vector>
#include <unordered_set>
#include <cstdint>
namespace wowee {
namespace rendering { class Renderer; }
namespace pipeline { class AssetManager; class DBCLayout; struct M2Model; }
namespace game { class GameHandler; }
namespace core {
class EntitySpawner;
// Default (bare) geoset IDs per equipment group.
// Each group's base is groupNumber * 100; variant 01 is typically bare/default.
constexpr uint16_t kGeosetDefaultConnector = 101; // Group 1: default hair connector
constexpr uint16_t kGeosetBareForearms = 401; // Group 4: no gloves
constexpr uint16_t kGeosetBareShins = 503; // Group 5: no boots
constexpr uint16_t kGeosetDefaultEars = 702; // Group 7: ears
constexpr uint16_t kGeosetBareSleeves = 801; // Group 8: no chest armor sleeves
constexpr uint16_t kGeosetDefaultKneepads = 902; // Group 9: kneepads
constexpr uint16_t kGeosetDefaultTabard = 1201; // Group 12: tabard base
constexpr uint16_t kGeosetBarePants = 1301; // Group 13: no leggings
constexpr uint16_t kGeosetNoCape = 1501; // Group 15: no cape
constexpr uint16_t kGeosetWithCape = 1502; // Group 15: with cape
constexpr uint16_t kGeosetBareFeet = 2002; // Group 20: bare feet
/// Resolved texture paths from CharSections.dbc for player character compositing.
struct PlayerTextureInfo {
std::string bodySkinPath;
std::string faceLowerPath;
std::string faceUpperPath;
std::string hairTexturePath;
std::vector<std::string> underwearPaths;
};
/// Handles player character visual appearance: skin compositing, geoset selection,
/// texture path lookups, and equipment weapon rendering.
class AppearanceComposer {
public:
AppearanceComposer(rendering::Renderer* renderer,
pipeline::AssetManager* assetManager,
game::GameHandler* gameHandler,
pipeline::DBCLayout* dbcLayout,
EntitySpawner* entitySpawner);
// Player model path resolution
std::string getPlayerModelPath(game::Race race, game::Gender gender) const;
// Resolve texture paths from CharSections.dbc and fill model texture slots.
// Call BEFORE charRenderer->loadModel().
PlayerTextureInfo resolvePlayerTextures(pipeline::M2Model& model,
game::Race race, game::Gender gender,
uint32_t appearanceBytes);
// Apply composited textures to loaded model instance.
// Call AFTER charRenderer->loadModel(). Saves skin state for re-compositing.
void compositePlayerSkin(uint32_t modelSlotId, const PlayerTextureInfo& texInfo);
// Build default active geosets for player character
std::unordered_set<uint16_t> buildDefaultPlayerGeosets(uint8_t raceId, uint8_t sexId,
uint8_t hairStyleId, uint8_t facialId);
// Equipment weapon loading (reads inventory, attaches weapon M2 models)
void loadEquippedWeapons();
// Weapon sheathe state
void setWeaponsSheathed(bool sheathed) { weaponsSheathed_ = sheathed; }
bool isWeaponsSheathed() const { return weaponsSheathed_; }
void toggleWeaponsSheathed() { weaponsSheathed_ = !weaponsSheathed_; }
// Saved skin state accessors (used by game_screen.cpp for equipment re-compositing)
const std::string& getBodySkinPath() const { return bodySkinPath_; }
const std::vector<std::string>& getUnderwearPaths() const { return underwearPaths_; }
uint32_t getSkinTextureSlotIndex() const { return skinTextureSlotIndex_; }
uint32_t getCloakTextureSlotIndex() const { return cloakTextureSlotIndex_; }
private:
bool loadWeaponM2(const std::string& m2Path, pipeline::M2Model& outModel);
rendering::Renderer* renderer_;
pipeline::AssetManager* assetManager_;
game::GameHandler* gameHandler_;
pipeline::DBCLayout* dbcLayout_;
EntitySpawner* entitySpawner_;
// Saved at spawn for skin re-compositing on equipment changes
std::string bodySkinPath_;
std::vector<std::string> underwearPaths_;
uint32_t skinTextureSlotIndex_ = 0;
uint32_t cloakTextureSlotIndex_ = 0;
bool weaponsSheathed_ = false;
};
} // namespace core
} // namespace wowee