feat: track shapeshift form and fire UPDATE_SHAPESHIFT_FORM events

Add UNIT_FIELD_BYTES_1 to all expansion update field tables (Classic=133,
TBC/WotLK=137). Byte 3 of this field contains the shapeshift form ID
(Bear=1, Cat=3, Travel=4, Moonkin=31, Tree=36, Battle Stance=17, etc.).

Track form changes in the VALUES update handler and fire
UPDATE_SHAPESHIFT_FORM + UPDATE_SHAPESHIFT_FORMS events when the
form changes. This enables stance bar addons and druid form tracking.

New Lua functions:
- GetShapeshiftForm() — returns current form ID (0 = no form)
- GetNumShapeshiftForms() — returns form count by class (Warrior=3,
  Druid=6, DK=3, Rogue=1, Priest=1, Paladin=3)
This commit is contained in:
Kelsi 2026-03-22 22:12:17 -07:00
parent b9a1b0244b
commit 587c0ef60d
8 changed files with 194 additions and 147 deletions

View file

@ -1,48 +1,49 @@
{
"CONTAINER_FIELD_NUM_SLOTS": 48,
"CONTAINER_FIELD_SLOT_1": 50,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_DURABILITY": 48,
"ITEM_FIELD_MAXDURABILITY": 49,
"ITEM_FIELD_STACK_COUNT": 14,
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_FIELD_TARGET_HI": 17,
"PLAYER_BYTES": 191,
"PLAYER_BYTES_2": 192,
"PLAYER_END": 1282,
"PLAYER_EXPLORED_ZONES_START": 1111,
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
"PLAYER_FIELD_BANK_SLOT_1": 564,
"PLAYER_FIELD_COINAGE": 1176,
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
"PLAYER_FIELD_PACK_SLOT_1": 532,
"PLAYER_FLAGS": 190,
"PLAYER_NEXT_LEVEL_XP": 717,
"PLAYER_QUEST_LOG_START": 198,
"PLAYER_REST_STATE_EXPERIENCE": 1175,
"PLAYER_SKILL_INFO_START": 718,
"PLAYER_XP": 716,
"UNIT_DYNAMIC_FLAGS": 143,
"UNIT_END": 188,
"UNIT_FIELD_AURAFLAGS": 98,
"UNIT_FIELD_AURAS": 50,
"UNIT_FIELD_BYTES_0": 36,
"UNIT_FIELD_HEALTH": 22,
"UNIT_FIELD_POWER1": 23,
"UNIT_FIELD_MAXHEALTH": 28,
"UNIT_FIELD_MAXPOWER1": 29,
"UNIT_FIELD_LEVEL": 34,
"UNIT_FIELD_BYTES_1": 133,
"UNIT_FIELD_DISPLAYID": 131,
"UNIT_FIELD_FACTIONTEMPLATE": 35,
"UNIT_FIELD_FLAGS": 46,
"UNIT_FIELD_DISPLAYID": 131,
"UNIT_FIELD_HEALTH": 22,
"UNIT_FIELD_LEVEL": 34,
"UNIT_FIELD_MAXHEALTH": 28,
"UNIT_FIELD_MAXPOWER1": 29,
"UNIT_FIELD_MOUNTDISPLAYID": 133,
"UNIT_FIELD_AURAS": 50,
"UNIT_FIELD_AURAFLAGS": 98,
"UNIT_NPC_FLAGS": 147,
"UNIT_DYNAMIC_FLAGS": 143,
"UNIT_FIELD_POWER1": 23,
"UNIT_FIELD_RESISTANCES": 154,
"UNIT_FIELD_STAT0": 138,
"UNIT_FIELD_STAT1": 139,
"UNIT_FIELD_STAT2": 140,
"UNIT_FIELD_STAT3": 141,
"UNIT_FIELD_STAT4": 142,
"UNIT_END": 188,
"PLAYER_FLAGS": 190,
"PLAYER_BYTES": 191,
"PLAYER_BYTES_2": 192,
"PLAYER_XP": 716,
"PLAYER_NEXT_LEVEL_XP": 717,
"PLAYER_REST_STATE_EXPERIENCE": 1175,
"PLAYER_FIELD_COINAGE": 1176,
"PLAYER_QUEST_LOG_START": 198,
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
"PLAYER_FIELD_PACK_SLOT_1": 532,
"PLAYER_FIELD_BANK_SLOT_1": 564,
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
"PLAYER_SKILL_INFO_START": 718,
"PLAYER_EXPLORED_ZONES_START": 1111,
"PLAYER_END": 1282,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 48,
"ITEM_FIELD_MAXDURABILITY": 49,
"CONTAINER_FIELD_NUM_SLOTS": 48,
"CONTAINER_FIELD_SLOT_1": 50
"UNIT_FIELD_TARGET_HI": 17,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_NPC_FLAGS": 147
}

