your corpse is your own dead body, and particles take the room's light
- corpses render: a corpse create spawned a real entity, latched its guid for the reclaim send, and drew nothing — one missing EntityKind variant folded it into the catch-all and four downstream lanes then failed by default, not even the debug cube fired; the reference forks once, on the bones flag: a fresh body builds a character composite from the corpse's OWN appearance bytes and 19 item slots through the same compositor a living player is dressed by (its own hide-helm/hide-cloak bits honoured, the ranged slot never dresses, and weapons never dress because the client looks a packed display word up as a guid — we reproduce the outcome, not the dead lookup), while a bone pile takes its race and sex's DeathSkeleton, sixteen of which ship; the pose is one settled arm — Dead, or Drowned when the body lies more than 0.66666669 yd under a liquid surface — and a corpse is not a unit, so clicking one no longer sends a selection for a guid no unit owns
- a lit particle takes its MODEL's committed light node, not the sky's: indoors the reference lights an object's particles from the object's own node — the footprint's baked floor colours on the fixed interior axis plus the room's placed lights — never the day/night sun; a particle quad carries exactly one normal, world up, one constant per draw, so the whole node collapses to three floats, published off the same fold the model's mesh renders from so a mesh and its own particles can never disagree about the room they stand in; in Onyxia's Lair the 78 lit falling-rock emitters on the 26 anchors that classify interior now take the room's light
- and that lit lane is FIXED FUNCTION, not the mesh's soft SH curve: the shipped client null-binds both shader slots for a particle batch and re-zeroes the shader mask at the head of every batch, so a model and its own particles share the same committed light words and legitimately differ in the curve applied to them — the mesh's wrapping lobe against the hardware's max(N·L, 0) is 0.900 vs 0.869 on the key axis, up to the three nearest room lights arrive diffuse-only under GL's own 1/(0.7d + 0.03d²) falloff (a light at or below the emitter cannot reach it at all), and the clamp sits on the product, never the multiplier, because it is GL that clamps the lit vertex colour
- the cinematic letterbox has no aspect test in it: CinematicFrame.lua's if guards a recompute, not whether there are bars — below 4:3 the reference's bar textures simply stand at their declared 1024x128 on the client's native 1024x768 sheet, which is height/6, the same formula evaluated at exactly 4:3 — so one expression covers every window shape, min((h − min(w/2, h))/2, h/6): a 2:1 picture when wide, bars at 4:3 too; and a shown keyboard frame swallows EVERY key, not the ten names the host used to feed it — you could walk around underneath your own intro; the proof is the reference's own handler, which has to RunBinding("SCREENSHOT") by hand to get one key back; ESC still skips, and a W already held when the shot starts keeps running, which is faithful too
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,6 +35,11 @@ pub enum EntityKind {
|
||||
/// area effect hangs on (Blizzard's storm, Flamestrike's burn). Carries no display id; its
|
||||
/// visual resolves through the spell chain (`DYNAMICOBJECT_SPELLID`), not a model field.
|
||||
DynamicObject,
|
||||
/// A `TYPEID_CORPSE` (7) create — a dead player's remains: a fresh body while the owner is a
|
||||
/// ghost, or the **bone pile** it converts to once released/looted. It renders as the dead
|
||||
/// player (decision 1706): the character-model chain, dressed from the corpse's own
|
||||
/// `CORPSE_FIELD_BYTES_*` + `CORPSE_FIELD_ITEM` snapshot.
|
||||
Corpse,
|
||||
Other,
|
||||
}
|
||||
|
||||
|
||||
@@ -1081,22 +1081,38 @@ fn entity_kind(t: ObjectType) -> EntityKind {
|
||||
ObjectType::Unit => EntityKind::Unit,
|
||||
ObjectType::GameObject => EntityKind::GameObject,
|
||||
ObjectType::DynamicObject => EntityKind::DynamicObject,
|
||||
ObjectType::Corpse => EntityKind::Corpse,
|
||||
_ => EntityKind::Other,
|
||||
}
|
||||
}
|
||||
|
||||
/// The display id from a create packet — units, **players**, and GameObjects all carry one. A player's
|
||||
/// body model resolves through the same CreatureDisplayInfo→CreatureModelData chain as a creature's:
|
||||
/// the 1.12 client has no race/sex→path resolver, CGPlayer inherits CGUnit's displayId model-build
|
||||
/// (decision 0041), so we route `Player` alongside `Unit`. Cast from the wire `i32`; `0`/absent →
|
||||
/// `None`.
|
||||
/// The display id from a create packet — units, **players**, GameObjects **and corpses** all carry
|
||||
/// one. A player's body model resolves through the same CreatureDisplayInfo→CreatureModelData chain
|
||||
/// as a creature's: the 1.12 client has no race/sex→path resolver, CGPlayer inherits CGUnit's
|
||||
/// displayId model-build (decision 0041), so we route `Player` alongside `Unit`. A corpse's
|
||||
/// `CORPSE_FIELD_DISPLAY_ID` is the dead player's own body display (vmangos writes
|
||||
/// `GetNativeDisplayId()` straight in), so it resolves down that identical chain — the reference's
|
||||
/// `0x5d6700` does exactly this lookup (decision 1706).
|
||||
///
|
||||
/// **A bone pile's display id is deliberately still reported here.** The client ignores it (it
|
||||
/// builds `<Race><Sex>DeathSkeleton` from the BYTES instead), but that is a *model-resolution* law,
|
||||
/// not a wire fact — the field is present and it is what it is, and the fork lives where models are
|
||||
/// resolved (`entities::corpse`). Reporting `None` here would also blind the live-display differ to
|
||||
/// a flesh→bones flag flip, which the reference reacts to with a full model reload.
|
||||
///
|
||||
/// Cast from the wire `i32`; `0`/absent → `None`.
|
||||
fn display_id(t: ObjectType, mask: &ObjectFields) -> Option<u32> {
|
||||
let raw = match t {
|
||||
ObjectType::Unit | ObjectType::Player => mask.unit_displayid(),
|
||||
ObjectType::GameObject => mask.gameobject_displayid(),
|
||||
match t {
|
||||
ObjectType::Unit | ObjectType::Player => {
|
||||
mask.unit_displayid().filter(|&d| d > 0).map(|d| d as u32)
|
||||
}
|
||||
ObjectType::GameObject => mask
|
||||
.gameobject_displayid()
|
||||
.filter(|&d| d > 0)
|
||||
.map(|d| d as u32),
|
||||
ObjectType::Corpse => mask.corpse_display_id(),
|
||||
_ => None,
|
||||
}?;
|
||||
(raw > 0).then_some(raw as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/// The per-object scale (`OBJECT_FIELD_SCALE_X`) units/players/GameObjects carry — the *complete*
|
||||
@@ -1107,7 +1123,12 @@ fn display_id(t: ObjectType, mask: &ObjectFields) -> Option<u32> {
|
||||
/// sends a positive value on create).
|
||||
fn object_scale(t: ObjectType, mask: &ObjectFields) -> f32 {
|
||||
let raw = match t {
|
||||
ObjectType::Unit | ObjectType::Player | ObjectType::GameObject => mask.object_scale_x(),
|
||||
// A corpse joins them: `OBJECT_FIELD_SCALE_X` is an OBJECT-block field and vmangos sets it
|
||||
// (`Corpse::Create` → `SetObjectScale(DEFAULT_OBJECT_SCALE)`), so the same one-field law
|
||||
// sizes it (decision 1706).
|
||||
ObjectType::Unit | ObjectType::Player | ObjectType::GameObject | ObjectType::Corpse => {
|
||||
mask.object_scale_x()
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
raw.filter(|s| *s > 0.0).unwrap_or(1.0)
|
||||
|
||||
@@ -18,9 +18,9 @@ pub use events::{
|
||||
SessionEvent,
|
||||
};
|
||||
pub use messages::{
|
||||
CharCreateReq, CharEnumItem, Character, CreateSpline, ItemInfo, JumpInfo, MonsterMoveFacing,
|
||||
MoveMode, ObjectFields, OwnerFallback, ServerPacket, SpeedKind, TransportPose,
|
||||
CHARACTER_FLAG_GHOST, CHARACTER_FLAG_HIDE_CLOAK, CHARACTER_FLAG_HIDE_HELM,
|
||||
CharCreateReq, CharEnumItem, Character, CorpseLook, CreateSpline, ItemInfo, JumpInfo,
|
||||
MonsterMoveFacing, MoveMode, ObjectFields, OwnerFallback, ServerPacket, SpeedKind,
|
||||
TransportPose, CHARACTER_FLAG_GHOST, CHARACTER_FLAG_HIDE_CLOAK, CHARACTER_FLAG_HIDE_HELM,
|
||||
CHARACTER_FLAG_RENAME,
|
||||
};
|
||||
pub use world::{
|
||||
|
||||
@@ -229,10 +229,10 @@ pub use trade::{
|
||||
};
|
||||
pub use trainer::{train_fail, trainer_buy_spell, trainer_list, trainer_spell_state, TrainerSpell};
|
||||
pub use update_object::{
|
||||
quest_slot_state, CreateSpline, MovementBlock, Object, ObjectFields, ObjectType, OwnerFallback,
|
||||
PlayerSkillSlot, QuestLogSlot, UnitAuraSlot, AURA_FLAG_CANCELABLE, AURA_FLAG_EFF_INDEX_MASK,
|
||||
FIELD_PLAYER_SKILL_INFO_1_1, PLAYER_EXPLORED_ZONES_SLOTS, PLAYER_QUEST_LOG_SLOTS,
|
||||
PLAYER_SKILL_SLOTS, UNIT_AURA_POSITIVE_SLOTS, UNIT_AURA_SLOTS,
|
||||
quest_slot_state, CorpseLook, CreateSpline, MovementBlock, Object, ObjectFields, ObjectType,
|
||||
OwnerFallback, PlayerSkillSlot, QuestLogSlot, UnitAuraSlot, AURA_FLAG_CANCELABLE,
|
||||
AURA_FLAG_EFF_INDEX_MASK, FIELD_PLAYER_SKILL_INFO_1_1, PLAYER_EXPLORED_ZONES_SLOTS,
|
||||
PLAYER_QUEST_LOG_SLOTS, PLAYER_SKILL_SLOTS, UNIT_AURA_POSITIVE_SLOTS, UNIT_AURA_SLOTS,
|
||||
};
|
||||
pub use vendor::{
|
||||
buy_item, buy_result, buyback_item, list_inventory, repair_item, sell_item, sell_result,
|
||||
|
||||
@@ -594,6 +594,24 @@ pub struct PlayerSkillSlot {
|
||||
/// `None`. Conflating the two made every `player_*` accessor answer a confident `0` for any
|
||||
/// created creature, which is how the pet sheet's damage multiplier came out `0` instead of the
|
||||
/// unstreamed default `1.0` and the tooltip read `inf - inf` / `nan` (director, 2026-08-07).
|
||||
/// A corpse's body appearance — the seven `CORPSE_FIELD_BYTES_1`/`_2` bytes the reference's dress
|
||||
/// `0x5d6260` loads into the corpse's own `CCharacterComponent` (`+0x69..+0x6f`), which are the same
|
||||
/// seven selectors a living player's body is composited from. Read via
|
||||
/// [`ObjectFields::corpse_look`].
|
||||
///
|
||||
/// This is the corpse's OWN snapshot, taken at death: it is not the owner's live `PLAYER_BYTES`,
|
||||
/// and it survives the owner logging out or being nowhere near.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct CorpseLook {
|
||||
pub race: u8,
|
||||
pub sex: u8,
|
||||
pub skin: u8,
|
||||
pub face: u8,
|
||||
pub hair_style: u8,
|
||||
pub hair_color: u8,
|
||||
pub facial_hair: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ObjectFields {
|
||||
/// Presence bitmask, one bit per field index (`index = word * 32 + bit`) — the wire's block
|
||||
@@ -683,6 +701,101 @@ impl ObjectFields {
|
||||
self.get_guid(6).filter(|&g| g != 0)
|
||||
}
|
||||
|
||||
/// `CORPSE_FIELD_DISPLAY_ID` (field 12, `OBJECT_END + 0x6`): the dead player's body model, as a
|
||||
/// **`CreatureDisplayInfo` id** — vmangos writes the owner's `GetNativeDisplayId()` straight in
|
||||
/// (`Player::CreateCorpse`, `Player.cpp:4809`), so it resolves down the very same
|
||||
/// displayId → CreatureDisplayInfo → CreatureModelData chain a player's living body does
|
||||
/// (decision 0041). The reference reads exactly this field for the model: `0x5d6700`'s
|
||||
/// not-bones leg is `[[corpse+0x110]+0x18]` → `[0xc0de90]` (CreatureDisplayInfo) → `[row+4]`
|
||||
/// → `[0xc0de68]` (CreatureModelData) → `[row+8]` = the path.
|
||||
///
|
||||
/// **Only meaningful while [`Self::corpse_is_bones`] is false.** A bone pile carries the field
|
||||
/// (the server's corpse→bones conversion copies fields 3.. verbatim, vmangos `Map.cpp:3638`)
|
||||
/// and the client ignores it: `0x5d6700` branches on the BONES bit *first* and builds the
|
||||
/// skeleton path from race/sex instead.
|
||||
pub fn corpse_display_id(&self) -> Option<u32> {
|
||||
self.get_u32(12).filter(|&d| d != 0)
|
||||
}
|
||||
|
||||
/// One `CORPSE_FIELD_ITEM` slot (fields 13..31, `OBJECT_END + 0x7 + slot`, `slot` = the
|
||||
/// **equipment slot** 0..18): the worn piece as `(ItemDisplayInfo id, InventoryType)` — the
|
||||
/// server packs `DisplayInfoID | (InventoryType << 24)` (`Player::CreateCorpse`,
|
||||
/// `Player.cpp:4821`), and the client unpacks the same way (`0x5d6423 and eax,0xffffff`
|
||||
/// before the `[0xc0dc10]` ItemDisplayInfo index). `None` for an empty slot.
|
||||
///
|
||||
/// Note this is a **display id, already resolved** — unlike `PLAYER_VISIBLE_ITEM`, which
|
||||
/// carries item *entries* and needs a template round trip. A corpse dresses off the wire alone.
|
||||
pub fn corpse_item(&self, slot: u8) -> Option<(u32, u8)> {
|
||||
let raw = self.get_u32(13 + u16::from(slot))?;
|
||||
let display = raw & 0x00ff_ffff;
|
||||
(display != 0).then_some((display, (raw >> 24) as u8))
|
||||
}
|
||||
|
||||
/// `CORPSE_FIELD_BYTES_1` (field 32, `OBJECT_END + 0x1A`) — byte 1 **race**, byte 2 **gender**,
|
||||
/// byte 3 **skinColor** (byte 0 is unused; vmangos writes `(0) | (race<<8) | (gender<<16) |
|
||||
/// (skin<<24)`, `Corpse.cpp:228`). The reference reads them one byte at a time off the same
|
||||
/// word — `[[corpse+0x110]+0x69/+0x6a/+0x6b]` in the dress `0x5d6260`.
|
||||
fn corpse_bytes_1(&self) -> Option<u32> {
|
||||
self.get_u32(32)
|
||||
}
|
||||
/// `CORPSE_FIELD_BYTES_2` (field 33, `OBJECT_END + 0x1B`) — byte 0 **face**, byte 1
|
||||
/// **hairStyle**, byte 2 **hairColor**, byte 3 **facialHair** (`Corpse.cpp:229`; the reference's
|
||||
/// `+0x6c..+0x6f`).
|
||||
fn corpse_bytes_2(&self) -> Option<u32> {
|
||||
self.get_u32(33)
|
||||
}
|
||||
/// The corpse's body appearance — `(race, sex, skin, face, hairStyle, hairColor, facialHair)`,
|
||||
/// the seven bytes `0x5d6260` loads into the corpse's own `CCharacterComponent`. `None` when
|
||||
/// neither BYTES word is known (a bare `Values` delta).
|
||||
pub fn corpse_look(&self) -> Option<CorpseLook> {
|
||||
let (b1, b2) = (self.corpse_bytes_1()?, self.corpse_bytes_2()?);
|
||||
Some(CorpseLook {
|
||||
race: (b1 >> 8) as u8,
|
||||
sex: (b1 >> 16) as u8,
|
||||
skin: (b1 >> 24) as u8,
|
||||
face: b2 as u8,
|
||||
hair_style: (b2 >> 8) as u8,
|
||||
hair_color: (b2 >> 16) as u8,
|
||||
facial_hair: (b2 >> 24) as u8,
|
||||
})
|
||||
}
|
||||
|
||||
/// `CORPSE_FIELD_FLAGS` (field 35, `OBJECT_END + 0x1D`) — the corpse's own flag word, read by
|
||||
/// the reference as `[[corpse+0x110]+0x74]`. `0` when absent.
|
||||
pub fn corpse_flags(&self) -> u32 {
|
||||
self.get_u32(35).unwrap_or(0)
|
||||
}
|
||||
/// `CORPSE_FLAG_BONES` (`0x01`) — this object is a **bone pile**, not a fresh body. It is the
|
||||
/// first thing `0x5d6260` tests (`0x5d6287 test byte [eax+0x74],0x1; jne`): a bone pile builds
|
||||
/// **no** character component at all, wears nothing, and takes its model from race/sex rather
|
||||
/// than from [`Self::corpse_display_id`] (`0x5d6700`). Named in vmangos `Corpse.h:45`; set on
|
||||
/// the converted object at `Map.cpp:3644`.
|
||||
pub fn corpse_is_bones(&self) -> bool {
|
||||
self.corpse_flags() & 0x01 != 0
|
||||
}
|
||||
/// `CORPSE_FLAG_HIDE_HELM` (`0x08`) — the owner's helm preference, snapshotted at death
|
||||
/// (vmangos copies `PLAYER_FLAGS_HIDE_HELM` in, `Player.cpp:4801`). The corpse honours it by
|
||||
/// **never installing equipment slot 0** (`0x5d6465 test byte [edx+0x74],0x8` → the skip),
|
||||
/// which is its own bit on its own field — *not* `PLAYER_FLAGS`, and the opposite instruction
|
||||
/// polarity to the player lane (wow-re `helm-cloak-hide.md` §2b).
|
||||
pub fn corpse_hides_helm(&self) -> bool {
|
||||
self.corpse_flags() & 0x08 != 0
|
||||
}
|
||||
/// `CORPSE_FLAG_HIDE_CLOAK` (`0x10`) — the same, for equipment slot 14 (`0x5d6470`).
|
||||
pub fn corpse_hides_cloak(&self) -> bool {
|
||||
self.corpse_flags() & 0x10 != 0
|
||||
}
|
||||
|
||||
/// `CORPSE_FIELD_DYNAMIC_FLAGS` (field 36, `OBJECT_END + 0x1E`) bit 0 — this bone pile carries
|
||||
/// **lootable insignia** (`CORPSE_DYNFLAG_LOOTABLE`, vmangos `Map.cpp:3660`). The reference's
|
||||
/// `0x5d6e20` is literally `return *(+0x110)+0x78 & 1`, and it gates both the corpse highlight
|
||||
/// emitter (`0x5d6e30`/`0x5d6e80`, armed at build and re-armed by the field's own mirror
|
||||
/// handler) and the `CMSG_LOOT` route off the corpse's interact slot (wow-re
|
||||
/// `loot-anim-leg.md`).
|
||||
pub fn corpse_lootable(&self) -> bool {
|
||||
self.get_u32(36).unwrap_or(0) & 0x01 != 0
|
||||
}
|
||||
|
||||
/// `DYNAMICOBJECT_CASTER` (fields 6–7, `OBJECT_END + 0x0`, a 2-field guid — vmangos
|
||||
/// `UpdateFields_1_12_1.h:325`): the unit whose ground cast anchored this object.
|
||||
/// Index-twin of [`Self::corpse_owner`] — each TYPEID reuses `OBJECT_END`; the TypeId (or
|
||||
|
||||
@@ -579,6 +579,74 @@ fn created_store_reads_absent_fields_as_zero_a_delta_as_none() {
|
||||
assert_eq!(create.corpse_owner(), None);
|
||||
}
|
||||
|
||||
/// The `TYPEID_CORPSE` descriptor's field indices and its two packed words (decision 1706). Every
|
||||
/// number here is a *position*, and a position is exactly what a doc comment cannot check: an
|
||||
/// off-by-one on `CORPSE_FIELD_ITEM`'s base would read the display id as a shoulder and dress a
|
||||
/// body in a helm's row, silently.
|
||||
#[test]
|
||||
fn corpse_descriptor_indices_and_packing() {
|
||||
// `OBJECT_END + 0x6 = 12` display · `+0x7 = 13` item[0] · `+0x1A/0x1B = 32/33` bytes ·
|
||||
// `+0x1D = 35` flags · `+0x1E = 36` dynamic flags (vmangos `UpdateFields_1_12_1.h:338-350`).
|
||||
let corpse = ObjectFields::from_pairs(&[
|
||||
(12, 49),
|
||||
// Slot 4 (chest): DisplayInfoID 902 | InventoryType 5 << 24 (`Player.cpp:4821`).
|
||||
(13 + 4, 902 | (5 << 24)),
|
||||
// BYTES_1: (0) | race<<8 | gender<<16 | skin<<24 (`Corpse.cpp:228`).
|
||||
(32, (6 << 8) | (1 << 16) | (3 << 24)),
|
||||
// BYTES_2: face | hairstyle<<8 | haircolor<<16 | facialhair<<24 (`Corpse.cpp:229`).
|
||||
(33, 7 | (2 << 8) | (4 << 16) | (5 << 24)),
|
||||
(35, 0x04 | 0x08),
|
||||
(36, 0x01),
|
||||
])
|
||||
.into_created(ObjectType::Corpse);
|
||||
|
||||
assert_eq!(corpse.corpse_display_id(), Some(49));
|
||||
assert_eq!(
|
||||
corpse.corpse_item(4),
|
||||
Some((902, 5)),
|
||||
"display | invType<<24"
|
||||
);
|
||||
assert_eq!(
|
||||
corpse.corpse_item(3),
|
||||
None,
|
||||
"an empty slot is absent, not (0, 0)"
|
||||
);
|
||||
|
||||
let look = corpse.corpse_look().expect("both BYTES words present");
|
||||
assert_eq!(
|
||||
(
|
||||
look.race,
|
||||
look.sex,
|
||||
look.skin,
|
||||
look.face,
|
||||
look.hair_style,
|
||||
look.hair_color,
|
||||
look.facial_hair
|
||||
),
|
||||
(6, 1, 3, 7, 2, 4, 5),
|
||||
"the seven bytes the reference loads at [descr+0x69..+0x6f], in that order"
|
||||
);
|
||||
|
||||
// The flags are three independent bits on one field — and BONES is the model fork, so a
|
||||
// mis-read here swaps a dressed body for a skeleton.
|
||||
assert!(!corpse.corpse_is_bones(), "0x01 clear");
|
||||
assert!(corpse.corpse_hides_helm(), "0x08 set");
|
||||
assert!(!corpse.corpse_hides_cloak(), "0x10 clear");
|
||||
assert!(
|
||||
corpse.corpse_lootable(),
|
||||
"DYNAMIC_FLAGS bit 0 — a different field"
|
||||
);
|
||||
|
||||
// …and CORPSE_END = 38, so a created corpse answers 0 (not None) for every field below it.
|
||||
let bare = ObjectFields::from_pairs(&[(12, 49)]).into_created(ObjectType::Corpse);
|
||||
assert_eq!(bare.corpse_flags(), 0);
|
||||
assert!(
|
||||
bare.corpse_look().is_some(),
|
||||
"absent BYTES read as the zero default"
|
||||
);
|
||||
assert!(!bare.corpse_lootable());
|
||||
}
|
||||
|
||||
/// …and "absent = 0" stops at the end of the object's OWN descriptor (decision 1081). A creature
|
||||
/// has no PLAYER block to be absent from, so a PLAYER-block read off one is `None` — a question it
|
||||
/// cannot answer — not a confident `0`.
|
||||
|
||||
@@ -13,8 +13,8 @@ mod fields;
|
||||
mod movement;
|
||||
|
||||
pub use fields::{
|
||||
power_display_scale, quest_slot_state, ObjectFields, OwnerFallback, PlayerSkillSlot,
|
||||
QuestLogSlot, UnitAuraSlot, AURA_FLAG_CANCELABLE, AURA_FLAG_EFF_INDEX_MASK,
|
||||
power_display_scale, quest_slot_state, CorpseLook, ObjectFields, OwnerFallback,
|
||||
PlayerSkillSlot, QuestLogSlot, UnitAuraSlot, AURA_FLAG_CANCELABLE, AURA_FLAG_EFF_INDEX_MASK,
|
||||
FIELD_PLAYER_SKILL_INFO_1_1, PLAYER_EXPLORED_ZONES_SLOTS, PLAYER_QUEST_LOG_SLOTS,
|
||||
PLAYER_SKILL_SLOTS, UNIT_AURA_POSITIVE_SLOTS, UNIT_AURA_SLOTS,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user