feat(Core/Unit): New helper IsClass and script hook OnPlayerIsClass (#18243)

* Class Comparison Logic Encapsulation - Parity

* Add Context to IsClass

* Add Unit IsClass script hook

* Replace additional getClass with IsClass

* Update CanUseItem to replace getClass with IsClass

* Add separate context for pet vs ability

* Change Create to Init since not all referenced contexts are creation

* Align spacing in ClassContext

* Drop context on LFGManager max power

* Update IsClass context that wraps around Missle Barrage

* Rename context for swapping weapons

* Be more specific than CLASS_CONTEXT_TALENT

* Remove duplicate context

* Moved IsClass Hook to Player

* Removed unused parameter in virtual base function

* Added maybe_unused to IsClass virtual in order to compile

To match the override signature, the virtual base needs to include the parameter in question, so using [maybe_unused] to signal to the compiler to allow it

* Remove extra blank line

* Add ABILITY_REACTIVE context

* Add context for PET_CHARM

* Remove explicit nullopt check per review

* Code Readability - Change if to if else in pet

Due to the return pattern, this doesn't change functionality in any way

* Add OnPlayer to disambiguate

---------

Co-authored-by: NathanHandley <nathanhandley@protonmail.com>
This commit is contained in:
Nathan Handley 2024-02-10 09:25:00 -06:00 committed by GitHub
parent c47c945aa4
commit df33a57b78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
33 changed files with 344 additions and 280 deletions

View file

@ -133,8 +133,6 @@ void Player::SetSheath(SheathState sheathed)
uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) const
{
uint8 playerClass = getClass();
uint8 slots[4];
slots[0] = NULL_SLOT;
slots[1] = NULL_SLOT;
@ -246,23 +244,23 @@ uint8 Player::FindEquipSlot(ItemTemplate const* proto, uint32 slot, bool swap) c
switch (proto->SubClass)
{
case ITEM_SUBCLASS_ARMOR_LIBRAM:
if (playerClass == CLASS_PALADIN)
if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_RELIC))
slots[0] = EQUIPMENT_SLOT_RANGED;
break;
case ITEM_SUBCLASS_ARMOR_IDOL:
if (playerClass == CLASS_DRUID)
if (IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_RELIC))
slots[0] = EQUIPMENT_SLOT_RANGED;
break;
case ITEM_SUBCLASS_ARMOR_TOTEM:
if (playerClass == CLASS_SHAMAN)
if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_RELIC))
slots[0] = EQUIPMENT_SLOT_RANGED;
break;
case ITEM_SUBCLASS_ARMOR_MISC:
if (playerClass == CLASS_WARLOCK)
if (IsClass(CLASS_WARLOCK, CLASS_CONTEXT_EQUIP_RELIC))
slots[0] = EQUIPMENT_SLOT_RANGED;
break;
case ITEM_SUBCLASS_ARMOR_SIGIL:
if (playerClass == CLASS_DEATH_KNIGHT)
if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC))
slots[0] = EQUIPMENT_SLOT_RANGED;
break;
}
@ -2270,16 +2268,13 @@ InventoryResult Player::CanUseItem(Item* pItem, bool not_loading) const
// In fact it's a visual bug, everything works properly... I need sniffs of operations with
// binded to account items from off server.
switch (getClass())
if (IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_ARMOR_CLASS))
{
case CLASS_HUNTER:
case CLASS_SHAMAN:
allowEquip = (itemSkill == SKILL_MAIL);
break;
case CLASS_PALADIN:
case CLASS_WARRIOR:
allowEquip = (itemSkill == SKILL_PLATE_MAIL);
break;
allowEquip = (itemSkill == SKILL_PLATE_MAIL);
}
else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS))
{
allowEquip = (itemSkill == SKILL_MAIL);
}
}
if (!allowEquip && GetSkillValue(itemSkill) == 0)
@ -2394,39 +2389,40 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje
return EQUIP_ERR_CANT_EQUIP_SKILL;
}
uint8 _class = getClass();
if (proto->Class == ITEM_CLASS_WEAPON && GetSkillValue(item_weapon_skills[proto->SubClass]) == 0)
return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
if (proto->Class == ITEM_CLASS_ARMOR)
{
// Check for shields
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD && !(_class == CLASS_PALADIN || _class == CLASS_WARRIOR || _class == CLASS_SHAMAN))
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SHIELD && !(
IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_SHIELDS)
|| IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_SHIELDS)
|| IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_SHIELDS)))
{
return EQUIP_ERR_NO_REQUIRED_PROFICIENCY;
}
// Check for librams.
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_LIBRAM && _class != CLASS_PALADIN)
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_LIBRAM && !IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_RELIC))
{
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
}
// CHeck for idols.
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_IDOL && _class != CLASS_DRUID)
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_IDOL && !IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_RELIC))
{
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
}
// Check for totems.
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_TOTEM && _class != CLASS_SHAMAN)
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_TOTEM && !IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_RELIC))
{
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
}
// Check for sigils.
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SIGIL && _class != CLASS_DEATH_KNIGHT)
if (proto->SubClass == ITEM_SUBCLASS_ARMOR_SIGIL && !IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_RELIC))
{
return EQUIP_ERR_YOU_CAN_NEVER_USE_THAT_ITEM;
}
@ -2436,33 +2432,33 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje
proto->InventoryType != INVTYPE_CLOAK)
{
uint32 subclassToCompare = ITEM_SUBCLASS_ARMOR_CLOTH;
switch (_class)
if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS))
{
case CLASS_WARRIOR:
if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat())
{
return EQUIP_ERR_CANT_DO_RIGHT_NOW;
}
[[fallthrough]];
case CLASS_DEATH_KNIGHT:
case CLASS_PALADIN:
subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE;
break;
case CLASS_HUNTER:
case CLASS_SHAMAN:
subclassToCompare = ITEM_SUBCLASS_ARMOR_MAIL;
break;
case CLASS_ROGUE:
if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat())
{
return EQUIP_ERR_CANT_DO_RIGHT_NOW;
}
[[fallthrough]];
case CLASS_DRUID:
subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER;
break;
default:
break;
subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE;
}
else if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_EQUIP_ARMOR_CLASS))
{
if ((proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat()))
{
return EQUIP_ERR_CANT_DO_RIGHT_NOW;
}
subclassToCompare = ITEM_SUBCLASS_ARMOR_PLATE;
}
else if (IsClass(CLASS_HUNTER, CLASS_CONTEXT_EQUIP_ARMOR_CLASS) || IsClass(CLASS_SHAMAN, CLASS_CONTEXT_EQUIP_ARMOR_CLASS))
{
subclassToCompare = ITEM_SUBCLASS_ARMOR_MAIL;
}
else if (IsClass(CLASS_DRUID, CLASS_CONTEXT_EQUIP_ARMOR_CLASS))
{
subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER;
}
else if (IsClass(CLASS_ROGUE, CLASS_CONTEXT_EQUIP_ARMOR_CLASS))
{
if (proto->HasStat(ITEM_MOD_SPELL_POWER) || proto->HasSpellPowerStat())
{
return EQUIP_ERR_CANT_DO_RIGHT_NOW;
}
subclassToCompare = ITEM_SUBCLASS_ARMOR_LEATHER;
}
if (proto->SubClass > subclassToCompare)
@ -2772,7 +2768,7 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update)
if (pProto && IsInCombat() && (pProto->Class == ITEM_CLASS_WEAPON || pProto->InventoryType == INVTYPE_RELIC) && m_weaponChangeTimer == 0)
{
uint32 cooldownSpell = getClass() == CLASS_ROGUE ? 6123 : 6119;
uint32 cooldownSpell = IsClass(CLASS_ROGUE, CLASS_CONTEXT_WEAPON_SWAP) ? 6123 : 6119;
SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(cooldownSpell);
if (!spellProto)
@ -4645,7 +4641,7 @@ void Player::ApplyEnchantment(Item* item, EnchantmentSlot slot, bool apply, bool
}
case ITEM_ENCHANTMENT_TYPE_TOTEM: // Shaman Rockbiter Weapon
{
if (getClass() == CLASS_SHAMAN)
if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_ABILITY))
{
float addValue = 0.0f;
if (item->GetSlot() == EQUIPMENT_SLOT_MAINHAND)