View file

@ -1,48 +1,49 @@
{
"CONTAINER_FIELD_NUM_SLOTS": 64,
"CONTAINER_FIELD_SLOT_1": 66,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_DURABILITY": 60,
"ITEM_FIELD_MAXDURABILITY": 61,
"ITEM_FIELD_STACK_COUNT": 14,
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_FIELD_TARGET_HI": 17,
"PLAYER_BYTES": 237,
"PLAYER_BYTES_2": 238,
"PLAYER_EXPLORED_ZONES_START": 1312,
"PLAYER_FIELD_ARENA_CURRENCY": 1506,
"PLAYER_FIELD_BANKBAG_SLOT_1": 784,
"PLAYER_FIELD_BANK_SLOT_1": 728,
"PLAYER_FIELD_COINAGE": 1441,
"PLAYER_FIELD_HONOR_CURRENCY": 1505,
"PLAYER_FIELD_INV_SLOT_HEAD": 650,
"PLAYER_FIELD_PACK_SLOT_1": 696,
"PLAYER_FLAGS": 236,
"PLAYER_NEXT_LEVEL_XP": 927,
"PLAYER_QUEST_LOG_START": 244,
"PLAYER_REST_STATE_EXPERIENCE": 1440,
"PLAYER_SKILL_INFO_START": 928,
"PLAYER_XP": 926,
"UNIT_DYNAMIC_FLAGS": 164,
"UNIT_END": 234,
"UNIT_FIELD_BYTES_0": 36,
"UNIT_FIELD_HEALTH": 22,
"UNIT_FIELD_POWER1": 23,
"UNIT_FIELD_MAXHEALTH": 28,
"UNIT_FIELD_MAXPOWER1": 29,
"UNIT_FIELD_LEVEL": 34,
"UNIT_FIELD_BYTES_1": 137,
"UNIT_FIELD_DISPLAYID": 152,
"UNIT_FIELD_FACTIONTEMPLATE": 35,
"UNIT_FIELD_FLAGS": 46,
"UNIT_FIELD_FLAGS_2": 47,
"UNIT_FIELD_DISPLAYID": 152,
"UNIT_FIELD_HEALTH": 22,
"UNIT_FIELD_LEVEL": 34,
"UNIT_FIELD_MAXHEALTH": 28,
"UNIT_FIELD_MAXPOWER1": 29,
"UNIT_FIELD_MOUNTDISPLAYID": 154,
"UNIT_NPC_FLAGS": 168,
"UNIT_DYNAMIC_FLAGS": 164,
"UNIT_FIELD_POWER1": 23,
"UNIT_FIELD_RESISTANCES": 185,
"UNIT_FIELD_STAT0": 159,
"UNIT_FIELD_STAT1": 160,
"UNIT_FIELD_STAT2": 161,
"UNIT_FIELD_STAT3": 162,
"UNIT_FIELD_STAT4": 163,
"UNIT_END": 234,
"PLAYER_FLAGS": 236,
"PLAYER_BYTES": 237,
"PLAYER_BYTES_2": 238,
"PLAYER_XP": 926,
"PLAYER_NEXT_LEVEL_XP": 927,
"PLAYER_REST_STATE_EXPERIENCE": 1440,
"PLAYER_FIELD_COINAGE": 1441,
"PLAYER_QUEST_LOG_START": 244,
"PLAYER_FIELD_INV_SLOT_HEAD": 650,
"PLAYER_FIELD_PACK_SLOT_1": 696,
"PLAYER_FIELD_BANK_SLOT_1": 728,
"PLAYER_FIELD_BANKBAG_SLOT_1": 784,
"PLAYER_SKILL_INFO_START": 928,
"PLAYER_EXPLORED_ZONES_START": 1312,
"PLAYER_FIELD_HONOR_CURRENCY": 1505,
"PLAYER_FIELD_ARENA_CURRENCY": 1506,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 60,
"ITEM_FIELD_MAXDURABILITY": 61,
"CONTAINER_FIELD_NUM_SLOTS": 64,
"CONTAINER_FIELD_SLOT_1": 66
"UNIT_FIELD_TARGET_HI": 17,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_NPC_FLAGS": 168
}

