Merge branch 'master' into Playerbot

This commit is contained in:
Yunfan Li
2024-10-10 23:13:20 +08:00
15 changed files with 100 additions and 34 deletions

View File

@@ -584,7 +584,8 @@ Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
summons(creature),
_bossId(bossId)
_bossId(bossId),
_nextHealthCheck(0, { }, false)
{
callForHelpRange = 0.0f;
if (instance)
@@ -733,22 +734,20 @@ void BossAI::UpdateAI(uint32 diff)
void BossAI::DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damagetype*/, SpellSchoolMask /*damageSchoolMask*/)
{
if (!_healthCheckEvents.empty())
{
for (auto& check : _healthCheckEvents)
if (_nextHealthCheck._valid)
if (me->HealthBelowPctDamaged(_nextHealthCheck._healthPct, damage))
{
if (check._valid && me->HealthBelowPctDamaged(check._healthPct, damage))
{
check._exec();
check._valid = false;
}
}
_nextHealthCheck._exec();
_nextHealthCheck._valid = false;
_healthCheckEvents.remove_if([&](HealthCheckEventData data) -> bool
{
return !data._valid;
});
}
_healthCheckEvents.remove_if([&](HealthCheckEventData data) -> bool
{
return data._healthPct == _nextHealthCheck._healthPct;
});
if (!_healthCheckEvents.empty())
_nextHealthCheck = _healthCheckEvents.front();
}
}
/**
@@ -760,6 +759,7 @@ void BossAI::DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*
void BossAI::ScheduleHealthCheckEvent(uint32 healthPct, std::function<void()> exec)
{
_healthCheckEvents.push_back(HealthCheckEventData(healthPct, exec));
_nextHealthCheck = _healthCheckEvents.front();
};
void BossAI::ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, std::function<void()> exec)
@@ -768,6 +768,8 @@ void BossAI::ScheduleHealthCheckEvent(std::initializer_list<uint8> healthPct, st
{
_healthCheckEvents.push_back(HealthCheckEventData(checks, exec));
}
_nextHealthCheck = _healthCheckEvents.front();
}
// WorldBossAI - for non-instanced bosses

View File

@@ -503,6 +503,7 @@ protected:
private:
uint32 const _bossId;
std::list<HealthCheckEventData> _healthCheckEvents;
HealthCheckEventData _nextHealthCheck;
};
class WorldBossAI : public ScriptedAI

View File

@@ -416,7 +416,7 @@ ThreatMgr::ThreatMgr(Unit* owner) : iCurrentVictim(nullptr), iOwner(owner), iUpd
void ThreatMgr::ClearAllThreat()
{
if (iOwner->CanHaveThreatList() && !isThreatListEmpty())
if (iOwner->CanHaveThreatList(true) && !isThreatListEmpty())
iOwner->SendClearThreatListOpcode();
clearReferences();
}

View File

@@ -1876,7 +1876,7 @@ bool Creature::IsInvisibleDueToDespawn() const
if (Unit::IsInvisibleDueToDespawn())
return true;
if (IsAlive() || m_corpseRemoveTime > GameTime::GetGameTime().count())
if (IsAlive() || isDying() || m_corpseRemoveTime > GameTime::GetGameTime().count())
return false;
return true;

View File

@@ -87,6 +87,7 @@ void CharmInfo::InitPossessCreateSpells()
case 27664: // Crashin' Thrashin' Racer
case 40281: // Crashin' Thrashin' Racer
case 23109: // Vengeful Spirit
case 25653: // Power of the Blue Flight
break;
default:
InitEmptyActionBar();

View File

@@ -1893,6 +1893,23 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss)
Unit::DealDamage(this, victim, damageInfo->damages[i].damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->damages[i].damageSchoolMask), nullptr, durabilityLoss);
}
// gain rage if attack is fully blocked, dodged or parried
if (HasActivePowerType(POWER_RAGE) && (damageInfo->TargetState == VICTIMSTATE_BLOCKS || damageInfo->TargetState == VICTIMSTATE_DODGE || damageInfo->TargetState == VICTIMSTATE_PARRY))
{
switch (damageInfo->attackType)
{
case BASE_ATTACK:
case OFF_ATTACK:
{
uint32 weaponSpeedHitFactor = uint32(GetAttackTime(damageInfo->attackType) / 1000.0f * (damageInfo->attackType == BASE_ATTACK ? 3.5f : 1.75f));
RewardRage(damageInfo->cleanDamage, weaponSpeedHitFactor, true);
break;
}
default:
break;
}
}
// If this is a creature and it attacks from behind it has a probability to daze it's victim
if ((damageInfo->damages[0].damage + damageInfo->damages[1].damage) && ((damageInfo->hitOutCome == MELEE_HIT_CRIT || damageInfo->hitOutCome == MELEE_HIT_CRUSHING || damageInfo->hitOutCome == MELEE_HIT_NORMAL || damageInfo->hitOutCome == MELEE_HIT_GLANCING) &&
!IsPlayer() && !ToCreature()->IsControlledByPlayer() && !victim->HasInArc(M_PI, this)
@@ -8970,9 +8987,9 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67772; }
break;
}
// Mana Drain Trigger
case 27522:
case 40336:
case 27522: // Mana Drain Trigger
case 40336: // Mana Drain Trigger
case 46939: // Black Bow of the Betrayer
{
// On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target.
if (IsAlive())
@@ -14535,6 +14552,7 @@ void Unit::setDeathState(DeathState s, bool despawn)
{
// death state needs to be updated before RemoveAllAurasOnDeath() calls HandleChannelDeathItem(..) so that
// it can be used to check creation of death items (such as soul shards).
m_deathState = s;
if (s != DeathState::Alive && s != DeathState::JustRespawned)
{
@@ -14584,8 +14602,6 @@ void Unit::setDeathState(DeathState s, bool despawn)
{
RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground)
}
m_deathState = s;
}
/*########################################
@@ -14593,14 +14609,14 @@ void Unit::setDeathState(DeathState s, bool despawn)
######## AGGRO SYSTEM ########
######## ########
########################################*/
bool Unit::CanHaveThreatList() const
bool Unit::CanHaveThreatList(bool skipAliveCheck) const
{
// only creatures can have threat list
if (!IsCreature())
return false;
// only alive units can have threat list
if (!IsAlive() || isDying())
if (!skipAliveCheck && !IsAlive())
return false;
// totems can not have threat list
@@ -20144,6 +20160,7 @@ void Unit::SendRemoveFromThreatListOpcode(HostileReference* pHostileReference)
void Unit::RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker)
{
// Rage formulae https://wowwiki-archive.fandom.com/wiki/Rage#Formulae
float addRage;
float rageconversion = ((0.0091107836f * GetLevel() * GetLevel()) + 3.225598133f * GetLevel()) + 4.2652911f;
@@ -20154,9 +20171,10 @@ void Unit::RewardRage(uint32 damage, uint32 weaponSpeedHitFactor, bool attacker)
if (attacker)
{
addRage = (damage / rageconversion * 7.5f + weaponSpeedHitFactor) / 2;
// talent who gave more rage on attack
// see Bornak's bluepost explanation (05/29/2009)
float rageFromDamageDealt = damage / rageconversion * 7.5f;
addRage = (rageFromDamageDealt + weaponSpeedHitFactor) / 2.0f;
addRage = std::min(addRage, rageFromDamageDealt * 2.0f);
AddPct(addRage, GetTotalAuraModifier(SPELL_AURA_MOD_RAGE_FROM_DAMAGE_DEALT));
}
else

View File

@@ -831,7 +831,7 @@ public:
void SetCombatTimer(uint32 timer) { m_CombatTimer = timer; }
// Threat related methods
[[nodiscard]] bool CanHaveThreatList() const;
[[nodiscard]] bool CanHaveThreatList(bool skipAliveCheck = false) const;
void AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellInfo const* threatSpell = nullptr);
float ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL);
void TauntApply(Unit* victim);

