Fix spell targeting, item query parsing, loot UI, hair/skin textures, and attack animations

Fix spell cast target fallback using selected target instead of no-op tautology.
Fix SMSG_ITEM_QUERY_SINGLE_RESPONSE to always read 10 stat pairs (server sends
all 10 regardless of statsCount), fixing misaligned armor/stat reads. Fix XP gain
parser to read float groupRate + uint8 RAF instead of bogus uint32 groupBonus.
Add item icons and quality-colored names to loot window. Use actual character
appearance bytes for CharSections.dbc skin/face/hair lookups instead of hardcoded
defaults. Add weapon-type-aware attack animation selection (2H prioritizes anim 18).
Add readable spell cast failure messages and vendor sell hint.
This commit is contained in:
Kelsi 2026-02-06 15:41:29 -08:00
parent 29c1326845
commit deabe0bedd
9 changed files with 338 additions and 54 deletions

View file

@ -65,5 +65,173 @@ struct SpellCooldownEntry {
uint32_t categoryCooldownMs;
};
/**
* Get human-readable spell cast failure reason (WoW 3.3.5a SpellCastResult)
*/
inline const char* getSpellCastResultString(uint8_t result) {
switch (result) {
case 0: return "Spell failed";
case 1: return "Affects dead target";
case 2: return "Already at full health";
case 3: return "Already at full mana";
case 4: return "Already at full power";
case 5: return "Already being tamed";
case 6: return "Already have charm";
case 7: return "Already have summon";
case 8: return "Already open";
case 9: return "Aura bounced";
case 10: return "Autopilot in use";
case 11: return "Bad implicit targets";
case 12: return "Bad targets";
case 13: return "Can't be charmed";
case 14: return "Can't be disenchanted";
case 15: return "Can't be disenchanted (skill)";
case 16: return "Can't be milled";
case 17: return "Can't be prospected";
case 18: return "Can't cast on tapped";
case 19: return "Can't duel while invisible";
case 20: return "Can't duel while stealthed";
case 21: return "Can't stealth";
case 22: return "Caster aurastate";
case 23: return "Caster dead";
case 24: return "Charmed";
case 25: return "Chest in use";
case 26: return "Confused";
case 27: return "Don't report";
case 28: return "Equipped item";
case 29: return "Equipped item (class)";
case 30: return "Equipped item (class2)";
case 31: return "Equipped item (level)";
case 32: return "Error";
case 33: return "Fizzle";
case 34: return "Fleeing";
case 35: return "Food too low level";
case 36: return "Highlighted rune needed";
case 37: return "Immune";
case 38: return "Interrupted";
case 39: return "Interrupted (combat)";
case 40: return "Invalid item";
case 41: return "Item already enchanted";
case 42: return "Item gone";
case 43: return "Item not found";
case 44: return "Item not ready";
case 45: return "Level requirement";
case 46: return "Line of sight";
case 47: return "Lowlevel";
case 48: return "Low castlevel";
case 49: return "Mainhand empty";
case 50: return "Moving";
case 51: return "Must be behind target";
case 52: return "Need ammo";
case 53: return "Need ammo pouch";
case 54: return "Need exotic ammo";
case 55: return "Need more items";
case 56: return "No path";
case 57: return "Not behind";
case 58: return "Not fishable";
case 59: return "Not flying";
case 60: return "Not here";
case 61: return "Not infront";
case 62: return "Not in control";
case 63: return "Not known";
case 64: return "Not mounted";
case 65: return "Not on taxi";
case 66: return "Not on transport";
case 67: return "Not ready";
case 68: return "Not shapeshift";
case 69: return "Not standing";
case 70: return "Not tradeable";
case 71: return "Not trading";
case 72: return "Not unsheathed";
case 73: return "Not while ghost";
case 74: return "Not while looting";
case 75: return "No charges remain";
case 76: return "No champion";
case 77: return "No combo points";
case 78: return "No dueling";
case 79: return "No endurance";
case 80: return "No fish";
case 81: return "No items while shapeshifted";
case 82: return "No mounts allowed here";
case 83: return "No pet";
case 84: return "No power";
case 85: return "Nothing to dispel";
case 86: return "Nothing to steal";
case 87: return "Only above water";
case 88: return "Only daytime";
case 89: return "Only indoors";
case 90: return "Only mounted";
case 91: return "Only nighttime";
case 92: return "Only outdoors";
case 93: return "Only shapeshift";
case 94: return "Only stealthed";
case 95: return "Only underwater";
case 96: return "Out of range";
case 97: return "Pacified";
case 98: return "Possessed";
case 99: return "Reagents";
case 100: return "Requires area";
case 101: return "Requires spell focus";
case 102: return "Rooted";
case 103: return "Silenced";
case 104: return "Spell in progress";
case 105: return "Spell learned";
case 106: return "Spell unavailable";
case 107: return "Stunned";
case 108: return "Targets dead";
case 109: return "Target not dead";
case 110: return "Target not in party";
case 111: return "Target not in raid";
case 112: return "Target friendly";
case 113: return "Target is player";
case 114: return "Target is player controlled";
case 115: return "Target not dead";
case 116: return "Target not in party";
case 117: return "Target not player";
case 118: return "Target no pockets";
case 119: return "Target no weapons";
case 120: return "Target out of range";
case 121: return "Target unskinnable";
case 122: return "Thirst satiated";
case 123: return "Too close";
case 124: return "Too many of item";
case 125: return "Totem category";
case 126: return "Totems";
case 127: return "Training points";
case 128: return "Try again";
case 129: return "Unit not behind";
case 130: return "Unit not infront";
case 131: return "Wrong pet food";
case 132: return "Not while fatigued";
case 133: return "Target not in instance";
case 134: return "Not while trading";
case 135: return "Target not in raid";
case 136: return "Target feign dead";
case 137: return "Disabled by power scaling";
case 138: return "Quest players only";
case 139: return "Not idle";
case 140: return "Not inactive";
case 141: return "Partial playtime";
case 142: return "No playtime";
case 143: return "Not in battleground";
case 144: return "Not in raid instance";
case 145: return "Only in arena";
case 146: return "Target locked to raid instance";
case 147: return "On use enchant";
case 148: return "Not on ground";
case 149: return "Custom error";
case 150: return "Can't open lock";
case 151: return "Wrong artifact equipped";
case 173: return "Not enough mana";
case 174: return "Not enough health";
case 175: return "Not enough holy power";
case 176: return "Not enough rage";
case 177: return "Not enough energy";
case 178: return "Not enough runes";
case 179: return "Not enough runic power";
default: return nullptr;
}
}
} // namespace game
} // namespace wowee

View file

@ -122,6 +122,7 @@ public:
void setTargetPosition(const glm::vec3* pos);
bool isMoving() const;
void triggerMeleeSwing();
void setEquippedWeaponType(uint32_t inventoryType) { equippedWeaponInvType_ = inventoryType; meleeAnimId = 0; }
// Selection circle for targeted entity
void setSelectionCircle(const glm::vec3& pos, float radius, const glm::vec3& color);
@ -254,6 +255,7 @@ private:
float meleeSwingCooldown = 0.0f;
float meleeAnimDurationMs = 0.0f;
uint32_t meleeAnimId = 0;
uint32_t equippedWeaponInvType_ = 0;
bool terrainEnabled = true;
bool terrainLoaded = false;

View file

@ -76,7 +76,9 @@ private:
// Item icon cache: displayInfoId -> GL texture
std::unordered_map<uint32_t, GLuint> iconCache_;
public:
GLuint getItemIcon(uint32_t displayInfoId);
private:
// Character model preview
std::unique_ptr<rendering::CharacterPreview> charPreview_;