View file

@ -1,48 +1,49 @@
{
"CONTAINER_FIELD_NUM_SLOTS": 48,
"CONTAINER_FIELD_SLOT_1": 50,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_DURABILITY": 48,
"ITEM_FIELD_MAXDURABILITY": 49,
"ITEM_FIELD_STACK_COUNT": 14,
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_FIELD_TARGET_HI": 17,
"PLAYER_BYTES": 191,
"PLAYER_BYTES_2": 192,
"PLAYER_END": 1282,
"PLAYER_EXPLORED_ZONES_START": 1111,
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
"PLAYER_FIELD_BANK_SLOT_1": 564,
"PLAYER_FIELD_COINAGE": 1176,
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
"PLAYER_FIELD_PACK_SLOT_1": 532,
"PLAYER_FLAGS": 190,
"PLAYER_NEXT_LEVEL_XP": 717,
"PLAYER_QUEST_LOG_START": 198,
"PLAYER_REST_STATE_EXPERIENCE": 1175,
"PLAYER_SKILL_INFO_START": 718,
"PLAYER_XP": 716,
"UNIT_DYNAMIC_FLAGS": 143,
"UNIT_END": 188,
"UNIT_FIELD_AURAFLAGS": 98,
"UNIT_FIELD_AURAS": 50,
"UNIT_FIELD_BYTES_0": 36,
"UNIT_FIELD_HEALTH": 22,
"UNIT_FIELD_POWER1": 23,
"UNIT_FIELD_MAXHEALTH": 28,
"UNIT_FIELD_MAXPOWER1": 29,
"UNIT_FIELD_LEVEL": 34,
"UNIT_FIELD_BYTES_1": 133,
"UNIT_FIELD_DISPLAYID": 131,
"UNIT_FIELD_FACTIONTEMPLATE": 35,
"UNIT_FIELD_FLAGS": 46,
"UNIT_FIELD_DISPLAYID": 131,
"UNIT_FIELD_HEALTH": 22,
"UNIT_FIELD_LEVEL": 34,
"UNIT_FIELD_MAXHEALTH": 28,
"UNIT_FIELD_MAXPOWER1": 29,
"UNIT_FIELD_MOUNTDISPLAYID": 133,
"UNIT_FIELD_AURAS": 50,
"UNIT_FIELD_AURAFLAGS": 98,
"UNIT_NPC_FLAGS": 147,
"UNIT_DYNAMIC_FLAGS": 143,
"UNIT_FIELD_POWER1": 23,
"UNIT_FIELD_RESISTANCES": 154,
"UNIT_FIELD_STAT0": 138,
"UNIT_FIELD_STAT1": 139,
"UNIT_FIELD_STAT2": 140,
"UNIT_FIELD_STAT3": 141,
"UNIT_FIELD_STAT4": 142,
"UNIT_END": 188,
"PLAYER_FLAGS": 190,
"PLAYER_BYTES": 191,
"PLAYER_BYTES_2": 192,
"PLAYER_XP": 716,
"PLAYER_NEXT_LEVEL_XP": 717,
"PLAYER_REST_STATE_EXPERIENCE": 1175,
"PLAYER_FIELD_COINAGE": 1176,
"PLAYER_QUEST_LOG_START": 198,
"PLAYER_FIELD_INV_SLOT_HEAD": 486,
"PLAYER_FIELD_PACK_SLOT_1": 532,
"PLAYER_FIELD_BANK_SLOT_1": 564,
"PLAYER_FIELD_BANKBAG_SLOT_1": 612,
"PLAYER_SKILL_INFO_START": 718,
"PLAYER_EXPLORED_ZONES_START": 1111,
"PLAYER_END": 1282,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 48,
"ITEM_FIELD_MAXDURABILITY": 49,
"CONTAINER_FIELD_NUM_SLOTS": 48,
"CONTAINER_FIELD_SLOT_1": 50
}
"UNIT_FIELD_TARGET_HI": 17,
"UNIT_FIELD_TARGET_LO": 16,
"UNIT_NPC_FLAGS": 147
}

