Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
bash
2025-09-24 01:59:18 +02:00
committed by GitHub
25 changed files with 544 additions and 266 deletions
+3
View File
@@ -231,6 +231,9 @@ public:
// Called when an aura is removed or expires.
virtual void OnAuraRemove(AuraApplication* /*aurApp*/, AuraRemoveMode /*mode*/) { }
virtual void DistancingStarted() {}
virtual void DistancingEnded() {}
protected:
virtual void MoveInLineOfSight(Unit* /*who*/);
+44 -50
View File
@@ -49,7 +49,6 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
mEvadeDisabled = false;
mCanAutoAttack = true;
mCanCombatMove = true;
mForcedPaused = false;
@@ -80,6 +79,9 @@ SmartAI::SmartAI(Creature* c) : CreatureAI(c)
m_ConditionsTimer = 0;
if (me->GetVehicleKit())
conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_TEMPLATE_VEHICLE, me->GetEntry());
_currentRangeMode = false;
_attackDistance = 0.f;
}
bool SmartAI::IsAIControlled() const
@@ -846,7 +848,7 @@ void SmartAI::AttackStart(Unit* who)
if (who && me->Attack(who, me->IsWithinMeleeRange(who)))
{
if (mCanCombatMove)
if (!me->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
{
SetRun(mRun);
MovementGeneratorType type = me->GetMotionMaster()->GetMotionSlotType(MOTION_SLOT_ACTIVE);
@@ -1047,37 +1049,49 @@ void SmartAI::sQuestReward(Player* player, Quest const* quest, uint32 opt)
GetScript()->ProcessEventsFor(SMART_EVENT_REWARD_QUEST, player, quest->GetQuestId(), opt);
}
void SmartAI::SetCombatMove(bool on, float chaseRange)
void SmartAI::SetCombatMovement(bool on, bool stopOrStartMovement)
{
if (mCanCombatMove == on)
if (on)
me->ClearUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT);
else
me->AddUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT);
if (!IsAIControlled() || HasEscortState(SMART_ESCORT_ESCORTING))
return;
mCanCombatMove = on;
if (!IsAIControlled())
return;
if (!HasEscortState(SMART_ESCORT_ESCORTING))
if (stopOrStartMovement && me->GetVictim()) // Only change current movement while in combat
{
if (on && me->GetVictim())
if (!me->IsCrowdControlled())
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == IDLE_MOTION_TYPE)
{
SetRun(mRun);
me->GetMotionMaster()->MoveChase(me->GetVictim(), chaseRange);
me->CastStop();
}
}
else
{
me->StopMoving();
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MoveIdle();
if (on)
me->GetMotionMaster()->MoveChase(me->GetVictim());
else if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->StopMoving();
}
}
}
void SmartAI::SetCurrentRangeMode(bool on, float range)
{
_currentRangeMode = on;
_attackDistance = range;
if (Unit* victim = me->GetVictim())
me->GetMotionMaster()->MoveChase(victim, _attackDistance);
}
void SmartAI::DistanceYourself(float range)
{
Unit* victim = me->GetVictim();
if (!victim || !victim->IsWithinMeleeRange(me))
return;
float combatReach = me->GetMeleeRange(victim);
float distance = DISTANCING_CONSTANT + std::max(combatReach * 1.5f, combatReach + range);
me->GetMotionMaster()->DistanceYourself(distance);
_pendingDistancing = distance;
}
void SmartAI::SetFollow(Unit* target, float dist, float angle, uint32 credit, uint32 end, uint32 creditType, bool aliveState)
{
if (!target)
@@ -1130,32 +1144,6 @@ void SmartAI::StopFollow(bool complete)
GetScript()->ProcessEventsFor(SMART_EVENT_FOLLOW_COMPLETED, player);
}
void SmartAI::MoveAway(float distance)
{
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
return;
mCanCombatMove = false;
if (!IsAIControlled())
return;
if (!HasEscortState(SMART_ESCORT_ESCORTING))
{
if (me->GetVictim())
{
me->StopMoving();
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
me->GetMotionMaster()->Clear(false);
float x, y, z;
me->GetClosePoint(x, y, z, me->GetObjectSize(), distance, M_PI);
if (me->GetVictim()->IsWithinLOS(x, y, z))
me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z);
}
}
}
void SmartAI::SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker)
{
if (invoker)
@@ -1183,6 +1171,12 @@ void SmartAI::PathEndReached(uint32 /*pathId*/)
me->LoadPath(0);
}
void SmartAI::DistancingEnded()
{
SetCurrentRangeMode(true, _pendingDistancing);
_pendingDistancing = 0.f;
}
void SmartGameObjectAI::SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
{
GetScript()->ProcessEventsFor(SMART_EVENT_SUMMONED_UNIT_DIES, summon);
+10 -6
View File
@@ -40,6 +40,8 @@ enum SmartEscortVars
SMART_MAX_AID_DIST = SMART_ESCORT_MAX_PLAYER_DIST / 2,
};
#define DISTANCING_CONSTANT 1.f // buffer for better functionality of distancing
class SmartAI : public CreatureAI
{
public:
@@ -63,11 +65,11 @@ public:
bool IsEscorted() override { return (mEscortState & SMART_ESCORT_ESCORTING); }
void RemoveEscortState(uint32 uiEscortState) { mEscortState &= ~uiEscortState; }
void SetAutoAttack(bool on) { mCanAutoAttack = on; }
void SetCombatMove(bool on, float chaseRange = 0.0f);
bool CanCombatMove() { return mCanCombatMove; }
void SetCombatMovement(bool on, bool stopOrStartMovement);
void SetCurrentRangeMode(bool on, float range = 0.f);
void DistanceYourself(float range);
void SetFollow(Unit* target, float dist = 0.0f, float angle = 0.0f, uint32 credit = 0, uint32 end = 0, uint32 creditType = 0, bool aliveState = true);
void StopFollow(bool complete);
void MoveAway(float distance);
void SetScript9(SmartScriptHolder& e, uint32 entry, WorldObject* invoker);
SmartScript* GetScript() { return &mScript; }
@@ -212,8 +214,7 @@ public:
// Xinef
void SetWPPauseTimer(uint32 time) { mWPPauseTimer = time; }
void SetChaseOnInterrupt(bool apply) { _chaseOnInterrupt = apply; }
[[nodiscard]] bool CanChaseOnInterrupt() const { return _chaseOnInterrupt; }
void DistancingEnded() override;
private:
bool mIsCharmed;
@@ -242,7 +243,6 @@ private:
bool mRun;
bool mEvadeDisabled;
bool mCanAutoAttack;
bool mCanCombatMove;
bool mForcedPaused;
uint32 mInvincibilityHpLevel;
@@ -263,6 +263,10 @@ private:
bool _chaseOnInterrupt;
std::unordered_map<uint32, uint32> aiDataSet;
bool _currentRangeMode;
float _attackDistance;
float _pendingDistancing;
};
class SmartGameObjectAI : public GameObjectAI
+29 -37
View File
@@ -686,7 +686,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
float spellMinRange = me->GetSpellMinRangeForTarget(target->ToUnit(), spellInfo);
float meleeRange = me->GetMeleeRange(target->ToUnit());
bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit());
bool isWithinLOSInMap = me->IsWithinLOSInMap(target->ToUnit(), VMAP::ModelIgnoreFlags::M2);
bool isWithinMeleeRange = distanceToTarget <= meleeRange;
bool isRangedAttack = spellMaxRange > NOMINAL_MELEE_RANGE;
bool isTargetRooted = target->ToUnit()->HasUnitState(UNIT_STATE_ROOT);
@@ -703,7 +703,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
continue;
float minDistance = std::max(meleeRange, spellMinRange) - distanceToTarget + NOMINAL_MELEE_RANGE;
CAST_AI(SmartAI, me->AI())->MoveAway(std::min(minDistance, spellMaxRange));
CAST_AI(SmartAI, me->AI())->DistanceYourself(std::min(minDistance, spellMaxRange));
continue;
}
@@ -715,7 +715,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
continue;
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
continue;
}
else if (distanceToTarget < spellMinRange || !(isWithinLOSInMap || isSpellIgnoreLOS))
@@ -725,7 +725,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (me->IsRooted()) // Rooted inhabit type, never move/reposition
continue;
CAST_AI(SmartAI, me->AI())->SetCombatMove(true);
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, 0.f);
if (e.action.cast.castFlags & SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS)
CAST_AI(SmartAI, me->AI())->SetCombatMovement(true, true);
continue;
}
@@ -743,18 +745,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
{
CAST_AI(SmartAI, me->AI())->SetChaseOnInterrupt(true);
if (!me->isMoving()) // Don't try to reposition while we are moving
{
// If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS.
if (result == SPELL_FAILED_OUT_OF_RANGE)
// if we are just out of range, we only chase until we are back in spell range.
CAST_AI(SmartAI, me->AI())->SetCombatMove(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
else
// if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful.
CAST_AI(SmartAI, me->AI())->SetCombatMove(spellCastFailed);
}
// If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS.
if (result == SPELL_FAILED_OUT_OF_RANGE || result == SPELL_CAST_OK)
// if we are just out of range, we only chase until we are back in spell range.
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
else // move into melee on any other fail
// if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful.
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f);
}
if (spellCastFailed)
@@ -989,7 +986,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break;
bool move = e.action.combatMove.move;
CAST_AI(SmartAI, me->AI())->SetCombatMove(move);
CAST_AI(SmartAI, me->AI())->SetCombatMovement(move, true);
LOG_DEBUG("sql.sql", "SmartScript::ProcessAction:: SMART_ACTION_ALLOW_COMBAT_MOVEMENT: Creature {} bool on = {}",
me->GetGUID().ToString(), e.action.combatMove.move);
break;
@@ -2061,7 +2058,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
for (WorldObject* target : targets)
if (Creature* creature = target->ToCreature())
if (IsSmart(creature) && creature->GetVictim())
if (CAST_AI(SmartAI, creature->AI())->CanCombatMove())
if (!creature->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT))
creature->GetMotionMaster()->MoveChase(creature->GetVictim(), attackDistance, attackAngle);
break;
@@ -2730,26 +2727,9 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
me->InterruptNonMeleeSpells(false);
}
if (e.action.castCustom.flags & SMARTCAST_COMBAT_MOVE)
{
// If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed
// unless target is outside spell range, out of mana, or LOS.
bool _allowMove = false;
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.castCustom.spell); // AssertSpellInfo?
int32 mana = me->GetPower(POWER_MANA);
if (me->GetDistance(target->ToUnit()) > spellInfo->GetMaxRange(true) ||
me->GetDistance(target->ToUnit()) < spellInfo->GetMinRange(true) ||
!me->IsWithinLOSInMap(target->ToUnit()) ||
mana < spellInfo->CalcPowerCost(me, spellInfo->GetSchoolMask()))
_allowMove = true;
CAST_AI(SmartAI, me->AI())->SetCombatMove(_allowMove);
}
if (!(e.action.castCustom.flags & SMARTCAST_AURA_NOT_PRESENT) || !target->ToUnit()->HasAura(e.action.castCustom.spell))
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(e.action.castCustom.spell);
CustomSpellValues values;
if (e.action.castCustom.bp1)
values.AddSpellMod(SPELLVALUE_BASE_POINT0, e.action.castCustom.bp1);
@@ -2757,7 +2737,19 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
values.AddSpellMod(SPELLVALUE_BASE_POINT1, e.action.castCustom.bp2);
if (e.action.castCustom.bp3)
values.AddSpellMod(SPELLVALUE_BASE_POINT2, e.action.castCustom.bp3);
me->CastCustomSpell(e.action.castCustom.spell, values, target->ToUnit(), (e.action.castCustom.flags & SMARTCAST_TRIGGERED) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
SpellCastResult result = me->CastCustomSpell(spellInfo, values, target->ToUnit(), (e.action.castCustom.flags & SMARTCAST_TRIGGERED) ? TRIGGERED_FULL_MASK : TRIGGERED_NONE);
float spellMaxRange = me->GetSpellMaxRangeForTarget(target->ToUnit(), spellInfo);
if (e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE)
{
// If cast flag SMARTCAST_COMBAT_MOVE is set combat movement will not be allowed unless target is outside spell range, out of mana, or LOS.
if (result == SPELL_FAILED_OUT_OF_RANGE || result == SPELL_CAST_OK)
// if we are just out of range, we only chase until we are back in spell range.
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(true, std::max(spellMaxRange - NOMINAL_MELEE_RANGE, 0.0f));
else // move into melee on any other fail
// if spell fail for any other reason, we chase to melee range, or stay where we are if spellcast was successful.
CAST_AI(SmartAI, me->AI())->SetCurrentRangeMode(false, 0.f);
}
}
}
}
@@ -1957,7 +1957,8 @@ enum SmartCastFlags
SMARTCAST_AURA_NOT_PRESENT = 0x020, // Only casts the spell if the target does not have an aura from the spell
SMARTCAST_COMBAT_MOVE = 0x040, // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS
SMARTCAST_THREATLIST_NOT_SINGLE = 0x080, // Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment)
SMARTCAST_TARGET_POWER_MANA = 0x100 // Only cast if the target has power type mana (e.g. Mana Drain)
SMARTCAST_TARGET_POWER_MANA = 0x100, // Only cast if the target has power type mana (e.g. Mana Drain)
SMARTCAST_ENABLE_COMBAT_MOVE_ON_LOS = 0x200,
};
enum SmartFollowType
+10 -8
View File
@@ -1081,7 +1081,6 @@ void ConditionMgr::LoadConditions(bool isReload)
LOG_INFO("server.loading", "Reloading `gossip_menu_option` Table for Conditions!");
sObjectMgr->LoadGossipMenuItems();
sSpellMgr->UnloadSpellInfoImplicitTargetConditionLists();
}
QueryResult result = WorldDatabase.Query("SELECT SourceTypeOrReferenceId, SourceGroup, SourceEntry, SourceId, ElseGroup, ConditionTypeOrReference, ConditionTarget, "
@@ -1405,7 +1404,7 @@ bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond)
// build new shared mask with found effect
uint32 sharedMask = (1 << i);
ConditionList* cmp = spellInfo->Effects[i].ImplicitTargetConditions;
std::shared_ptr<ConditionList> cmp = spellInfo->Effects[i].ImplicitTargetConditions;
for (uint8 effIndex = i + 1; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
{
if (spellInfo->Effects[effIndex].ImplicitTargetConditions == cmp)
@@ -1428,7 +1427,7 @@ bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond)
return false;
// get shared data
ConditionList* sharedList = spellInfo->Effects[firstEffIndex].ImplicitTargetConditions;
std::shared_ptr<ConditionList> sharedList = spellInfo->Effects[firstEffIndex].ImplicitTargetConditions;
// there's already data entry for that sharedMask
if (sharedList)
@@ -1447,22 +1446,25 @@ bool ConditionMgr::addToSpellImplicitTargetConditions(Condition* cond)
else
{
// add new list, create new shared mask
sharedList = new ConditionList();
auto newList = std::make_shared<ConditionList>();
bool assigned = false;
for (uint8 i = firstEffIndex; i < MAX_SPELL_EFFECTS; ++i)
{
if ((1 << i) & commonMask)
{
spellInfo->Effects[i].ImplicitTargetConditions = sharedList;
assigned = true;
spellInfo->Effects[i].ImplicitTargetConditions = newList;
assigned = true;
}
}
if (!assigned)
delete sharedList;
if (assigned)
sharedList = newList;
}
if (sharedList)
sharedList->push_back(cond);
break;
}
}
+5 -9
View File
@@ -1283,6 +1283,11 @@ SpellCastResult Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const& v
return SPELL_FAILED_SPELL_UNAVAILABLE;
}
return CastCustomSpell(spellInfo, value, victim, triggerFlags, castItem, triggeredByAura, originalCaster);
}
SpellCastResult Unit::CastCustomSpell(SpellInfo const* spellInfo, CustomSpellValues const& value, Unit* victim, TriggerCastFlags triggerFlags, Item* castItem, AuraEffect const* triggeredByAura, ObjectGuid originalCaster)
{
SpellCastTargets targets;
targets.SetUnitTarget(victim);
@@ -4130,15 +4135,6 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi
spell->SetReferencedFromCurrent(false);
}
// SAI creatures only
// Start chasing victim if they are spell casters (at least one SMC spell) if interrupted/silenced.
if (IsCreature())
{
if (SmartAI* ai = dynamic_cast<SmartAI*>(ToCreature()->AI()))
if (ai->CanChaseOnInterrupt())
ai->SetCombatMove(true);
}
if (IsCreature() && IsAIEnabled)
ToCreature()->AI()->OnSpellCastFinished(spell->GetSpellInfo(), SPELL_FINISHED_CANCELED);
}
+3
View File
@@ -736,6 +736,8 @@ public:
[[nodiscard]] uint16 GetExtraUnitMovementFlags() const { return m_movementInfo.flags2; }
void SetExtraUnitMovementFlags(uint16 f) { m_movementInfo.flags2 = f; }
inline bool IsCrowdControlled() const { return HasFlag(UNIT_FIELD_FLAGS, (UNIT_FLAG_CONFUSED | UNIT_FLAG_FLEEING | UNIT_FLAG_STUNNED)); }
/*********************************************************/
/*** UNIT TYPES, CLASSES, RACES... ***/
/*********************************************************/
@@ -1595,6 +1597,7 @@ public:
SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim, bool triggered, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
SpellCastResult CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
SpellCastResult CastCustomSpell(uint32 spellId, CustomSpellValues const& value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
SpellCastResult CastCustomSpell(SpellInfo const* spellInfo, CustomSpellValues const& value, Unit* victim = nullptr, TriggerCastFlags triggerFlags = TRIGGERED_NONE, Item* castItem = nullptr, AuraEffect const* triggeredByAura = nullptr, ObjectGuid originalCaster = ObjectGuid::Empty);
/*********************************************************/
/*** METHODS RELATED TO GAMEOBJECT & DYNOBEJCTS ***/
@@ -197,6 +197,8 @@ enum UnitState
UNIT_STATE_IGNORE_PATHFINDING = 0x10000000, // do not use pathfinding in any MovementGenerator
UNIT_STATE_NO_ENVIRONMENT_UPD = 0x20000000,
UNIT_STATE_NO_COMBAT_MOVEMENT, // serverside only - should never be changed outside of core and hence shouldnt have a defined static value and be at the end
UNIT_STATE_ALL_STATE_SUPPORTED = UNIT_STATE_DIED | UNIT_STATE_MELEE_ATTACKING | UNIT_STATE_STUNNED | UNIT_STATE_ROAMING | UNIT_STATE_CHASE
| UNIT_STATE_FLEEING | UNIT_STATE_IN_FLIGHT | UNIT_STATE_FOLLOW | UNIT_STATE_ROOT | UNIT_STATE_CONFUSED
| UNIT_STATE_DISTRACTED | UNIT_STATE_ISOLATED | UNIT_STATE_ATTACK_PLAYER | UNIT_STATE_CASTING
+36 -1
View File
@@ -310,12 +310,29 @@ void MotionMaster::MoveConfused()
/**
* @brief Force the unit to chase this target. Doesn't work with UNIT_FLAG_DISABLE_MOVE
*/
void MotionMaster::MoveChase(Unit* target, std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle)
void MotionMaster::MoveChase(Unit* target, std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle)
{
// ignore movement request if target not exist
if (!target || target == _owner || _owner->HasUnitFlag(UNIT_FLAG_DISABLE_MOVE))
return;
if (GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
{
if (_owner->IsPlayer())
{
ChaseMovementGenerator<Player>* gen = (ChaseMovementGenerator<Player>*)top();
gen->SetOffsetAndAngle(dist, angle);
gen->SetNewTarget(target);
}
else
{
ChaseMovementGenerator<Creature>* gen = (ChaseMovementGenerator<Creature>*)top();
gen->SetOffsetAndAngle(dist, angle);
gen->SetNewTarget(target);
}
return;
}
//_owner->ClearUnitState(UNIT_STATE_FOLLOW);
if (_owner->IsPlayer())
{
@@ -331,6 +348,24 @@ void MotionMaster::MoveChase(Unit* target, std::optional<ChaseRange> dist, std:
}
}
void MotionMaster::DistanceYourself(float dist)
{
if (GetCurrentMovementGeneratorType() == CHASE_MOTION_TYPE)
{
if (_owner->IsPlayer())
{
ChaseMovementGenerator<Player>* gen = (ChaseMovementGenerator<Player>*)top();
gen->DistanceYourself((Player*)_owner, dist);
}
else
{
ChaseMovementGenerator<Creature>* gen = (ChaseMovementGenerator<Creature>*)top();
gen->DistanceYourself((Creature*)_owner, dist);
}
return;
}
}
void MotionMaster::MoveBackwards(Unit* target, float dist)
{
if (!target)
+2
View File
@@ -250,6 +250,8 @@ public:
void ReinitializeMovement();
bool GetDestination(float& x, float& y, float& z);
void DistanceYourself(float range);
private:
void Mutate(MovementGenerator* m, MovementSlot slot); // use Move* functions instead
@@ -62,6 +62,81 @@ bool ChaseMovementGenerator<T>::PositionOkay(T* owner, Unit* target, Optional<fl
return true;
}
template<class T>
void ChaseMovementGenerator<T>::SetOffsetAndAngle(std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle)
{
_range = dist;
_angle = angle;
_lastTargetPosition.reset();
}
template<class T>
void ChaseMovementGenerator<T>::SetNewTarget(Unit* target)
{
i_target.link(target, this);
_lastTargetPosition.reset();
}
template<class T>
void ChaseMovementGenerator<T>::DistanceYourself(T* owner, float distance)
{
// make a new path if we have to...
if (!i_path)
i_path = std::make_unique<PathGenerator>(owner);
float x, y, z;
i_target->GetNearPoint(owner, x, y, z, owner->GetBoundaryRadius(), distance, i_target->GetAngle(owner));
if (DispatchSplineToPosition(owner, x, y, z, false, false, 0.f, false, false))
{
m_currentMode = CHASE_MODE_DISTANCING;
if constexpr (!std::is_same_v<T, Player>)
{
owner->AI()->DistancingStarted();
}
}
}
template<class T>
bool ChaseMovementGenerator<T>::DispatchSplineToPosition(T* owner, float x, float y, float z, bool walk, bool cutPath, float maxTarget, bool forceDest, bool target)
{
Creature* cOwner = owner->ToCreature();
if (owner->IsHovering())
owner->UpdateAllowedPositionZ(x, y, z);
bool success = i_path->CalculatePath(x, y, z, forceDest);
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
{
if (cOwner)
{
cOwner->SetCannotReachTarget(i_target.getTarget()->GetGUID());
}
owner->StopMoving();
return true;
}
if (cutPath)
i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget);
if (cOwner)
{
cOwner->SetCannotReachTarget();
}
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
i_recalculateTravel = true;
Movement::MoveSplineInit init(owner);
init.MovebyPath(i_path->GetPath());
if (target)
init.SetFacing(i_target.getTarget());
init.SetWalk(walk);
init.Launch();
return false;
}
template<class T>
bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
{
@@ -71,6 +146,13 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
if (!owner || !owner->IsAlive())
return false;
if (owner->HasUnitState(UNIT_STATE_NO_COMBAT_MOVEMENT)) // script paused combat movement
{
owner->StopMoving();
_lastTargetPosition.reset();
return true;
}
Creature* cOwner = owner->ToCreature();
bool isStoppedBecauseOfCasting = cOwner && cOwner->IsMovementPreventedByCasting();
@@ -243,53 +325,23 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
shortenPath = false;
}
if (owner->IsHovering())
owner->UpdateAllowedPositionZ(x, y, z);
bool success = i_path->CalculatePath(x, y, z, forceDest);
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
{
if (cOwner)
{
cOwner->SetCannotReachTarget(target->GetGUID());
}
owner->StopMoving();
return true;
}
if (shortenPath)
i_path->ShortenPathUntilDist(G3D::Vector3(x, y, z), maxTarget);
if (cOwner)
{
cOwner->SetCannotReachTarget();
}
bool walk = false;
if (cOwner && !cOwner->IsPet())
{
switch (cOwner->GetMovementTemplate().GetChase())
{
case CreatureChaseMovementType::CanWalk:
walk = owner->IsWalking();
break;
case CreatureChaseMovementType::AlwaysWalk:
walk = true;
break;
default:
break;
case CreatureChaseMovementType::CanWalk:
walk = owner->IsWalking();
break;
case CreatureChaseMovementType::AlwaysWalk:
walk = true;
break;
default:
break;
}
}
owner->AddUnitState(UNIT_STATE_CHASE_MOVE);
i_recalculateTravel = true;
Movement::MoveSplineInit init(owner);
init.MovebyPath(i_path->GetPath());
init.SetFacing(target);
init.SetWalk(walk);
init.Launch();
DispatchSplineToPosition(owner, x, y, z, walk, shortenPath, maxTarget, forceDest, true);
}
}
@@ -339,9 +391,24 @@ void ChaseMovementGenerator<T>::MovementInform(T* owner)
if (!owner->IsCreature())
return;
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
if (CreatureAI* AI = owner->ToCreature()->AI())
AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter());
switch (m_currentMode)
{
default:
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
if (CreatureAI* AI = owner->ToCreature()->AI())
AI->MovementInform(CHASE_MOTION_TYPE, i_target.getTarget()->GetGUID().GetCounter());
break;
}
case CHASE_MODE_DISTANCING:
{
if (CreatureAI* AI = owner->ToCreature()->AI())
AI->DistancingEnded();
break;
}
}
m_currentMode = CHASE_MODE_NORMAL;
}
//-----------------------------------------------//
@@ -604,6 +671,13 @@ template bool ChaseMovementGenerator<Player>::DoUpdate(Player*, uint32);
template bool ChaseMovementGenerator<Creature>::DoUpdate(Creature*, uint32);
template void ChaseMovementGenerator<Unit>::MovementInform(Unit*);
template void ChaseMovementGenerator<Creature>::SetOffsetAndAngle(std::optional<ChaseRange>, std::optional<ChaseAngle>);
template void ChaseMovementGenerator<Creature>::SetNewTarget(Unit*);
template void ChaseMovementGenerator<Creature>::DistanceYourself(Creature*, float);
template void ChaseMovementGenerator<Player>::SetOffsetAndAngle(std::optional<ChaseRange>, std::optional<ChaseAngle>);
template void ChaseMovementGenerator<Player>::SetNewTarget(Unit*);
template void ChaseMovementGenerator<Player>::DistanceYourself(Player*, float);
template void FollowMovementGenerator<Player>::DoInitialize(Player*);
template void FollowMovementGenerator<Creature>::DoInitialize(Creature*);
template void FollowMovementGenerator<Player>::DoFinalize(Player*);
@@ -34,12 +34,20 @@ protected:
FollowerReference i_target;
};
enum ChaseMovementMode
{
CHASE_MODE_NORMAL, // chasing target
CHASE_MODE_BACKPEDAL, // collision movement
CHASE_MODE_DISTANCING, // running away from melee
CHASE_MODE_FANNING, // mob collision movement
};
template<class T>
class ChaseMovementGenerator : public MovementGeneratorMedium<T, ChaseMovementGenerator<T>>, public TargetedMovementGeneratorBase
{
public:
ChaseMovementGenerator(Unit* target, Optional<ChaseRange> range = {}, Optional<ChaseAngle> angle = {})
: TargetedMovementGeneratorBase(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle) {}
: TargetedMovementGeneratorBase(target), i_leashExtensionTimer(5000), i_path(nullptr), i_recheckDistance(0), i_recalculateTravel(true), _range(range), _angle(angle), m_currentMode(CHASE_MODE_NORMAL) {}
~ChaseMovementGenerator() { }
MovementGeneratorType GetMovementGeneratorType() { return CHASE_MOTION_TYPE; }
@@ -58,6 +66,11 @@ public:
bool EnableWalking() const { return false; }
bool HasLostTarget(Unit* unit) const { return unit->GetVictim() != this->GetTarget(); }
void SetOffsetAndAngle(std::optional<ChaseRange> dist, std::optional<ChaseAngle> angle);
void SetNewTarget(Unit* target);
void DistanceYourself(T* owner, float distance);
bool DispatchSplineToPosition(T* owner, float x, float y, float z, bool walk, bool cutPath, float maxTarget, bool forceDest, bool target = false);
private:
TimeTrackerSmall i_leashExtensionTimer;
std::unique_ptr<PathGenerator> i_path;
@@ -65,10 +78,12 @@ private:
bool i_recalculateTravel;
Optional<Position> _lastTargetPosition;
Optional<ChaseRange> const _range;
Optional<ChaseAngle> const _angle;
Optional<ChaseRange> _range;
Optional<ChaseAngle> _angle;
bool _movingTowards = true;
bool _mutualChase = true;
ChaseMovementMode m_currentMode;
};
template<class T>
+23 -12
View File
@@ -1126,7 +1126,7 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar
break;
}
ConditionList* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions;
std::shared_ptr<ConditionList> condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions;
// handle emergency case - try to use other provided targets if no conditions provided
if (targetType.GetCheckType() == TARGET_CHECK_ENTRY && (!condList || condList->empty()))
@@ -1221,7 +1221,7 @@ void Spell::SelectImplicitConeTargets(SpellEffIndex effIndex, SpellImplicitTarge
std::list<WorldObject*> targets;
SpellTargetObjectTypes objectType = targetType.GetObjectType();
SpellTargetCheckTypes selectionType = targetType.GetCheckType();
ConditionList* condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions;
std::shared_ptr<ConditionList> condList = m_spellInfo->Effects[effIndex].ImplicitTargetConditions;
float coneAngle = M_PI / 2;
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
@@ -2119,7 +2119,7 @@ void Spell::SelectEffectTypeImplicitTargets(uint8 effIndex)
}
}
uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList* condList)
uint32 Spell::GetSearcherTypeMask(SpellTargetObjectTypes objType, std::shared_ptr<ConditionList> condList)
{
// this function selects which containers need to be searched for spell target
uint32 retMask = GRID_MAP_TYPE_MASK_ALL;
@@ -2164,7 +2164,9 @@ void Spell::SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* refere
Cell::VisitObjects(pos->GetPositionX(), pos->GetPositionY(), referer->GetMap(), searcher, radius);
}
WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList)
WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objectType,
SpellTargetCheckTypes selectionType,
std::shared_ptr<ConditionList> condList)
{
WorldObject* target = nullptr;
uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList);
@@ -2176,7 +2178,11 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec
return target;
}
void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList)
void Spell::SearchAreaTargets(std::list<WorldObject*> &targets, float range,
Position const *position, Unit *referer,
SpellTargetObjectTypes objectType,
SpellTargetCheckTypes selectionType,
std::shared_ptr<ConditionList> condList)
{
uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList);
if (!containerTypeMask)
@@ -2186,7 +2192,11 @@ void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Pos
SearchTargets<Acore::WorldObjectListSearcher<Acore::WorldObjectSpellAreaTargetCheck> > (searcher, containerTypeMask, m_caster, position, range);
}
void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellTargetSelectionCategories /*selectCategory*/, ConditionList* condList, bool isChainHeal)
void Spell::SearchChainTargets(
std::list<WorldObject*> &targets, uint32 chainTargets, WorldObject *target,
SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType,
SpellTargetSelectionCategories /*selectCategory*/,
std::shared_ptr<ConditionList> condList, bool isChainHeal)
{
// max dist for jump target selection
float jumpRadius = 0.0f;
@@ -2223,7 +2233,8 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar
WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target;
std::list<WorldObject*> tempTargets;
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList);
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster,
objectType, selectType, condList);
tempTargets.remove(target);
// remove targets which are always invalid for chain spells
@@ -8984,7 +8995,7 @@ namespace Acore
{
WorldObjectSpellTargetCheck::WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo,
SpellTargetCheckTypes selectionType, ConditionList* condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo),
SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList) : _caster(caster), _referer(referer), _spellInfo(spellInfo),
_targetSelectionType(selectionType), _condList(condList)
{
if (condList)
@@ -9067,7 +9078,7 @@ namespace Acore
}
WorldObjectSpellNearbyTargetCheck::WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo,
SpellTargetCheckTypes selectionType, ConditionList* condList)
SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList)
: WorldObjectSpellTargetCheck(caster, caster, spellInfo, selectionType, condList), _range(range), _position(caster)
{
}
@@ -9084,7 +9095,7 @@ namespace Acore
}
WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList)
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList)
: WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position)
{
}
@@ -9104,7 +9115,7 @@ namespace Acore
}
WorldObjectSpellConeTargetCheck::WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster,
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList)
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList)
: WorldObjectSpellAreaTargetCheck(range, caster, caster, caster, spellInfo, selectionType, condList), _coneAngle(coneAngle)
{
}
@@ -9130,7 +9141,7 @@ namespace Acore
}
WorldObjectSpellTrajTargetCheck::WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster,
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList)
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList)
: WorldObjectSpellAreaTargetCheck(range, position, caster, caster, spellInfo, selectionType, condList)
{
}
+23 -10
View File
@@ -445,12 +445,25 @@ public:
void SelectEffectTypeImplicitTargets(uint8 effIndex);
uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType, ConditionList* condList);
uint32 GetSearcherTypeMask(SpellTargetObjectTypes objType,
std::shared_ptr<ConditionList> condList);
template<class SEARCHER> void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius);
WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList = nullptr);
void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList);
void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellTargetSelectionCategories selectCategory, ConditionList* condList, bool isChainHeal);
WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType,
SpellTargetCheckTypes selectionType,
std::shared_ptr<ConditionList> condList = nullptr);
void SearchAreaTargets(std::list<WorldObject *> &targets, float range,
Position const *position, Unit *referer,
SpellTargetObjectTypes objectType,
SpellTargetCheckTypes selectionType,
std::shared_ptr<ConditionList> condList);
void SearchChainTargets(std::list<WorldObject *> &targets,
uint32 chainTargets, WorldObject *target,
SpellTargetObjectTypes objectType,
SpellTargetCheckTypes selectType,
SpellTargetSelectionCategories selectCategory,
std::shared_ptr<ConditionList> condList,
bool isChainHeal);
SpellCastResult prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura = nullptr);
void cancel(bool bySelf = false);
@@ -801,10 +814,10 @@ namespace Acore
SpellInfo const* _spellInfo;
SpellTargetCheckTypes _targetSelectionType;
ConditionSourceInfo* _condSrcInfo;
ConditionList* _condList;
std::shared_ptr<ConditionList> _condList;
WorldObjectSpellTargetCheck(Unit* caster, Unit* referer, SpellInfo const* spellInfo,
SpellTargetCheckTypes selectionType, ConditionList* condList);
SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList);
~WorldObjectSpellTargetCheck();
bool operator()(WorldObject* target);
};
@@ -814,7 +827,7 @@ namespace Acore
float _range;
Position const* _position;
WorldObjectSpellNearbyTargetCheck(float range, Unit* caster, SpellInfo const* spellInfo,
SpellTargetCheckTypes selectionType, ConditionList* condList);
SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList);
bool operator()(WorldObject* target);
};
@@ -823,7 +836,7 @@ namespace Acore
float _range;
Position const* _position;
WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList);
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList);
bool operator()(WorldObject* target);
};
@@ -831,14 +844,14 @@ namespace Acore
{
float _coneAngle;
WorldObjectSpellConeTargetCheck(float coneAngle, float range, Unit* caster,
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList);
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList);
bool operator()(WorldObject* target);
};
struct WorldObjectSpellTrajTargetCheck : public WorldObjectSpellAreaTargetCheck
{
WorldObjectSpellTrajTargetCheck(float range, Position const* position, Unit* caster,
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList);
SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, std::shared_ptr<ConditionList> condList);
bool operator()(WorldObject* target);
};
}
-22
View File
@@ -863,11 +863,6 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry)
_requireCooldownInfo = false;
}
SpellInfo::~SpellInfo()
{
_UnloadImplicitTargetConditionLists();
}
uint32 SpellInfo::GetCategory() const
{
return CategoryEntry ? CategoryEntry->Id : 0;
@@ -2873,23 +2868,6 @@ bool SpellInfo::_IsPositiveTarget(uint32 targetA, uint32 targetB)
return true;
}
void SpellInfo::_UnloadImplicitTargetConditionLists()
{
// find the same instances of ConditionList and delete them.
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
ConditionList* cur = Effects[i].ImplicitTargetConditions;
if (!cur)
continue;
for (uint8 j = i; j < MAX_SPELL_EFFECTS; ++j)
{
if (Effects[j].ImplicitTargetConditions == cur)
Effects[j].ImplicitTargetConditions = nullptr;
}
delete cur;
}
}
bool SpellInfo::CheckElixirStacking(Unit const* caster) const
{
if (!caster)
+3 -3
View File
@@ -39,6 +39,7 @@ struct SpellRadiusEntry;
struct SpellEntry;
struct SpellCastTimesEntry;
struct Condition;
typedef std::list<Condition*> ConditionList;
enum SpellCastTargetFlags
{
@@ -270,12 +271,12 @@ public:
uint32 ItemType;
uint32 TriggerSpell;
flag96 SpellClassMask;
std::list<Condition*>* ImplicitTargetConditions;
std::shared_ptr<ConditionList> ImplicitTargetConditions;
SpellEffectInfo() : _spellInfo(nullptr), _effIndex(0), Effect(0), ApplyAuraName(0), Amplitude(0), DieSides(0),
RealPointsPerLevel(0), BasePoints(0), PointsPerComboPoint(0), ValueMultiplier(0), DamageMultiplier(0),
BonusMultiplier(0), MiscValue(0), MiscValueB(0), Mechanic(MECHANIC_NONE), RadiusEntry(nullptr), ChainTarget(0),
ItemType(0), TriggerSpell(0), ImplicitTargetConditions(nullptr) {}
ItemType(0), TriggerSpell(0), ImplicitTargetConditions() {}
SpellEffectInfo(SpellEntry const* spellEntry, SpellInfo const* spellInfo, uint8 effIndex);
bool IsEffect() const;
@@ -403,7 +404,6 @@ public:
bool _requireCooldownInfo;
SpellInfo(SpellEntry const* spellEntry);
~SpellInfo();
uint32 GetCategory() const;
bool HasEffect(SpellEffects effect) const;
-9
View File
@@ -2766,15 +2766,6 @@ void SpellMgr::UnloadSpellInfoStore()
mSpellInfoMap.clear();
}
void SpellMgr::UnloadSpellInfoImplicitTargetConditionLists()
{
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
{
if (mSpellInfoMap[i])
mSpellInfoMap[i]->_UnloadImplicitTargetConditionLists();
}
}
void SpellMgr::LoadSpellSpecificAndAuraState()
{
uint32 oldMSTime = getMSTime();
-1
View File
@@ -786,7 +786,6 @@ public:
void LoadSpellInfoStore();
void LoadSpellCooldownOverrides();
void UnloadSpellInfoStore();
void UnloadSpellInfoImplicitTargetConditionLists();
void LoadSpellInfoCustomAttributes();
void LoadSpellInfoCorrections();
void LoadSpellSpecificAndAuraState();
@@ -887,6 +887,7 @@ static LocationXYZO ThrallSpawn[] =
// Valimathras Trashspawn
{ 1325.059f, 332.652f, -65.027f, 2.186f },
{ 1270.474f, 350.982f, -65.027f, 0.034f },
{ 1805.753f, 285.499f, 70.399f, 4.691f }
};
#define GOSSIP_WRYNN "Reporting for duty, your majesty! Let the assault begin!"
@@ -2382,7 +2383,8 @@ public:
switch (summoned->GetEntry())
{
case NPC_BLIGHT_ABBERATION:
summoned->AI()->AttackStart(me);
summoned->SetHomePosition(me->GetPosition());
summoned->AddThreat(me, 100.0f);
break;
case NPC_WARSONG_BATTLEGUARD:
summoned->ApplySpellImmune(0, IMMUNITY_ID, SPELL_SYLVANAS_BUFF, true);
@@ -2424,6 +2426,10 @@ public:
me->AddThreat(summoned, 100.0f);
summoned->AI()->AttackStart(me);
break;
case NPC_KHANOK:
summoned->SetHomePosition(me->GetPosition());
summoned->AddThreat(me, 100.0f);
summoned->AI()->AttackStart(me);
default:
break;
}
@@ -2580,10 +2586,8 @@ public:
// Bossspawn 1
if (Creature* temp = me->SummonCreature(NPC_BLIGHT_ABBERATION, ThrallSpawn[28].x, ThrallSpawn[28].y, ThrallSpawn[28].z, ThrallSpawn[28].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 900 * IN_MILLISECONDS))
{
temp->GetMotionMaster()->MoveJump(ThrallSpawn[62].x, ThrallSpawn[62].y, ThrallSpawn[62].z, 10.0f, 20.0f, 0);
temp->AddThreat(me, 100.0f);
me->AddThreat(temp, 100.0f);
temp->AI()->AttackStart(me);
me->AI()->AttackStart(temp);
}
break;
case 6:
@@ -2747,7 +2751,11 @@ public:
break;
// NPC_KHANOK - Inner Sunktum Spawn Middle
case 17:
me->SummonCreature(NPC_KHANOK, ThrallSpawn[68].x, ThrallSpawn[68].y, ThrallSpawn[68].z, TEMPSUMMON_DEAD_DESPAWN);
if (Creature* temp = me->SummonCreature(NPC_KHANOK, ThrallSpawn[68].x, ThrallSpawn[68].y, ThrallSpawn[68].z, TEMPSUMMON_DEAD_DESPAWN))
{
me->AddThreat(temp, 100.0f);
me->AI()->AttackStart(temp);
}
break;
case 18:
if (Creature* temp = me->SummonCreature(NPC_WARSONG_BATTLEGUARD, ThrallSpawn[69].x, ThrallSpawn[69].y, ThrallSpawn[69].z, ThrallSpawn[69].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 240 * IN_MILLISECONDS))
@@ -3167,9 +3175,8 @@ public:
me->GetCreatureListWithEntryInGrid(HostileList, NPC_DOCTOR_H, 1000.0f);
me->GetCreatureListWithEntryInGrid(HostileList, NPC_CHEMIST_H, 1000.0f);
me->GetCreatureListWithEntryInGrid(HostileList, NPC_BLIGHT_SLINGER, 1000.0f);
if (!HostileList.empty())
for (std::list<Creature*>::iterator itr = HostileList.begin(); itr != HostileList.end(); itr++)
(*itr)->DespawnOrUnsummon();
for (auto& creature : HostileList)
creature->DespawnOrUnsummon();
for (uint8 i = 0; i < 7; ++i)
me->SummonGameObject(GO_HORDE_BANNER, ThrallSpawn[i + 37].x, ThrallSpawn[i + 37].y, ThrallSpawn[i + 37].z, ThrallSpawn[i + 37].o, 0.0f, 0.0f, 0.0f, 0.0f, 120 * IN_MILLISECONDS);
SpawnWave(6);
@@ -3620,9 +3627,11 @@ public:
valimathras->RemoveAura(SPELL_AURA_OF_VARIMATHRAS);
valimathras->RemoveAura(SPELL_OPENING_LEGION_PORTALS);
valimathras->AI()->Talk(SAY_VALIMATHRAS_ATTACK);
valimathras->SetHomePosition(me->GetPosition());
valimathras->AddThreat(me, 100.0f);
me->AddThreat(valimathras, 100.0f);
valimathras->AI()->AttackStart(me);
me->AI()->AttackStart(valimathras);
}
bStepping = false;
JumpToNextStep(0 * IN_MILLISECONDS);
@@ -3945,7 +3954,6 @@ public:
{
me->SetCorpseDelay(1);
me->SetRespawnTime(1);
_events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20s);
_events.ScheduleEvent(EVENT_BLACK_ARROW, 15s);
_events.ScheduleEvent(EVENT_SHOOT, 5s);
_events.ScheduleEvent(EVENT_MULTI_SHOT, 6s);
@@ -3974,10 +3982,6 @@ public:
{
switch (eventId)
{
case EVENT_SUMMON_SKELETON:
DoCast(me, SPELL_SUMMON_SKELETON);
_events.ScheduleEvent(EVENT_SUMMON_SKELETON, 20s, 30s);
break;
case EVENT_BLACK_ARROW:
if (Unit* victim = me->GetVictim())
DoCast(victim, SPELL_BLACK_ARROW);
+49 -4
View File
@@ -3147,6 +3147,7 @@ enum BrewfestMountTransformation
SPELL_MOUNT_KODO_60 = 49378,
SPELL_BREWFEST_MOUNT_TRANSFORM = 49357,
SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE = 52845,
SPELL_FRESH_DWARVEN_HOPS = 66050,
};
class spell_item_brewfest_mount_transformation : public SpellScript
@@ -3169,25 +3170,26 @@ class spell_item_brewfest_mount_transformation : public SpellScript
Player* caster = GetCaster()->ToPlayer();
if (!caster)
{
return;
}
if (caster->HasMountedAura())
{
float speed = caster->GetSpeedRate(MOVE_RUN);
caster->RemoveAurasByType(SPELL_AURA_MOUNTED);
uint32 spell_id;
switch (GetSpellInfo()->Id)
{
case SPELL_BREWFEST_MOUNT_TRANSFORM:
if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f)
if (speed >= 2.0f)
spell_id = caster->GetTeamId() == TEAM_ALLIANCE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100;
else
spell_id = caster->GetTeamId() == TEAM_ALLIANCE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60;
break;
case SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE:
if (caster->GetSpeedRate(MOVE_RUN) >= 2.0f)
if (speed >= 2.0f)
spell_id = caster->GetTeamId() == TEAM_HORDE ? SPELL_MOUNT_RAM_100 : SPELL_MOUNT_KODO_100;
else
spell_id = caster->GetTeamId() == TEAM_HORDE ? SPELL_MOUNT_RAM_60 : SPELL_MOUNT_KODO_60;
@@ -3205,6 +3207,48 @@ class spell_item_brewfest_mount_transformation : public SpellScript
}
};
class spell_item_brewfest_hops : public AuraScript
{
PrepareAuraScript(spell_item_brewfest_hops);
bool Validate(SpellInfo const* /*spell*/) override
{
return ValidateSpellInfo(
{
SPELL_BREWFEST_MOUNT_TRANSFORM,
SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE,
});
}
bool Load() override
{
_spell_id = GetSpellInfo()->Id == SPELL_FRESH_DWARVEN_HOPS ? SPELL_BREWFEST_MOUNT_TRANSFORM_REVERSE : SPELL_BREWFEST_MOUNT_TRANSFORM;
return true;
}
void CalcPeriodic(AuraEffect const* /*effect*/, bool& isPeriodic, int32& amplitude)
{
isPeriodic = true;
amplitude = 3 * IN_MILLISECONDS;
}
void Update(AuraEffect* /*effect*/)
{
Unit* caster = GetCaster();
if (!caster || caster->HasAnyAuras(SPELL_MOUNT_RAM_100, SPELL_MOUNT_RAM_60, SPELL_MOUNT_KODO_100, SPELL_MOUNT_KODO_60))
return;
caster->CastSpell(caster, _spell_id, true);
}
void Register() override
{
DoEffectCalcPeriodic += AuraEffectCalcPeriodicFn(spell_item_brewfest_hops::CalcPeriodic, EFFECT_0, SPELL_AURA_DUMMY);
OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_item_brewfest_hops::Update, EFFECT_0, SPELL_AURA_DUMMY);
}
private:
uint32 _spell_id;
};
enum NitroBoots
{
SPELL_NITRO_BOOTS_SUCCESS = 54861,
@@ -4338,4 +4382,5 @@ void AddSC_item_spell_scripts()
RegisterSpellScript(spell_item_spell_reflectors);
RegisterSpellScript(spell_item_multiphase_goggles);
RegisterSpellScript(spell_item_bloodsail_admiral_hat);
RegisterSpellScript(spell_item_brewfest_hops);
}