View File

@@ -2929,7 +2929,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
{
if (missInfo != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(effectUnit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL)))
{
if (!m_triggeredByAuraSpell.spellInfo || (!(m_triggeredByAuraSpell.spellInfo->Effects[m_triggeredByAuraSpell.effectIndex].TriggerSpell == m_spellInfo->Id) && !(m_triggeredByAuraSpell.spellInfo->IsAuraEffectEqual(m_spellInfo))))
if (!m_triggeredByAuraSpell.spellInfo || m_damage || (!(m_triggeredByAuraSpell.spellInfo->Effects[m_triggeredByAuraSpell.effectIndex].TriggerSpell == m_spellInfo->Id) && !(m_triggeredByAuraSpell.spellInfo->IsAuraEffectEqual(m_spellInfo))))
m_caster->CombatStart(effectUnit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS));
// Patch 3.0.8: All player spells which cause a creature to become aggressive to you will now also immediately cause the creature to be tapped.

View File

@@ -289,7 +289,7 @@ public:
_duelInProgress = true;
timer = 600000; // clear playerGUIDs after 10 minutes if no one initiates a duel
me->GetMotionMaster()->MoveFollow(caster, 2.0f, 0.0f);
me->SetFacingToObject(caster);
events.ScheduleEvent(EVENT_SPEAK, 3s);
events.ScheduleEvent(EVENT_SPEAK + 1, 7s);