View file

@ -1,60 +1,61 @@
{
"CONTAINER_FIELD_NUM_SLOTS": 64,
"CONTAINER_FIELD_SLOT_1": 66,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_DURABILITY": 60,
"ITEM_FIELD_MAXDURABILITY": 61,
"ITEM_FIELD_STACK_COUNT": 14,
"OBJECT_FIELD_ENTRY": 3,
"OBJECT_FIELD_SCALE_X": 4,
"UNIT_FIELD_TARGET_LO": 6,
"UNIT_FIELD_TARGET_HI": 7,
"PLAYER_BLOCK_PERCENTAGE": 1024,
"PLAYER_BYTES": 153,
"PLAYER_BYTES_2": 154,
"PLAYER_CHOSEN_TITLE": 1349,
"PLAYER_CRIT_PERCENTAGE": 1029,
"PLAYER_DODGE_PERCENTAGE": 1025,
"PLAYER_EXPLORED_ZONES_START": 1041,
"PLAYER_FIELD_ARENA_CURRENCY": 1423,
"PLAYER_FIELD_BANKBAG_SLOT_1": 458,
"PLAYER_FIELD_BANK_SLOT_1": 402,
"PLAYER_FIELD_COINAGE": 1170,
"PLAYER_FIELD_COMBAT_RATING_1": 1231,
"PLAYER_FIELD_HONOR_CURRENCY": 1422,
"PLAYER_FIELD_INV_SLOT_HEAD": 324,
"PLAYER_FIELD_MOD_DAMAGE_DONE_POS": 1171,
"PLAYER_FIELD_MOD_HEALING_DONE_POS": 1192,
"PLAYER_FIELD_PACK_SLOT_1": 370,
"PLAYER_FLAGS": 150,
"PLAYER_NEXT_LEVEL_XP": 635,
"PLAYER_PARRY_PERCENTAGE": 1026,
"PLAYER_QUEST_LOG_START": 158,
"PLAYER_RANGED_CRIT_PERCENTAGE": 1030,
"PLAYER_REST_STATE_EXPERIENCE": 1169,
"PLAYER_SKILL_INFO_START": 636,
"PLAYER_SPELL_CRIT_PERCENTAGE1": 1032,
"PLAYER_XP": 634,
"UNIT_DYNAMIC_FLAGS": 147,
"UNIT_END": 148,
"UNIT_FIELD_ATTACK_POWER": 123,
"UNIT_FIELD_BYTES_0": 23,
"UNIT_FIELD_HEALTH": 24,
"UNIT_FIELD_POWER1": 25,
"UNIT_FIELD_MAXHEALTH": 32,
"UNIT_FIELD_MAXPOWER1": 33,
"UNIT_FIELD_LEVEL": 54,
"UNIT_FIELD_BYTES_1": 137,
"UNIT_FIELD_DISPLAYID": 67,
"UNIT_FIELD_FACTIONTEMPLATE": 55,
"UNIT_FIELD_FLAGS": 59,
"UNIT_FIELD_FLAGS_2": 60,
"UNIT_FIELD_DISPLAYID": 67,
"UNIT_FIELD_HEALTH": 24,
"UNIT_FIELD_LEVEL": 54,
"UNIT_FIELD_MAXHEALTH": 32,
"UNIT_FIELD_MAXPOWER1": 33,
"UNIT_FIELD_MOUNTDISPLAYID": 69,
"UNIT_NPC_FLAGS": 82,
"UNIT_DYNAMIC_FLAGS": 147,
"UNIT_FIELD_POWER1": 25,
"UNIT_FIELD_RANGED_ATTACK_POWER": 126,
"UNIT_FIELD_RESISTANCES": 99,
"UNIT_FIELD_STAT0": 84,
"UNIT_FIELD_STAT1": 85,
"UNIT_FIELD_STAT2": 86,
"UNIT_FIELD_STAT3": 87,
"UNIT_FIELD_STAT4": 88,
"UNIT_END": 148,
"UNIT_FIELD_ATTACK_POWER": 123,
"UNIT_FIELD_RANGED_ATTACK_POWER": 126,
"PLAYER_FLAGS": 150,
"PLAYER_BYTES": 153,
"PLAYER_BYTES_2": 154,
"PLAYER_XP": 634,
"PLAYER_NEXT_LEVEL_XP": 635,
"PLAYER_REST_STATE_EXPERIENCE": 1169,
"PLAYER_FIELD_COINAGE": 1170,
"PLAYER_QUEST_LOG_START": 158,
"PLAYER_FIELD_INV_SLOT_HEAD": 324,
"PLAYER_FIELD_PACK_SLOT_1": 370,
"PLAYER_FIELD_BANK_SLOT_1": 402,
"PLAYER_FIELD_BANKBAG_SLOT_1": 458,
"PLAYER_SKILL_INFO_START": 636,
"PLAYER_EXPLORED_ZONES_START": 1041,
"PLAYER_CHOSEN_TITLE": 1349,
"PLAYER_FIELD_MOD_DAMAGE_DONE_POS": 1171,
"PLAYER_FIELD_MOD_HEALING_DONE_POS": 1192,
"PLAYER_BLOCK_PERCENTAGE": 1024,
"PLAYER_DODGE_PERCENTAGE": 1025,
"PLAYER_PARRY_PERCENTAGE": 1026,
"PLAYER_CRIT_PERCENTAGE": 1029,
"PLAYER_RANGED_CRIT_PERCENTAGE": 1030,
"PLAYER_SPELL_CRIT_PERCENTAGE1": 1032,
"PLAYER_FIELD_COMBAT_RATING_1": 1231,
"PLAYER_FIELD_HONOR_CURRENCY": 1422,
"PLAYER_FIELD_ARENA_CURRENCY": 1423,
"GAMEOBJECT_DISPLAYID": 8,
"ITEM_FIELD_STACK_COUNT": 14,
"ITEM_FIELD_DURABILITY": 60,
"ITEM_FIELD_MAXDURABILITY": 61,
"CONTAINER_FIELD_NUM_SLOTS": 64,
"CONTAINER_FIELD_SLOT_1": 66
"UNIT_FIELD_TARGET_HI": 7,
"UNIT_FIELD_TARGET_LO": 6,
"UNIT_NPC_FLAGS": 82
}

