fix(Core/Unit): fix Dual Wield for more creatures, CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK, creature disarm damage (#20015)

* enable CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK

* sql set CREATURE_FLAG_EXTRA_USE_OFFHAND

* use new HasOffHandWeaponForAttack instead of haveOffhandWeapon

no longer requires m_CanDualwield set to use Offhand attack
requires non-disarmed weapon in Offhand OR creature_flag_extra enabled

Co-authored-by: Ovah <dreadkiller@gmx.de>
Co-authored-by: Warlockbugs <warlockbugs@outlook.com>

* Make shapeshift forms which dont override attack speed use weapon damage

Co-authored-by: killerwife <killerwife@gmail.com>

* SetEquipmentSlots turning off damage update when using template default

Co-authored-by: killerwife <killerwife@gmail.com>

* Setup DualWield & Damage On Equipment Updates, implement Set and GetVirtualItem

Co-authored-by: Yatzii <47720837+Yatzii93@users.noreply.github.com>

* creature disarm damage

set disarm to reduce minmax damage by 50% instead of setting to 0

Co-authored-by: Warlockbugs <warlockbugs@outlook.com>

---------

Co-authored-by: Ovah <dreadkiller@gmx.de>
Co-authored-by: Warlockbugs <warlockbugs@outlook.com>
Co-authored-by: killerwife <killerwife@gmail.com>
Co-authored-by: Yatzii <47720837+Yatzii93@users.noreply.github.com>
This commit is contained in:
Jelle Meeus
2024-09-25 16:53:39 +02:00
committed by GitHub
parent da31098094
commit 6e4a9bbb14
13 changed files with 160 additions and 30 deletions

View File

@@ -512,7 +512,7 @@ void Player::UpdateAttackPowerAndDamage(bool ranged)
else
{
UpdateDamagePhysical(BASE_ATTACK);
if (CanDualWield() && haveOffhandWeapon()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
if (CanDualWield() && HasOffhandWeaponForAttack()) //allow update offhand damage only if player knows DualWield Spec and has equipped offhand weapon
UpdateDamagePhysical(OFF_ATTACK);
if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_STATS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_STATS)) // mental quickness
UpdateSpellDamageAndHealingBonus();
@@ -567,7 +567,7 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
if (IsInFeralForm()) // check if player is druid and in cat or bear forms
if (IsAttackSpeedOverridenShapeShift()) // forms with no override on attack speed use normal weapon damage
{
uint8 lvl = GetLevel();
if (lvl > 60)
@@ -1118,7 +1118,7 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized,
break;
}
if (attType == OFF_ATTACK && !haveOffhandWeapon())
if (attType == OFF_ATTACK && !HasOffhandWeaponForAttack())
{
minDamage = 0.0f;
maxDamage = 0.0f;
@@ -1128,10 +1128,11 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized,
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
if (!CanUseAttackType(attType)) // disarm case
// Disarm for creatures
if (HasWeapon(attType) && !HasWeaponForAttack(attType))
{
weaponMinDamage = 0.0f;
weaponMaxDamage = 0.0f;
minDamage *= 0.5f;
maxDamage *= 0.5f;
}
float attackPower = GetTotalAttackPowerValue(attType);

View File

@@ -1856,7 +1856,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
float offtime = float(victim->getAttackTimer(OFF_ATTACK));
float basetime = float(victim->getAttackTimer(BASE_ATTACK));
// Reduce attack time
if (victim->haveOffhandWeapon() && offtime < basetime)
if (victim->HasOffhandWeaponForAttack() && offtime < basetime)
{
float percent20 = victim->GetAttackTime(OFF_ATTACK) * 0.20f;
float percent60 = 3.0f * percent20;
@@ -8430,7 +8430,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
if (dummySpell->SpellIconID == 2023)
{
// Must Dual Wield
if (!procSpell || !haveOffhandWeapon())
if (!procSpell || !HasOffhandWeaponForAttack())
return false;
// Chance as basepoints for dummy aura
if (!roll_chance_i(triggerAmount))
@@ -10348,9 +10348,9 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
}
// delay offhand weapon attack to next attack time
if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
// delay offhand weapon attack by 50% of the base attack time
if (HasOffhandWeaponForAttack() && isAttackReady(OFF_ATTACK))
setAttackTimer(OFF_ATTACK, std::max(getAttackTimer(OFF_ATTACK), getAttackTimer(BASE_ATTACK) + int32(CalculatePct(GetFloatValue(UNIT_FIELD_BASEATTACKTIME), 50))));
if (meleeAttack)
SendMeleeAttackStart(victim);
@@ -13400,7 +13400,7 @@ float Unit::GetWeaponProcChance() const
// (odd formula...)
if (isAttackReady(BASE_ATTACK))
return (GetAttackTime(BASE_ATTACK) * 1.8f / 1000.0f);
else if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
else if (HasOffhandWeaponForAttack() && isAttackReady(OFF_ATTACK))
return (GetAttackTime(OFF_ATTACK) * 1.6f / 1000.0f);
return 0;
}
@@ -15406,7 +15406,7 @@ float Unit::GetTotalAttackPowerValue(WeaponAttackType attType, Unit* victim) con
float Unit::GetWeaponDamageRange(WeaponAttackType attType, WeaponDamageRange type, uint8 damageIndex /*= 0*/) const
{
if (attType == OFF_ATTACK && !haveOffhandWeapon())
if (attType == OFF_ATTACK && !HasOffhandWeaponForAttack())
return 0.0f;
return m_weaponDamage[attType][type][damageIndex];
@@ -18953,7 +18953,7 @@ float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, i
float missChance = victim->GetUnitMissChance(attType);
// Check if dual wielding, add additional miss penalty - when mainhand has on next swing spell, offhand doesnt suffer penalty
if (!spellId && (attType != RANGED_ATTACK) && haveOffhandWeapon() && (!m_currentSpells[CURRENT_MELEE_SPELL] || !m_currentSpells[CURRENT_MELEE_SPELL]->IsNextMeleeSwingSpell()))
if (!spellId && (attType != RANGED_ATTACK) && HasOffhandWeaponForAttack() && (!m_currentSpells[CURRENT_MELEE_SPELL] || !m_currentSpells[CURRENT_MELEE_SPELL]->IsNextMeleeSwingSpell()))
{
missChance += 19;
}
@@ -19467,6 +19467,16 @@ Unit* Unit::GetRedirectThreatTarget() const
return _redirectThreatInfo.GetTargetGUID() ? ObjectAccessor::GetUnit(*this, _redirectThreatInfo.GetTargetGUID()) : nullptr;
}
bool Unit::IsAttackSpeedOverridenShapeShift() const
{
// Mirroring clientside gameplay logic
if (ShapeshiftForm form = GetShapeshiftForm())
if (SpellShapeshiftFormEntry const* entry = sSpellShapeshiftFormStore.LookupEntry(form))
return entry->attackSpeed > 0;
return false;
}
void Unit::JumpTo(float speedXY, float speedZ, bool forward)
{
float angle = forward ? 0 : M_PI;
@@ -21120,6 +21130,22 @@ void Unit::Whisper(std::string_view text, Language language, Player* target, boo
target->SendDirectMessage(&data);
}
uint32 Unit::GetVirtualItemId(uint32 slot) const
{
if (slot >= MAX_EQUIPMENT_ITEMS)
return 0;
return GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + slot);
}
void Unit::SetVirtualItem(uint32 slot, uint32 itemId)
{
if (slot >= MAX_EQUIPMENT_ITEMS)
return;
SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + slot, itemId);
}
void Unit::Talk(uint32 textId, ChatMsg msgType, float textRange, WorldObject const* target)
{
if (!sObjectMgr->GetBroadcastText(textId))

View File

@@ -1052,8 +1052,22 @@ public:
[[nodiscard]] float GetUnitMissChance(WeaponAttackType attType) const;
float GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const;
int32 GetMechanicResistChance(SpellInfo const* spell);
virtual bool HasWeapon(WeaponAttackType type) const = 0;
inline bool HasMainhandWeapon() const { return HasWeapon(BASE_ATTACK); }
inline bool HasOffhandWeapon() const { return HasWeapon(OFF_ATTACK); }
inline bool HasRangedWeapon() const { return HasWeapon(RANGED_ATTACK); }
inline bool hasMainhandWeaponForAttack() const { return HasWeaponForAttack(BASE_ATTACK); }
virtual bool HasWeaponForAttack(WeaponAttackType type) const { return CanUseAttackType(type); }
inline bool HasMainhandWeaponForAttack() const { return HasWeaponForAttack(BASE_ATTACK); }
inline bool HasOffhandWeaponForAttack() const { return HasWeaponForAttack(OFF_ATTACK); }
inline bool HasRangedWeaponForAttack() const { return HasWeaponForAttack(RANGED_ATTACK); }
[[nodiscard]] bool CanUseAttackType(uint8 attacktype) const
{
if (IsAttackSpeedOverridenShapeShift())
return false;
switch (attacktype)
{
case BASE_ATTACK:
@@ -1062,8 +1076,9 @@ public:
return !HasUnitFlag2(UNIT_FLAG2_DISARM_OFFHAND);
case RANGED_ATTACK:
return !HasUnitFlag2(UNIT_FLAG2_DISARM_RANGED);
default:
return true;
}
return true;
}
[[nodiscard]] virtual uint32 GetShieldBlockValue() const = 0;
@@ -1466,6 +1481,8 @@ public:
SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
}
bool IsAttackSpeedOverridenShapeShift() const;
[[nodiscard]] bool IsInFeralForm() const
{
ShapeshiftForm form = GetShapeshiftForm();
@@ -1760,6 +1777,9 @@ public:
[[nodiscard]] float GetCollisionWidth() const override;
[[nodiscard]] float GetCollisionRadius() const override;
uint32 GetVirtualItemId(uint32 slot) const;
void SetVirtualItem(uint32 slot, uint32 itemId);
void ProcessPositionDataChanged(PositionFullTerrainStatus const& data) override;
virtual void ProcessTerrainStatusUpdate();