View File

@@ -2137,6 +2137,7 @@ public:
{
_textCounter = 1;
_playerGUID.Clear();
_events.Reset();
}
void JustEngagedWith(Unit* who) override

View File

@@ -1317,8 +1317,18 @@ struct npc_flame_of_azzinoth : public ScriptedAI
{
ScheduleTimedEvent(10s, [&] {
if (Creature* _blade = ObjectAccessor::GetCreature(*me, _bladeGUID))
{
Unit* offTank = nullptr;
if (Creature* secondBlaze = me->FindNearestCreature(NPC_BLAZE, 100.0f, true))
offTank = secondBlaze->GetVictim();
if (Unit* target = _blade->AI()->SelectTarget(SelectTargetMethod::Random, 0, -40.0f, true))
DoCast(target, SPELL_CHARGE);
{
if (!offTank || offTank != target)
DoCast(target, SPELL_CHARGE);
}
}
}, 5s, 20s);
ScheduleTimedEvent(10s, 20s, [&] {

View File

@@ -196,12 +196,22 @@ class spell_mother_shahraz_saber_lash_aura : public AuraScript
}
};
const Position validTeleportStairsPos[4] =
const Position validTeleportStairsPos[9] =
{
//Platform teleports
{945.00f, 149.17f, 197.07483},
{956.92f, 153.20f, 197.07483},
{933.69f, 154.15f, 197.07483},
//Floor teleports
{966.87f, 184.45f, 192.84f},
{927.22f, 187.04f, 192.84f},
{922.54f, 110.09f, 192.84f},
{958.01f, 110.47f, 192.84f}
{958.01f, 110.47f, 192.84f},
{939.95f, 108.29f, 192.84f},
{945.68f, 205.74f, 192.84f}
};
constexpr float minTeleportDist = 30.f;
@@ -227,7 +237,7 @@ class spell_mother_shahraz_fatal_attraction : public SpellScript
// Check if the boss is near stairs to avoid players falling through the platform with random teleports.
if (GetCaster()->GetPositionY() < 194.f)
finalDest = validTeleportStairsPos[urand(0, 3)];
finalDest = validTeleportStairsPos[urand(0, 8)];
else
{
finalDest = GetCaster()->GetNearPosition(frand(minTeleportDist, maxTeleportDist), static_cast<float>(rand_norm()) * static_cast<float>(2 * M_PI), true);