View file

@ -1722,6 +1722,7 @@ public:
// Combo points
uint8_t getComboPoints() const { return comboPoints_; }
uint8_t getShapeshiftFormId() const { return shapeshiftFormId_; }
uint64_t getComboTarget() const { return comboTarget_; }
// Death Knight rune state (6 runes: 0-1=Blood, 2-3=Unholy, 4-5=Frost; may become Death=3)
@ -2995,6 +2996,8 @@ private:
// Mirror timers (0=fatigue, 1=breath, 2=feigndeath)
MirrorTimer mirrorTimers_[3];
// Shapeshift form (from UNIT_FIELD_BYTES_1 byte 3)
uint8_t shapeshiftFormId_ = 0;
// Combo points (rogues/druids)
uint8_t comboPoints_ = 0;
uint64_t comboTarget_ = 0;

View file

@ -20,6 +20,7 @@ enum class UF : uint16_t {
UNIT_FIELD_TARGET_LO,
UNIT_FIELD_TARGET_HI,
UNIT_FIELD_BYTES_0,
UNIT_FIELD_BYTES_1, // byte3 = shapeshift form ID
UNIT_FIELD_HEALTH,
UNIT_FIELD_POWER1,
UNIT_FIELD_MAXHEALTH,

View file

@ -5013,6 +5013,33 @@ void LuaEngine::registerCoreAPI() {
{"IsInRaid", lua_IsInRaid},
{"GetPlayerMapPosition", lua_GetPlayerMapPosition},
{"GetPlayerFacing", lua_GetPlayerFacing},
{"GetShapeshiftForm", [](lua_State* L) -> int {
auto* gh = getGameHandler(L);
lua_pushnumber(L, gh ? gh->getShapeshiftFormId() : 0);
return 1;
}},
{"GetNumShapeshiftForms", [](lua_State* L) -> int {
// Return count based on player class
auto* gh = getGameHandler(L);
if (!gh) { lua_pushnumber(L, 0); return 1; }
uint8_t classId = gh->getPlayerClass();
// Druid: Bear(1), Aquatic(2), Cat(3), Travel(4), Moonkin/Tree(5/6)
// Warrior: Battle(1), Defensive(2), Berserker(3)
// Rogue: Stealth(1)
// Priest: Shadowform(1)
// Paladin: varies by level/talents
// DK: Blood Presence, Frost, Unholy (3)
switch (classId) {
case 1: lua_pushnumber(L, 3); break; // Warrior
case 2: lua_pushnumber(L, 3); break; // Paladin (auras)
case 4: lua_pushnumber(L, 1); break; // Rogue
case 5: lua_pushnumber(L, 1); break; // Priest
case 6: lua_pushnumber(L, 3); break; // Death Knight
case 11: lua_pushnumber(L, 6); break; // Druid
default: lua_pushnumber(L, 0); break;
}
return 1;
}},
{"GetMaxPlayerLevel", [](lua_State* L) -> int {
auto* reg = core::Application::getInstance().getExpansionRegistry();
auto* prof = reg ? reg->getActive() : nullptr;

View file

@ -12500,6 +12500,7 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem
const uint16_t ufMountDisplayId = fieldIndex(UF::UNIT_FIELD_MOUNTDISPLAYID);
const uint16_t ufNpcFlags = fieldIndex(UF::UNIT_NPC_FLAGS);
const uint16_t ufBytes0 = fieldIndex(UF::UNIT_FIELD_BYTES_0);
const uint16_t ufBytes1 = fieldIndex(UF::UNIT_FIELD_BYTES_1);
for (const auto& [key, val] : block.fields) {
if (key == ufHealth) {
uint32_t oldHealth = unit->getHealth();
@ -12569,6 +12570,17 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem
addonEventCallback_("UNIT_DISPLAYPOWER", {uid});
}
} else if (key == ufFlags) { unit->setUnitFlags(val); }
else if (ufBytes1 != 0xFFFF && key == ufBytes1 && block.guid == playerGuid) {
uint8_t newForm = static_cast<uint8_t>((val >> 24) & 0xFF);
if (newForm != shapeshiftFormId_) {
shapeshiftFormId_ = newForm;
LOG_INFO("Shapeshift form changed: ", (int)newForm);
if (addonEventCallback_) {
addonEventCallback_("UPDATE_SHAPESHIFT_FORM", {});
addonEventCallback_("UPDATE_SHAPESHIFT_FORMS", {});
}
}
}
else if (key == ufDynFlags) {
uint32_t oldDyn = unit->getDynamicFlags();
unit->setDynamicFlags(val);