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:
@@ -0,0 +1,53 @@
|
|||||||
|
--
|
||||||
|
UPDATE `creature_template` SET `flags_extra` = `flags_extra` | 2048 WHERE `entry` IN (
|
||||||
|
3672, -- Boahn
|
||||||
|
4403, -- Muckshell Pincer
|
||||||
|
4540, -- Scarlet Monk
|
||||||
|
7033, -- Firegut Ogre
|
||||||
|
8218, -- Witherheart the Stalker
|
||||||
|
10478, -- Splintered Skeleton
|
||||||
|
10488, -- Risen Construct
|
||||||
|
11022, -- Alexi Barov
|
||||||
|
12100, -- Lava Reaver
|
||||||
|
12262, -- Ziggurat Protector
|
||||||
|
12263, -- Slaughterhouse Protector
|
||||||
|
14688, -- Prince Sandoval
|
||||||
|
14882, -- Atal'ai Mistress
|
||||||
|
16299, -- Skeletal Shocktrooper
|
||||||
|
16422, -- Skeletal Soldier
|
||||||
|
16593, -- Shattered Hand Brawler
|
||||||
|
16808, -- Warchief Kargath Bladefist
|
||||||
|
16936, -- Dreghood Wanderer
|
||||||
|
18120, -- Ango'rosh Mauler
|
||||||
|
18211, -- Murkblood Brute
|
||||||
|
18541, -- Urdak
|
||||||
|
18860, -- Daughter of Destiny
|
||||||
|
19191, -- Arazzius the Cruel
|
||||||
|
20456, -- Ethereum Researcher
|
||||||
|
20582, -- Shattered Hand Brawler (1)
|
||||||
|
20597, -- Warchief Kargath Bladefist (1)
|
||||||
|
20683, -- Prophetess Cavrylin
|
||||||
|
20783, -- Porfus the Gem Gorger
|
||||||
|
20784, -- Armbreaker Huffaz
|
||||||
|
20785, -- Fel Tinkerer Zortan
|
||||||
|
20786, -- Gul'bor
|
||||||
|
20790, -- Malevus the Mad
|
||||||
|
20929, -- Wrath Lord
|
||||||
|
20984, -- Protectorate Defender
|
||||||
|
21639, -- Illidari Slayer
|
||||||
|
21717, -- Dragonmaw Wrangler
|
||||||
|
21805, -- Protectorate Avenger
|
||||||
|
21877, -- Karsius the Ancient Watcher
|
||||||
|
22004, -- Leoroxx
|
||||||
|
22076, -- Torloth the Magnificent
|
||||||
|
22082, -- Shadowmoon Slayer
|
||||||
|
22199, -- Slaag
|
||||||
|
22377, -- Akuno
|
||||||
|
22825, -- Matron Li-sahar
|
||||||
|
23008, -- Ethereum Jailor
|
||||||
|
23410, -- Spirit of Udalo
|
||||||
|
23411, -- Spirit of Olum
|
||||||
|
24882, -- Brutallus
|
||||||
|
37132, -- Ymirjar Battle-Maiden
|
||||||
|
38132 -- Ymirjar Battle-Maiden (1)
|
||||||
|
);
|
||||||
@@ -52,7 +52,7 @@ void UnitAI::DoMeleeAttackIfReady()
|
|||||||
if (me->isAttackReady())
|
if (me->isAttackReady())
|
||||||
{
|
{
|
||||||
// xinef: prevent base and off attack in same time, delay attack at 0.2 sec
|
// xinef: prevent base and off attack in same time, delay attack at 0.2 sec
|
||||||
if (me->haveOffhandWeapon())
|
if (me->HasOffhandWeaponForAttack())
|
||||||
if (me->getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
|
if (me->getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
|
||||||
me->setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
|
me->setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
|
||||||
|
|
||||||
@@ -60,7 +60,7 @@ void UnitAI::DoMeleeAttackIfReady()
|
|||||||
me->resetAttackTimer();
|
me->resetAttackTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (me->haveOffhandWeapon() && me->isAttackReady(OFF_ATTACK))
|
if (me->HasOffhandWeaponForAttack() && me->isAttackReady(OFF_ATTACK))
|
||||||
{
|
{
|
||||||
// xinef: delay main hand attack if both will hit at the same time (players code)
|
// xinef: delay main hand attack if both will hit at the same time (players code)
|
||||||
if (me->getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
|
if (me->getAttackTimer(BASE_ATTACK) < ATTACK_DISPLAY_DELAY)
|
||||||
|
|||||||
@@ -522,17 +522,33 @@ void ScriptedAI::SetEquipmentSlots(bool loadDefault, int32 mainHand /*= EQUIP_NO
|
|||||||
if (loadDefault)
|
if (loadDefault)
|
||||||
{
|
{
|
||||||
me->LoadEquipment(me->GetOriginalEquipmentId(), true);
|
me->LoadEquipment(me->GetOriginalEquipmentId(), true);
|
||||||
|
if (me->HasWeapon(OFF_ATTACK))
|
||||||
|
me->SetCanDualWield(true);
|
||||||
|
else
|
||||||
|
me->SetCanDualWield(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mainHand >= 0)
|
if (mainHand >= 0)
|
||||||
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(mainHand));
|
{
|
||||||
|
me->SetVirtualItem(0, uint32(mainHand));
|
||||||
|
me->UpdateDamagePhysical(BASE_ATTACK);
|
||||||
|
}
|
||||||
|
|
||||||
if (offHand >= 0)
|
if (offHand >= 0)
|
||||||
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(offHand));
|
{
|
||||||
|
me->SetVirtualItem(1, uint32(offHand));
|
||||||
|
if (offHand >= 1)
|
||||||
|
me->SetCanDualWield(true);
|
||||||
|
else
|
||||||
|
me->SetCanDualWield(false);
|
||||||
|
}
|
||||||
|
|
||||||
if (ranged >= 0)
|
if (ranged >= 0)
|
||||||
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, uint32(ranged));
|
{
|
||||||
|
me->SetVirtualItem(2, uint32(ranged));
|
||||||
|
me->UpdateDamagePhysical(RANGED_ATTACK);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum eNPCs
|
enum eNPCs
|
||||||
|
|||||||
@@ -524,6 +524,8 @@ bool Creature::InitEntry(uint32 Entry, const CreatureData* data)
|
|||||||
|
|
||||||
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, cinfo->HoverHeight);
|
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, cinfo->HoverHeight);
|
||||||
|
|
||||||
|
SetCanDualWield(cinfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);
|
||||||
|
|
||||||
// checked at loading
|
// checked at loading
|
||||||
m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
|
m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
|
||||||
if (!m_wanderDistance && m_defaultMovementType == RANDOM_MOTION_TYPE)
|
if (!m_wanderDistance && m_defaultMovementType == RANDOM_MOTION_TYPE)
|
||||||
@@ -568,6 +570,8 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
|
|||||||
|
|
||||||
ReplaceAllDynamicFlags(dynamicflags);
|
ReplaceAllDynamicFlags(dynamicflags);
|
||||||
|
|
||||||
|
SetCanDualWield(cInfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);
|
||||||
|
|
||||||
SetAttackTime(BASE_ATTACK, cInfo->BaseAttackTime);
|
SetAttackTime(BASE_ATTACK, cInfo->BaseAttackTime);
|
||||||
SetAttackTime(OFF_ATTACK, cInfo->BaseAttackTime);
|
SetAttackTime(OFF_ATTACK, cInfo->BaseAttackTime);
|
||||||
SetAttackTime(RANGED_ATTACK, cInfo->RangeAttackTime);
|
SetAttackTime(RANGED_ATTACK, cInfo->RangeAttackTime);
|
||||||
@@ -3172,6 +3176,14 @@ bool Creature::IsImmuneToKnockback() const
|
|||||||
return cinfo && (cinfo->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK);
|
return cinfo && (cinfo->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Creature::HasWeapon(WeaponAttackType type) const
|
||||||
|
{
|
||||||
|
const uint8 slot = uint8(type);
|
||||||
|
ItemEntry const* item = sItemStore.LookupEntry(GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + slot));
|
||||||
|
|
||||||
|
return ((item && item->ClassID == ITEM_CLASS_WEAPON) || (type == OFF_ATTACK && CanDualWield()));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Enable or disable the creature's walk mode by removing: MOVEMENTFLAG_WALKING. Infom also the client
|
* @brief Enable or disable the creature's walk mode by removing: MOVEMENTFLAG_WALKING. Infom also the client
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -190,6 +190,8 @@ public:
|
|||||||
void UpdateAttackPowerAndDamage(bool ranged = false) override;
|
void UpdateAttackPowerAndDamage(bool ranged = false) override;
|
||||||
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
|
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
|
||||||
|
|
||||||
|
bool HasWeapon(WeaponAttackType type) const override;
|
||||||
|
bool HasWeaponForAttack(WeaponAttackType type) const override { return (Unit::HasWeaponForAttack(type) && HasWeapon(type)); }
|
||||||
void SetCanDualWield(bool value) override;
|
void SetCanDualWield(bool value) override;
|
||||||
[[nodiscard]] int8 GetOriginalEquipmentId() const { return m_originalEquipmentId; }
|
[[nodiscard]] int8 GetOriginalEquipmentId() const { return m_originalEquipmentId; }
|
||||||
uint8 GetCurrentEquipmentId() { return m_equipmentId; }
|
uint8 GetCurrentEquipmentId() { return m_equipmentId; }
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ enum CreatureFlagsExtra : uint32
|
|||||||
CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and 'attack me' effects
|
CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and 'attack me' effects
|
||||||
CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE = 0x00000200, // creature won't update movement flags
|
CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE = 0x00000200, // creature won't update movement flags
|
||||||
CREATURE_FLAG_EXTRA_GHOST_VISIBILITY = 0x00000400, // creature will only be visible to dead players
|
CREATURE_FLAG_EXTRA_GHOST_VISIBILITY = 0x00000400, // creature will only be visible to dead players
|
||||||
CREATURE_FLAG_EXTRA_UNUSED_12 = 0x00000800, /// @todo: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)
|
CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK = 0x00000800, // creature will use offhand attacks
|
||||||
CREATURE_FLAG_EXTRA_NO_SELL_VENDOR = 0x00001000, // players can't sell items to this vendor
|
CREATURE_FLAG_EXTRA_NO_SELL_VENDOR = 0x00001000, // players can't sell items to this vendor
|
||||||
CREATURE_FLAG_EXTRA_IGNORE_COMBAT = 0x00002000,
|
CREATURE_FLAG_EXTRA_IGNORE_COMBAT = 0x00002000,
|
||||||
CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging)
|
CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging)
|
||||||
@@ -77,9 +77,7 @@ enum CreatureFlagsExtra : uint32
|
|||||||
CREATURE_FLAG_EXTRA_HARD_RESET = 0x80000000,
|
CREATURE_FLAG_EXTRA_HARD_RESET = 0x80000000,
|
||||||
|
|
||||||
// Masks
|
// Masks
|
||||||
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12), // SKIP
|
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~CREATURE_FLAG_EXTRA_DUNGEON_BOSS) // SKIP
|
||||||
|
|
||||||
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) // SKIP
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CreatureGroundMovementType : uint8
|
enum class CreatureGroundMovementType : uint8
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ AC_API_EXPORT EnumText EnumUtils<CreatureFlagsExtra>::ToString(CreatureFlagsExtr
|
|||||||
case CREATURE_FLAG_EXTRA_NO_TAUNT: return { "CREATURE_FLAG_EXTRA_NO_TAUNT", "CREATURE_FLAG_EXTRA_NO_TAUNT", "creature is immune to taunt auras and 'attack me' effects" };
|
case CREATURE_FLAG_EXTRA_NO_TAUNT: return { "CREATURE_FLAG_EXTRA_NO_TAUNT", "CREATURE_FLAG_EXTRA_NO_TAUNT", "creature is immune to taunt auras and 'attack me' effects" };
|
||||||
case CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE: return { "CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE", "CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE", "creature won't update movement flags" };
|
case CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE: return { "CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE", "CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE", "creature won't update movement flags" };
|
||||||
case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return { "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "creature will only be visible to dead players" };
|
case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return { "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "creature will only be visible to dead players" };
|
||||||
case CREATURE_FLAG_EXTRA_UNUSED_12: return { "CREATURE_FLAG_EXTRA_UNUSED_12", "CREATURE_FLAG_EXTRA_UNUSED_12", "/ @todo: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)" };
|
case CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK: return { "CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK", "CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK", "/ @todo: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)" };
|
||||||
case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return { "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "players can't sell items to this vendor" };
|
case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return { "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "players can't sell items to this vendor" };
|
||||||
case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return { "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "" };
|
case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return { "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "" };
|
||||||
case CREATURE_FLAG_EXTRA_WORLDEVENT: return { "CREATURE_FLAG_EXTRA_WORLDEVENT", "CREATURE_FLAG_EXTRA_WORLDEVENT", "custom flag for world event creatures (left room for merging)" };
|
case CREATURE_FLAG_EXTRA_WORLDEVENT: return { "CREATURE_FLAG_EXTRA_WORLDEVENT", "CREATURE_FLAG_EXTRA_WORLDEVENT", "custom flag for world event creatures (left room for merging)" };
|
||||||
@@ -86,7 +86,7 @@ AC_API_EXPORT CreatureFlagsExtra EnumUtils<CreatureFlagsExtra>::FromIndex(std::s
|
|||||||
case 8: return CREATURE_FLAG_EXTRA_NO_TAUNT;
|
case 8: return CREATURE_FLAG_EXTRA_NO_TAUNT;
|
||||||
case 9: return CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE;
|
case 9: return CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE;
|
||||||
case 10: return CREATURE_FLAG_EXTRA_GHOST_VISIBILITY;
|
case 10: return CREATURE_FLAG_EXTRA_GHOST_VISIBILITY;
|
||||||
case 11: return CREATURE_FLAG_EXTRA_UNUSED_12;
|
case 11: return CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK;
|
||||||
case 12: return CREATURE_FLAG_EXTRA_NO_SELL_VENDOR;
|
case 12: return CREATURE_FLAG_EXTRA_NO_SELL_VENDOR;
|
||||||
case 13: return CREATURE_FLAG_EXTRA_IGNORE_COMBAT;
|
case 13: return CREATURE_FLAG_EXTRA_IGNORE_COMBAT;
|
||||||
case 14: return CREATURE_FLAG_EXTRA_WORLDEVENT;
|
case 14: return CREATURE_FLAG_EXTRA_WORLDEVENT;
|
||||||
@@ -127,7 +127,7 @@ AC_API_EXPORT std::size_t EnumUtils<CreatureFlagsExtra>::ToIndex(CreatureFlagsEx
|
|||||||
case CREATURE_FLAG_EXTRA_NO_TAUNT: return 8;
|
case CREATURE_FLAG_EXTRA_NO_TAUNT: return 8;
|
||||||
case CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE: return 9;
|
case CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE: return 9;
|
||||||
case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return 10;
|
case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return 10;
|
||||||
case CREATURE_FLAG_EXTRA_UNUSED_12: return 11;
|
case CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK: return 11;
|
||||||
case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return 12;
|
case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return 12;
|
||||||
case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return 13;
|
case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return 13;
|
||||||
case CREATURE_FLAG_EXTRA_WORLDEVENT: return 14;
|
case CREATURE_FLAG_EXTRA_WORLDEVENT: return 14;
|
||||||
|
|||||||
@@ -1248,6 +1248,8 @@ public:
|
|||||||
return GetItemByPos(bag, slot);
|
return GetItemByPos(bag, slot);
|
||||||
}
|
}
|
||||||
[[nodiscard]] Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const;
|
[[nodiscard]] Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const;
|
||||||
|
bool HasWeapon(WeaponAttackType type) const override { return GetWeaponForAttack(type, false); }
|
||||||
|
bool HasWeaponForAttack(WeaponAttackType type) const override { return (Unit::HasWeaponForAttack(type) && GetWeaponForAttack(type, true)); }
|
||||||
[[nodiscard]] Item* GetShield(bool useable = false) const;
|
[[nodiscard]] Item* GetShield(bool useable = false) const;
|
||||||
static uint8 GetAttackBySlot(uint8 slot); // MAX_ATTACK if not weapon slot
|
static uint8 GetAttackBySlot(uint8 slot); // MAX_ATTACK if not weapon slot
|
||||||
std::vector<Item*>& GetItemUpdateQueue() { return m_itemUpdateQueue; }
|
std::vector<Item*>& GetItemUpdateQueue() { return m_itemUpdateQueue; }
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ void Player::Update(uint32 p_time)
|
|||||||
|
|
||||||
// prevent base and off attack in same time, delay attack at
|
// prevent base and off attack in same time, delay attack at
|
||||||
// 0.2 sec
|
// 0.2 sec
|
||||||
if (haveOffhandWeapon())
|
if (HasOffhandWeaponForAttack())
|
||||||
if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
|
if (getAttackTimer(OFF_ATTACK) < ATTACK_DISPLAY_DELAY)
|
||||||
setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
|
setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ void Player::Update(uint32 p_time)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
|
if (HasOffhandWeaponForAttack() && isAttackReady(OFF_ATTACK))
|
||||||
{
|
{
|
||||||
if (!IsWithinMeleeRange(victim))
|
if (!IsWithinMeleeRange(victim))
|
||||||
setAttackTimer(OFF_ATTACK, 100);
|
setAttackTimer(OFF_ATTACK, 100);
|
||||||
|
|||||||
@@ -512,7 +512,7 @@ void Player::UpdateAttackPowerAndDamage(bool ranged)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
UpdateDamagePhysical(BASE_ATTACK);
|
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);
|
UpdateDamagePhysical(OFF_ATTACK);
|
||||||
if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_STATS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_STATS)) // mental quickness
|
if (IsClass(CLASS_SHAMAN, CLASS_CONTEXT_STATS) || IsClass(CLASS_PALADIN, CLASS_CONTEXT_STATS)) // mental quickness
|
||||||
UpdateSpellDamageAndHealingBonus();
|
UpdateSpellDamageAndHealingBonus();
|
||||||
@@ -567,7 +567,7 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo
|
|||||||
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
|
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
|
||||||
float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
|
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();
|
uint8 lvl = GetLevel();
|
||||||
if (lvl > 60)
|
if (lvl > 60)
|
||||||
@@ -1118,7 +1118,7 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attType == OFF_ATTACK && !haveOffhandWeapon())
|
if (attType == OFF_ATTACK && !HasOffhandWeaponForAttack())
|
||||||
{
|
{
|
||||||
minDamage = 0.0f;
|
minDamage = 0.0f;
|
||||||
maxDamage = 0.0f;
|
maxDamage = 0.0f;
|
||||||
@@ -1128,10 +1128,11 @@ void Creature::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized,
|
|||||||
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
|
float weaponMinDamage = GetWeaponDamageRange(attType, MINDAMAGE);
|
||||||
float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
|
float weaponMaxDamage = GetWeaponDamageRange(attType, MAXDAMAGE);
|
||||||
|
|
||||||
if (!CanUseAttackType(attType)) // disarm case
|
// Disarm for creatures
|
||||||
|
if (HasWeapon(attType) && !HasWeaponForAttack(attType))
|
||||||
{
|
{
|
||||||
weaponMinDamage = 0.0f;
|
minDamage *= 0.5f;
|
||||||
weaponMaxDamage = 0.0f;
|
maxDamage *= 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
float attackPower = GetTotalAttackPowerValue(attType);
|
float attackPower = GetTotalAttackPowerValue(attType);
|
||||||
|
|||||||
@@ -1856,7 +1856,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
|
|||||||
float offtime = float(victim->getAttackTimer(OFF_ATTACK));
|
float offtime = float(victim->getAttackTimer(OFF_ATTACK));
|
||||||
float basetime = float(victim->getAttackTimer(BASE_ATTACK));
|
float basetime = float(victim->getAttackTimer(BASE_ATTACK));
|
||||||
// Reduce attack time
|
// Reduce attack time
|
||||||
if (victim->haveOffhandWeapon() && offtime < basetime)
|
if (victim->HasOffhandWeaponForAttack() && offtime < basetime)
|
||||||
{
|
{
|
||||||
float percent20 = victim->GetAttackTime(OFF_ATTACK) * 0.20f;
|
float percent20 = victim->GetAttackTime(OFF_ATTACK) * 0.20f;
|
||||||
float percent60 = 3.0f * percent20;
|
float percent60 = 3.0f * percent20;
|
||||||
@@ -8430,7 +8430,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
|
|||||||
if (dummySpell->SpellIconID == 2023)
|
if (dummySpell->SpellIconID == 2023)
|
||||||
{
|
{
|
||||||
// Must Dual Wield
|
// Must Dual Wield
|
||||||
if (!procSpell || !haveOffhandWeapon())
|
if (!procSpell || !HasOffhandWeaponForAttack())
|
||||||
return false;
|
return false;
|
||||||
// Chance as basepoints for dummy aura
|
// Chance as basepoints for dummy aura
|
||||||
if (!roll_chance_i(triggerAmount))
|
if (!roll_chance_i(triggerAmount))
|
||||||
@@ -10348,9 +10348,9 @@ bool Unit::Attack(Unit* victim, bool meleeAttack)
|
|||||||
SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// delay offhand weapon attack to next attack time
|
// delay offhand weapon attack by 50% of the base attack time
|
||||||
if (haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
|
if (HasOffhandWeaponForAttack() && isAttackReady(OFF_ATTACK))
|
||||||
setAttackTimer(OFF_ATTACK, ATTACK_DISPLAY_DELAY);
|
setAttackTimer(OFF_ATTACK, std::max(getAttackTimer(OFF_ATTACK), getAttackTimer(BASE_ATTACK) + int32(CalculatePct(GetFloatValue(UNIT_FIELD_BASEATTACKTIME), 50))));
|
||||||
|
|
||||||
if (meleeAttack)
|
if (meleeAttack)
|
||||||
SendMeleeAttackStart(victim);
|
SendMeleeAttackStart(victim);
|
||||||
@@ -13400,7 +13400,7 @@ float Unit::GetWeaponProcChance() const
|
|||||||
// (odd formula...)
|
// (odd formula...)
|
||||||
if (isAttackReady(BASE_ATTACK))
|
if (isAttackReady(BASE_ATTACK))
|
||||||
return (GetAttackTime(BASE_ATTACK) * 1.8f / 1000.0f);
|
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 (GetAttackTime(OFF_ATTACK) * 1.6f / 1000.0f);
|
||||||
return 0;
|
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
|
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 0.0f;
|
||||||
|
|
||||||
return m_weaponDamage[attType][type][damageIndex];
|
return m_weaponDamage[attType][type][damageIndex];
|
||||||
@@ -18953,7 +18953,7 @@ float Unit::MeleeSpellMissChance(Unit const* victim, WeaponAttackType attType, i
|
|||||||
float missChance = victim->GetUnitMissChance(attType);
|
float missChance = victim->GetUnitMissChance(attType);
|
||||||
|
|
||||||
// Check if dual wielding, add additional miss penalty - when mainhand has on next swing spell, offhand doesnt suffer penalty
|
// 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;
|
missChance += 19;
|
||||||
}
|
}
|
||||||
@@ -19467,6 +19467,16 @@ Unit* Unit::GetRedirectThreatTarget() const
|
|||||||
return _redirectThreatInfo.GetTargetGUID() ? ObjectAccessor::GetUnit(*this, _redirectThreatInfo.GetTargetGUID()) : nullptr;
|
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)
|
void Unit::JumpTo(float speedXY, float speedZ, bool forward)
|
||||||
{
|
{
|
||||||
float angle = forward ? 0 : M_PI;
|
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);
|
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)
|
void Unit::Talk(uint32 textId, ChatMsg msgType, float textRange, WorldObject const* target)
|
||||||
{
|
{
|
||||||
if (!sObjectMgr->GetBroadcastText(textId))
|
if (!sObjectMgr->GetBroadcastText(textId))
|
||||||
|
|||||||
@@ -1052,8 +1052,22 @@ public:
|
|||||||
[[nodiscard]] float GetUnitMissChance(WeaponAttackType attType) const;
|
[[nodiscard]] float GetUnitMissChance(WeaponAttackType attType) const;
|
||||||
float GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const;
|
float GetUnitCriticalChance(WeaponAttackType attackType, Unit const* victim) const;
|
||||||
int32 GetMechanicResistChance(SpellInfo const* spell);
|
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
|
[[nodiscard]] bool CanUseAttackType(uint8 attacktype) const
|
||||||
{
|
{
|
||||||
|
if (IsAttackSpeedOverridenShapeShift())
|
||||||
|
return false;
|
||||||
|
|
||||||
switch (attacktype)
|
switch (attacktype)
|
||||||
{
|
{
|
||||||
case BASE_ATTACK:
|
case BASE_ATTACK:
|
||||||
@@ -1062,8 +1076,9 @@ public:
|
|||||||
return !HasUnitFlag2(UNIT_FLAG2_DISARM_OFFHAND);
|
return !HasUnitFlag2(UNIT_FLAG2_DISARM_OFFHAND);
|
||||||
case RANGED_ATTACK:
|
case RANGED_ATTACK:
|
||||||
return !HasUnitFlag2(UNIT_FLAG2_DISARM_RANGED);
|
return !HasUnitFlag2(UNIT_FLAG2_DISARM_RANGED);
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] virtual uint32 GetShieldBlockValue() const = 0;
|
[[nodiscard]] virtual uint32 GetShieldBlockValue() const = 0;
|
||||||
@@ -1466,6 +1481,8 @@ public:
|
|||||||
SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
|
SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsAttackSpeedOverridenShapeShift() const;
|
||||||
|
|
||||||
[[nodiscard]] bool IsInFeralForm() const
|
[[nodiscard]] bool IsInFeralForm() const
|
||||||
{
|
{
|
||||||
ShapeshiftForm form = GetShapeshiftForm();
|
ShapeshiftForm form = GetShapeshiftForm();
|
||||||
@@ -1760,6 +1777,9 @@ public:
|
|||||||
[[nodiscard]] float GetCollisionWidth() const override;
|
[[nodiscard]] float GetCollisionWidth() const override;
|
||||||
[[nodiscard]] float GetCollisionRadius() const override;
|
[[nodiscard]] float GetCollisionRadius() const override;
|
||||||
|
|
||||||
|
uint32 GetVirtualItemId(uint32 slot) const;
|
||||||
|
void SetVirtualItem(uint32 slot, uint32 itemId);
|
||||||
|
|
||||||
void ProcessPositionDataChanged(PositionFullTerrainStatus const& data) override;
|
void ProcessPositionDataChanged(PositionFullTerrainStatus const& data) override;
|
||||||
virtual void ProcessTerrainStatusUpdate();
|
virtual void ProcessTerrainStatusUpdate();
|
||||||
|
|
||||||
|
|||||||
@@ -4068,7 +4068,7 @@ void Spell::_cast(bool skipCheck)
|
|||||||
{
|
{
|
||||||
m_caster->resetAttackTimer(BASE_ATTACK);
|
m_caster->resetAttackTimer(BASE_ATTACK);
|
||||||
|
|
||||||
if (m_caster->haveOffhandWeapon())
|
if (m_caster->HasOffhandWeaponForAttack())
|
||||||
{
|
{
|
||||||
m_caster->resetAttackTimer(OFF_ATTACK);
|
m_caster->resetAttackTimer(OFF_ATTACK);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user