Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-09-29 23:22:47 +08:00
74 changed files with 806 additions and 146 deletions

View File

@@ -524,6 +524,8 @@ bool Creature::InitEntry(uint32 Entry, const CreatureData* data)
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, cinfo->HoverHeight);
SetCanDualWield(cinfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);
// checked at loading
m_defaultMovementType = MovementGeneratorType(cinfo->MovementType);
if (!m_wanderDistance && m_defaultMovementType == RANDOM_MOTION_TYPE)
@@ -568,6 +570,8 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
ReplaceAllDynamicFlags(dynamicflags);
SetCanDualWield(cInfo->flags_extra & CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK);
SetAttackTime(BASE_ATTACK, cInfo->BaseAttackTime);
SetAttackTime(OFF_ATTACK, cInfo->BaseAttackTime);
SetAttackTime(RANGED_ATTACK, cInfo->RangeAttackTime);
@@ -3173,6 +3177,14 @@ bool Creature::IsImmuneToKnockback() const
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
*/

View File

@@ -190,6 +190,8 @@ public:
void UpdateAttackPowerAndDamage(bool ranged = false) 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;
[[nodiscard]] int8 GetOriginalEquipmentId() const { return m_originalEquipmentId; }
uint8 GetCurrentEquipmentId() { return m_equipmentId; }

View File

@@ -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_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_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_IGNORE_COMBAT = 0x00002000,
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,
// Masks
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12), // SKIP
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) // SKIP
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~CREATURE_FLAG_EXTRA_DUNGEON_BOSS) // SKIP
};
enum class CreatureGroundMovementType : uint8

View File

@@ -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_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_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_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)" };
@@ -86,7 +86,7 @@ AC_API_EXPORT CreatureFlagsExtra EnumUtils<CreatureFlagsExtra>::FromIndex(std::s
case 8: return CREATURE_FLAG_EXTRA_NO_TAUNT;
case 9: return CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE;
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 13: return CREATURE_FLAG_EXTRA_IGNORE_COMBAT;
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_MOVE_FLAGS_UPDATE: return 9;
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_IGNORE_COMBAT: return 13;
case CREATURE_FLAG_EXTRA_WORLDEVENT: return 14;

View File

@@ -2725,17 +2725,17 @@ bool WorldObject::GetClosePoint(float& x, float& y, float& z, float size, float
return true;
}
Position WorldObject::GetNearPosition(float dist, float angle)
Position WorldObject::GetNearPosition(float dist, float angle, bool disableWarning)
{
Position pos = GetPosition();
MovePosition(pos, dist, angle);
MovePosition(pos, dist, angle, disableWarning);
return pos;
}
Position WorldObject::GetRandomNearPosition(float radius)
Position WorldObject::GetRandomNearPosition(float radius, bool disableWarning)
{
Position pos = GetPosition();
MovePosition(pos, radius * (float) rand_norm(), (float) rand_norm() * static_cast<float>(2 * M_PI));
MovePosition(pos, radius * (float) rand_norm(), (float) rand_norm() * static_cast<float>(2 * M_PI), disableWarning);
return pos;
}
@@ -2773,7 +2773,7 @@ void WorldObject::GetChargeContactPoint(WorldObject const* obj, float& x, float&
return (m_valuesCount > UNIT_FIELD_COMBATREACH) ? m_floatValues[UNIT_FIELD_COMBATREACH] : DEFAULT_WORLD_OBJECT_SIZE * GetObjectScale();
}
void WorldObject::MovePosition(Position& pos, float dist, float angle)
void WorldObject::MovePosition(Position& pos, float dist, float angle, bool disableWarning)
{
angle += GetOrientation();
float destx, desty, destz, ground, floor;
@@ -2783,7 +2783,9 @@ void WorldObject::MovePosition(Position& pos, float dist, float angle)
// Prevent invalid coordinates here, position is unchanged
if (!Acore::IsValidMapCoord(destx, desty))
{
LOG_FATAL("entities.object", "WorldObject::MovePosition invalid coordinates X: {} and Y: {} were passed!", destx, desty);
if (!disableWarning)
LOG_FATAL("entities.object", "WorldObject::MovePosition invalid coordinates X: {} and Y: {} were passed!", destx, desty);
return;
}

View File

@@ -420,13 +420,13 @@ public:
void GetNearPoint(WorldObject const* searcher, float& x, float& y, float& z, float searcher_size, float distance2d, float absAngle, float controlZ = 0, Position const* startPos = nullptr) const;
void GetVoidClosePoint(float& x, float& y, float& z, float size, float distance2d = 0, float relAngle = 0, float controlZ = 0) const;
bool GetClosePoint(float& x, float& y, float& z, float size, float distance2d = 0, float angle = 0, WorldObject const* forWho = nullptr, bool force = false) const;
void MovePosition(Position& pos, float dist, float angle);
Position GetNearPosition(float dist, float angle);
void MovePosition(Position& pos, float dist, float angle, bool disableWarning = false);
Position GetNearPosition(float dist, float angle, bool disableWarning = false);
void MovePositionToFirstCollision(Position& pos, float dist, float angle);
Position GetFirstCollisionPosition(float startX, float startY, float startZ, float destX, float destY);
Position GetFirstCollisionPosition(float destX, float destY, float destZ);
Position GetFirstCollisionPosition(float dist, float angle);
Position GetRandomNearPosition(float radius);
Position GetRandomNearPosition(float radius, bool disableWarning = false);
void GetContactPoint(WorldObject const* obj, float& x, float& y, float& z, float distance2d = CONTACT_DISTANCE) const;
void GetChargeContactPoint(WorldObject const* obj, float& x, float& y, float& z, float distance2d = CONTACT_DISTANCE) const;

View File

@@ -11347,6 +11347,9 @@ void Player::LeaveBattleground(Battleground* bg)
sScriptMgr->OnBattlegroundDesertion(this, BG_DESERTION_TYPE_LEAVE_BG);
}
if (bg->isArena() && (bg->GetStatus() == STATUS_IN_PROGRESS || bg->GetStatus() == STATUS_WAIT_JOIN))
sScriptMgr->OnBattlegroundDesertion(this, ARENA_DESERTION_TYPE_LEAVE_BG);
bg->RemovePlayerAtLeave(this);
// xinef: reset corpse reclaim time

View File

@@ -1238,6 +1238,8 @@ public:
return GetItemByPos(bag, slot);
}
[[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;
static uint8 GetAttackBySlot(uint8 slot); // MAX_ATTACK if not weapon slot
std::vector<Item*>& GetItemUpdateQueue() { return m_itemUpdateQueue; }

View File

@@ -195,7 +195,7 @@ void Player::Update(uint32 p_time)
// prevent base and off attack in same time, delay attack at
// 0.2 sec
if (haveOffhandWeapon())
if (HasOffhandWeaponForAttack())
if (getAttackTimer(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))
setAttackTimer(OFF_ATTACK, 100);

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))
@@ -10365,9 +10365,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);
@@ -13417,7 +13417,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;
}
@@ -15423,7 +15423,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];
@@ -18984,7 +18984,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;
}
@@ -19498,6 +19498,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;
@@ -21151,6 +21161,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

@@ -1053,6 +1053,17 @@ 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
{
switch (attacktype)
@@ -1063,8 +1074,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;
@@ -1468,6 +1480,8 @@ public:
SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
}
bool IsAttackSpeedOverridenShapeShift() const;
[[nodiscard]] bool IsInFeralForm() const
{
ShapeshiftForm form = GetShapeshiftForm();
@@ -1762,6 +1776,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();