fix(core): DK ToT off-hand strikes and Titan Grip for classless
- Item: 2H weapon subclass/inventory masks for strike spells (ToT / dual 2H). - Player: allow SPELL_EFFECT_TITAN_GRIP through skill validation; clear TG on talent/spec reset only for warriors. - PlayerStorage: sync m_canTitanGrip from spellbook before inventory load. - Spell: treat Thassarian off-hand strike spells as OFF_ATTACK when DBC omits SPELL_ATTR3_REQUIRES_OFF_HAND_WEAPON.
This commit is contained in:
@@ -898,7 +898,31 @@ bool Item::IsFitToSpellRequirements(SpellInfo const* spellInfo) const
|
|||||||
|
|
||||||
if (spellInfo->EquippedItemSubClassMask != 0) // 0 == any subclass
|
if (spellInfo->EquippedItemSubClassMask != 0) // 0 == any subclass
|
||||||
{
|
{
|
||||||
if ((spellInfo->EquippedItemSubClassMask & (1 << proto->SubClass)) == 0)
|
uint32 subclassMask = spellInfo->EquippedItemSubClassMask & (1 << proto->SubClass);
|
||||||
|
// Two-handers use *_2 subclasses; many strike spells only set one-hand bits (main or off hand).
|
||||||
|
if (!subclassMask && proto->Class == ITEM_CLASS_WEAPON && proto->InventoryType == INVTYPE_2HWEAPON)
|
||||||
|
{
|
||||||
|
if (Player const* player = GetOwner() ? GetOwner()->ToPlayer() : nullptr)
|
||||||
|
{
|
||||||
|
if (player && IsEquipped() && GetBagSlot() == INVENTORY_SLOT_BAG_0 &&
|
||||||
|
(GetSlot() == EQUIPMENT_SLOT_OFFHAND || GetSlot() == EQUIPMENT_SLOT_MAINHAND))
|
||||||
|
{
|
||||||
|
int32 pairedSubclass = -1;
|
||||||
|
switch (proto->SubClass)
|
||||||
|
{
|
||||||
|
case ITEM_SUBCLASS_WEAPON_AXE2: pairedSubclass = ITEM_SUBCLASS_WEAPON_AXE; break;
|
||||||
|
case ITEM_SUBCLASS_WEAPON_MACE2: pairedSubclass = ITEM_SUBCLASS_WEAPON_MACE; break;
|
||||||
|
case ITEM_SUBCLASS_WEAPON_SWORD2: pairedSubclass = ITEM_SUBCLASS_WEAPON_SWORD; break;
|
||||||
|
case ITEM_SUBCLASS_WEAPON_EXOTIC2: pairedSubclass = ITEM_SUBCLASS_WEAPON_EXOTIC; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
if (pairedSubclass >= 0)
|
||||||
|
subclassMask = spellInfo->EquippedItemSubClassMask & (1 << pairedSubclass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!subclassMask)
|
||||||
return false; // subclass not present in mask
|
return false; // subclass not present in mask
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -910,6 +934,25 @@ bool Item::IsFitToSpellRequirements(SpellInfo const* spellInfo) const
|
|||||||
(spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONMAINHAND) ||
|
(spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONMAINHAND) ||
|
||||||
spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONOFFHAND)))
|
spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONOFFHAND)))
|
||||||
return true;
|
return true;
|
||||||
|
// 2H in off-hand or main-hand: many spells only list 1H / generic inventory types.
|
||||||
|
if (proto->InventoryType == INVTYPE_2HWEAPON)
|
||||||
|
{
|
||||||
|
if (Player const* player = GetOwner() ? GetOwner()->ToPlayer() : nullptr)
|
||||||
|
{
|
||||||
|
if (player && IsEquipped() && GetBagSlot() == INVENTORY_SLOT_BAG_0)
|
||||||
|
{
|
||||||
|
if (GetSlot() == EQUIPMENT_SLOT_OFFHAND &&
|
||||||
|
((spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONOFFHAND)) ||
|
||||||
|
(spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPON))))
|
||||||
|
return true;
|
||||||
|
if (GetSlot() == EQUIPMENT_SLOT_MAINHAND &&
|
||||||
|
((spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPONMAINHAND)) ||
|
||||||
|
(spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_WEAPON)) ||
|
||||||
|
(spellInfo->EquippedItemInventoryTypeMask & (1 << INVTYPE_2HWEAPON))))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else if ((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0)
|
else if ((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0)
|
||||||
return false; // inventory type not present in mask
|
return false; // inventory type not present in mask
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3088,6 +3088,12 @@ bool Player::CheckSkillLearnedBySpell(uint32 spellId)
|
|||||||
if (!sWorld->getBoolConfig(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS))
|
if (!sWorld->getBoolConfig(CONFIG_VALIDATE_SKILL_LEARNED_BY_SPELLS))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
// Titan Grip (e.g. talent 46917) ties to warrior-only skill lines in DBC. Classless / cross-class
|
||||||
|
// characters must keep the spell; otherwise it is deleted on every login and 2H off-hand unequips.
|
||||||
|
if (SpellInfo const* si = sSpellMgr->GetSpellInfo(spellId))
|
||||||
|
if (si->HasEffect(SPELL_EFFECT_TITAN_GRIP))
|
||||||
|
return true;
|
||||||
|
|
||||||
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
|
SkillLineAbilityMapBounds skill_bounds = sSpellMgr->GetSkillLineAbilityMapBounds(spellId);
|
||||||
uint32 errorSkill = 0;
|
uint32 errorSkill = 0;
|
||||||
for (SkillLineAbilityMap::const_iterator sla = skill_bounds.first; sla != skill_bounds.second; ++sla)
|
for (SkillLineAbilityMap::const_iterator sla = skill_bounds.first; sla != skill_bounds.second; ++sla)
|
||||||
@@ -3773,8 +3779,8 @@ bool Player::resetTalents(bool noResetCost)
|
|||||||
_removeTalent(itr, GetActiveSpecMask());
|
_removeTalent(itr, GetActiveSpecMask());
|
||||||
}
|
}
|
||||||
|
|
||||||
// xinef: remove titan grip if player had it set
|
// xinef: remove titan grip on warrior talent reset (other classes may use custom Titan Grip)
|
||||||
if (m_canTitanGrip)
|
if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY) && m_canTitanGrip)
|
||||||
SetCanTitanGrip(false);
|
SetCanTitanGrip(false);
|
||||||
// xinef: remove dual wield if player does not have dual wield spell (shamans)
|
// xinef: remove dual wield if player does not have dual wield spell (shamans)
|
||||||
if (!HasSpell(674) && CanDualWield())
|
if (!HasSpell(674) && CanDualWield())
|
||||||
@@ -15409,8 +15415,8 @@ void Player::ActivateSpec(uint8 spec)
|
|||||||
SetPower(POWER_MANA, 0); // Mana must be 0 even if it isn't the active power type.
|
SetPower(POWER_MANA, 0); // Mana must be 0 even if it isn't the active power type.
|
||||||
SetPower(pw, 0);
|
SetPower(pw, 0);
|
||||||
|
|
||||||
// xinef: remove titan grip if player had it set and does not have appropriate talent
|
// xinef: remove titan grip if warrior had it set and does not have appropriate talent (other classes may use Titan Grip from custom spells)
|
||||||
if (!HasTalent(46917, GetActiveSpec()) && m_canTitanGrip)
|
if (IsClass(CLASS_WARRIOR, CLASS_CONTEXT_ABILITY) && !HasTalent(46917, GetActiveSpec()) && m_canTitanGrip)
|
||||||
SetCanTitanGrip(false);
|
SetCanTitanGrip(false);
|
||||||
// xinef: remove dual wield if player does not have dual wield spell (shamans)
|
// xinef: remove dual wield if player does not have dual wield spell (shamans)
|
||||||
if (!HasSpell(674) && CanDualWield())
|
if (!HasSpell(674) && CanDualWield())
|
||||||
|
|||||||
@@ -5525,6 +5525,21 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
|
|||||||
// xinef: load mails before inventory, so problematic items can be added to already loaded mails
|
// xinef: load mails before inventory, so problematic items can be added to already loaded mails
|
||||||
_LoadMail(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAILS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS));
|
_LoadMail(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAILS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS));
|
||||||
|
|
||||||
|
// m_canTitanGrip is normally set when SPELL_EFFECT_TITAN_GRIP runs. After spellbook load, ensure the
|
||||||
|
// flag matches any known TG spell so 2H off-hand items validate before _LoadInventory (classless DK, etc.).
|
||||||
|
for (auto const& spellItr : m_spells)
|
||||||
|
{
|
||||||
|
if (!spellItr.second->Active || spellItr.second->State == PLAYERSPELL_REMOVED)
|
||||||
|
continue;
|
||||||
|
if (SpellInfo const* si = sSpellMgr->GetSpellInfo(spellItr.first))
|
||||||
|
if (si->HasEffect(SPELL_EFFECT_TITAN_GRIP))
|
||||||
|
{
|
||||||
|
SetCanTitanGrip(true);
|
||||||
|
UpdateTitansGrip();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_LoadInventory(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff);
|
_LoadInventory(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_INVENTORY), time_diff);
|
||||||
|
|
||||||
// update items with duration and realtime
|
// update items with duration and realtime
|
||||||
|
|||||||
@@ -61,6 +61,34 @@
|
|||||||
#include "IVMapMgr.h"
|
#include "IVMapMgr.h"
|
||||||
#include "VMapMgr2.h"
|
#include "VMapMgr2.h"
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// Threat of Thassarian off-hand strikes (all ranks). DBC often omits SPELL_ATTR3_REQUIRES_OFF_HAND_WEAPON,
|
||||||
|
// so m_attackType would stay BASE_ATTACK and item/damage use the wrong hand (breaks dual 2H / Titan Grip).
|
||||||
|
bool IsDKThassarianOffHandStrikeSpell(SpellInfo const* spellInfo)
|
||||||
|
{
|
||||||
|
if (!spellInfo || spellInfo->SpellFamilyName != SPELLFAMILY_DEATHKNIGHT || spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SpellInfo const* first = spellInfo->GetFirstRankSpell();
|
||||||
|
if (!first)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (first->Id)
|
||||||
|
{
|
||||||
|
case 66198: // Obliterate
|
||||||
|
case 66196: // Frost Strike
|
||||||
|
case 66216: // Plague Strike
|
||||||
|
case 66188: // Death Strike
|
||||||
|
case 66217: // Rune Strike
|
||||||
|
case 66215: // Blood Strike
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
|
extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS];
|
||||||
|
|
||||||
SpellDestination::SpellDestination()
|
SpellDestination::SpellDestination()
|
||||||
@@ -591,7 +619,7 @@ Spell::Spell(Unit* caster, SpellInfo const* info, TriggerCastFlags triggerFlags,
|
|||||||
switch (m_spellInfo->DmgClass)
|
switch (m_spellInfo->DmgClass)
|
||||||
{
|
{
|
||||||
case SPELL_DAMAGE_CLASS_MELEE:
|
case SPELL_DAMAGE_CLASS_MELEE:
|
||||||
if (m_spellInfo->HasAttribute(SPELL_ATTR3_REQUIRES_OFF_HAND_WEAPON))
|
if (m_spellInfo->HasAttribute(SPELL_ATTR3_REQUIRES_OFF_HAND_WEAPON) || IsDKThassarianOffHandStrikeSpell(m_spellInfo))
|
||||||
m_attackType = OFF_ATTACK;
|
m_attackType = OFF_ATTACK;
|
||||||
else
|
else
|
||||||
m_attackType = BASE_ATTACK;
|
m_attackType = BASE_ATTACK;
|
||||||
|
|||||||
Reference in New Issue
Block a user