Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
ZhengPeiRu21
2022-04-16 21:41:45 -06:00
committed by GitHub
240 changed files with 14385 additions and 4083 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ void GuardAI::Reset()
me->CastSpell(me, 18950 /*SPELL_INVISIBILITY_AND_STEALTH_DETECTION*/, true);
}
void GuardAI::EnterEvadeMode()
void GuardAI::EnterEvadeMode(EvadeReason /*why*/)
{
if (!me->IsAlive())
{
+1 -1
View File
@@ -30,7 +30,7 @@ public:
static int Permissible(Creature const* creature);
void Reset() override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason /*why*/) override;
void JustDied(Unit* killer) override;
};
#endif
+4 -4
View File
@@ -26,7 +26,7 @@ NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(
void PassiveAI::UpdateAI(uint32)
{
if (me->IsInCombat() && me->getAttackers().empty())
EnterEvadeMode();
EnterEvadeMode(EVADE_REASON_NO_HOSTILES);
}
void PossessedAI::AttackStart(Unit* target)
@@ -66,11 +66,11 @@ void CritterAI::DamageTaken(Unit*, uint32&, DamageEffectType, SpellSchoolMask)
_combatTimer = 1;
}
void CritterAI::EnterEvadeMode()
void CritterAI::EnterEvadeMode(EvadeReason why)
{
if (me->HasUnitState(UNIT_STATE_FLEEING))
me->SetControlled(false, UNIT_STATE_FLEEING);
CreatureAI::EnterEvadeMode();
CreatureAI::EnterEvadeMode(why);
_combatTimer = 0;
}
@@ -80,7 +80,7 @@ void CritterAI::UpdateAI(uint32 diff)
{
_combatTimer += diff;
if (_combatTimer >= 5000)
EnterEvadeMode();
EnterEvadeMode(EVADE_REASON_OTHER);
}
}
+3 -3
View File
@@ -41,7 +41,7 @@ public:
void MoveInLineOfSight(Unit*) override {}
void AttackStart(Unit* target) override;
void UpdateAI(uint32) override;
void EnterEvadeMode() override {}
void EnterEvadeMode(EvadeReason /*why*/) override {}
void JustDied(Unit*) override;
void KilledUnit(Unit* victim) override;
@@ -57,7 +57,7 @@ public:
void MoveInLineOfSight(Unit*) override {}
void AttackStart(Unit*) override {}
void UpdateAI(uint32) override {}
void EnterEvadeMode() override {}
void EnterEvadeMode(EvadeReason /*why*/) override {}
void OnCharmed(bool /*apply*/) override {}
static int Permissible(Creature const*) { return PERMIT_BASE_IDLE; }
@@ -69,7 +69,7 @@ public:
explicit CritterAI(Creature* c) : PassiveAI(c) { _combatTimer = 0; }
void DamageTaken(Unit* /*done_by*/, uint32& /*damage*/, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask) override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason why) override;
void UpdateAI(uint32) override;
// Xinef: Added
+2 -1
View File
@@ -64,7 +64,8 @@ public:
//
void MoveInLineOfSight(Unit* /*who*/) override {} // CreatureAI interferes with returning pets
void MoveInLineOfSight_Safe(Unit* /*who*/) {} // CreatureAI interferes with returning pets
void EnterEvadeMode() override {} // For fleeing, pets don't use this type of Evade mechanic
void EnterEvadeMode(EvadeReason /*why*/) override {} // For fleeing, pets don't use this type of Evade mechanic
void SpellHit(Unit* caster, SpellInfo const* spellInfo) override;
void PetStopAttack() override;
+1 -1
View File
@@ -50,7 +50,7 @@ void TotemAI::MoveInLineOfSight(Unit* /*who*/)
{
}
void TotemAI::EnterEvadeMode()
void TotemAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->CombatStop(true);
}
+3 -1
View File
@@ -31,8 +31,10 @@ public:
void MoveInLineOfSight(Unit* who) override;
void AttackStart(Unit* victim) override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason /*why*/) override;
void SpellHit(Unit* /*caster*/, SpellInfo const* /*spellInfo*/) override;
void DoAction(int32 param) override;
void UpdateAI(uint32 diff) override;
+43 -3
View File
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AreaBoundary.h"
#include "CreatureAI.h"
#include "Creature.h"
#include "CreatureAIImpl.h"
@@ -24,6 +25,7 @@
#include "MapReference.h"
#include "Player.h"
#include "Vehicle.h"
#include "Language.h"
class PhasedRespawn : public BasicEvent
{
@@ -196,9 +198,9 @@ void CreatureAI::TriggerAlert(Unit const* who) const
me->GetMotionMaster()->MoveDistract(5 * IN_MILLISECONDS);
}
void CreatureAI::EnterEvadeMode()
void CreatureAI::EnterEvadeMode(EvadeReason why)
{
if (!_EnterEvadeMode())
if (!_EnterEvadeMode(why))
return;
LOG_DEBUG("entities.unit", "Creature {} enters evade mode.", me->GetEntry());
@@ -290,7 +292,7 @@ bool CreatureAI::UpdateVictim()
return true;
}
bool CreatureAI::_EnterEvadeMode()
bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
{
if (!me->IsAlive())
{
@@ -357,6 +359,44 @@ void CreatureAI::MoveBackwardsChecks() {
me->GetMotionMaster()->MoveBackwards(victim, moveDist);
}
bool CreatureAI::IsInBoundary(Position const* who) const
{
if (!_boundary)
return true;
if (!who)
who = me;
return (CreatureAI::IsInBounds(*_boundary, who) != _negateBoundary);
}
bool CreatureAI::IsInBounds(CreatureBoundary const& boundary, Position const* pos)
{
for (AreaBoundary const* areaBoundary : boundary)
if (!areaBoundary->IsWithinBoundary(pos))
return false;
return true;
}
bool CreatureAI::CheckInRoom()
{
if (IsInBoundary())
return true;
else
{
EnterEvadeMode(EVADE_REASON_BOUNDARY);
return false;
}
}
void CreatureAI::SetBoundary(CreatureBoundary const* boundary, bool negateBoundaries /*= false*/)
{
_boundary = boundary;
_negateBoundary = negateBoundaries;
me->DoImmediateBoundaryCheck();
}
Creature* CreatureAI::DoSummon(uint32 entry, const Position& pos, uint32 despawnTime, TempSummonType summonType)
{
return me->SummonCreature(entry, pos, summonType, despawnTime);
+26 -4
View File
@@ -18,6 +18,7 @@
#ifndef ACORE_CREATUREAI_H
#define ACORE_CREATUREAI_H
#include "AreaBoundary.h"
#include "Common.h"
#include "Creature.h"
#include "UnitAI.h"
@@ -29,6 +30,8 @@ class Creature;
class Player;
class SpellInfo;
typedef std::vector<AreaBoundary const*> CreatureBoundary;
#define TIME_INTERVAL_LOOK 5000
#define VISIBILITY_RANGE 10000
@@ -78,10 +81,19 @@ protected:
Creature* DoSummon(uint32 entry, Position const& pos, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummon(uint32 entry, WorldObject* obj, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
public:
// EnumUtils: DESCRIBE THIS
enum EvadeReason
{
EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty
EVADE_REASON_BOUNDARY, // the creature has moved outside its evade boundary
EVADE_REASON_SEQUENCE_BREAK, // this is a boss and the pre-requisite encounters for engaging it are not defeated yet
EVADE_REASON_OTHER
};
void Talk(uint8 id, WorldObject const* whisperTarget = nullptr);
explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), m_MoveInLineOfSight_locked(false) {}
explicit CreatureAI(Creature* creature) : UnitAI(creature), me(creature), _boundary(nullptr), _negateBoundary(false), m_MoveInLineOfSight_locked(false) { }
~CreatureAI() override {}
@@ -100,7 +112,7 @@ public:
virtual bool CanRespawn() { return true; }
// Called for reaction at stopping attack at no attackers or targets
virtual void EnterEvadeMode();
virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
// Called for reaction at enter to combat if not in combat yet (enemy can be nullptr)
virtual void EnterCombat(Unit* /*victim*/) {}
@@ -187,10 +199,20 @@ public:
virtual void PetStopAttack() { }
// boundary system methods
virtual bool CheckInRoom();
CreatureBoundary const* GetBoundary() const { return _boundary; }
void SetBoundary(CreatureBoundary const* boundary, bool negativeBoundaries = false);
static bool IsInBounds(CreatureBoundary const& boundary, Position const* who);
bool IsInBoundary(Position const* who = nullptr) const;
protected:
virtual void MoveInLineOfSight(Unit* /*who*/);
bool _EnterEvadeMode();
bool _EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER);
CreatureBoundary const* _boundary;
bool _negateBoundary;
private:
bool m_MoveInLineOfSight_locked;
@@ -15,6 +15,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AreaBoundary.h"
#include "ScriptedCreature.h"
#include "Cell.h"
#include "CellImpl.h"
@@ -171,7 +172,6 @@ bool SummonList::IsAnyCreatureInCombat() const
ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature),
me(creature),
IsFleeing(false),
_evadeCheckCooldown(2500),
_isCombatMovementAllowed(true)
{
_isHeroic = me->GetMap()->IsHeroic();
@@ -489,23 +489,6 @@ enum eNPCs
NPC_FREYA = 32906,
};
bool ScriptedAI::EnterEvadeIfOutOfCombatArea()
{
if (me->IsInEvadeMode() || !me->IsInCombat())
return false;
if (_evadeCheckCooldown == GameTime::GetGameTime().count())
return false;
_evadeCheckCooldown = GameTime::GetGameTime().count();
if (!CheckEvadeIfOutOfCombatArea())
return false;
EnterEvadeMode();
return true;
}
Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura, bool mustBeInLOS) const
{
Map::PlayerList const& pList = me->GetMap()->GetPlayers();
@@ -531,9 +514,10 @@ Player* ScriptedAI::SelectTargetFromPlayerList(float maxdist, uint32 excludeAura
BossAI::BossAI(Creature* creature, uint32 bossId) : ScriptedAI(creature),
instance(creature->GetInstanceScript()),
summons(creature),
_boundary(instance ? instance->GetBossBoundary(bossId) : nullptr),
_bossId(bossId)
{
if (instance)
SetBoundary(instance->GetBossBoundary(bossId));
}
void BossAI::_Reset()
@@ -583,59 +567,10 @@ void BossAI::TeleportCheaters()
ThreatContainer::StorageType threatList = me->getThreatMgr().getThreatList();
for (ThreatContainer::StorageType::const_iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
if (Unit* target = (*itr)->getTarget())
if (target->GetTypeId() == TYPEID_PLAYER && !CheckBoundary(target))
if (target->GetTypeId() == TYPEID_PLAYER && !IsInBoundary(target))
target->NearTeleportTo(x, y, z, 0);
}
bool BossAI::CheckBoundary(Unit* who)
{
if (!GetBoundary() || !who)
return true;
for (BossBoundaryMap::const_iterator itr = GetBoundary()->begin(); itr != GetBoundary()->end(); ++itr)
{
switch (itr->first)
{
case BOUNDARY_N:
if (who->GetPositionX() > itr->second)
return false;
break;
case BOUNDARY_S:
if (who->GetPositionX() < itr->second)
return false;
break;
case BOUNDARY_E:
if (who->GetPositionY() < itr->second)
return false;
break;
case BOUNDARY_W:
if (who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_NW:
if (who->GetPositionX() + who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_SE:
if (who->GetPositionX() + who->GetPositionY() < itr->second)
return false;
break;
case BOUNDARY_NE:
if (who->GetPositionX() - who->GetPositionY() > itr->second)
return false;
break;
case BOUNDARY_SW:
if (who->GetPositionX() - who->GetPositionY() < itr->second)
return false;
break;
default:
break;
}
}
return true;
}
void BossAI::JustSummoned(Creature* summon)
{
summons.Summon(summon);
@@ -344,7 +344,6 @@ struct ScriptedAI : public CreatureAI
void SetCombatMovement(bool allowMovement);
bool IsCombatMovementAllowed() const { return _isCombatMovementAllowed; }
bool EnterEvadeIfOutOfCombatArea();
virtual bool CheckEvadeIfOutOfCombatArea() const { return false; }
// return true for heroic mode. i.e.
@@ -416,7 +415,6 @@ struct ScriptedAI : public CreatureAI
private:
Difficulty _difficulty;
uint32 _evadeCheckCooldown;
bool _isCombatMovementAllowed;
bool _isHeroic;
};
@@ -428,7 +426,6 @@ public:
~BossAI() override {}
InstanceScript* const instance;
BossBoundaryMap const* GetBoundary() const { return _boundary; }
void JustSummoned(Creature* summon) override;
void SummonedCreatureDespawn(Creature* summon) override;
@@ -453,23 +450,12 @@ protected:
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
bool CheckInRoom()
{
if (CheckBoundary(me))
return true;
EnterEvadeMode();
return false;
}
bool CheckBoundary(Unit* who);
void TeleportCheaters();
EventMap events;
SummonList summons;
private:
BossBoundaryMap const* const _boundary;
uint32 const _bossId;
};
@@ -198,7 +198,7 @@ void npc_escortAI::ReturnToLastPoint()
me->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
}
void npc_escortAI::EnterEvadeMode()
void npc_escortAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
me->DeleteThreatList();
@@ -66,7 +66,7 @@ public:
void ReturnToLastPoint();
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
void UpdateAI(uint32 diff) override; //the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(uint32 diff); //used when it's needed to add code in update (abilities, scripted events, etc)
@@ -150,7 +150,7 @@ void FollowerAI::JustRespawned()
Reset();
}
void FollowerAI::EnterEvadeMode()
void FollowerAI::EnterEvadeMode(EvadeReason /*why*/)
{
me->RemoveAllAuras();
me->DeleteThreatList();
@@ -46,7 +46,7 @@ public:
void MoveInLineOfSight(Unit*) override;
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason /*why*/ = EVADE_REASON_OTHER) override;
void JustDied(Unit*) override;
+1 -1
View File
@@ -618,7 +618,7 @@ void SmartAI::MovementInform(uint32 MovementType, uint32 Data)
MovepointReached(Data);
}
void SmartAI::EnterEvadeMode()
void SmartAI::EnterEvadeMode(EvadeReason /*why*/)
{
// xinef: fixes strange jumps when charming SmartAI npc
if (!me->IsAlive() || me->IsInEvadeMode())
+1 -1
View File
@@ -80,7 +80,7 @@ public:
void EnterCombat(Unit* enemy) override;
// Called for reaction at stopping attack at no attackers or targets
void EnterEvadeMode() override;
void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER) override;
// Called when the creature is killed
void JustDied(Unit* killer) override;
+73 -15
View File
@@ -105,7 +105,7 @@ void SmartScript::ProcessEventsFor(SMART_EVENT e, Unit* unit, uint32 var0, uint3
if (eventType == SMART_EVENT_LINK)//special handling
continue;
if (eventType == e/* && (!(*i).event.event_phase_mask || IsInPhase((*i).event.event_phase_mask)) && !((*i).event.event_flags & SMART_EVENT_FLAG_NOT_REPEATABLE && (*i).runOnce)*/)
if (eventType == e)
{
ConditionList conds = sConditionMgr->GetConditionsForSmartEvent((*i).entryOrGuid, (*i).event_id, (*i).source_type);
ConditionSourceInfo info = ConditionSourceInfo(unit, GetBaseObject(), me ? me->GetVictim() : nullptr);
@@ -1496,6 +1496,8 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
unitTarget->AttackStop();
}
}
delete targets;
break;
}
case SMART_ACTION_SUMMON_CREATURE:
@@ -1584,7 +1586,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
z += e.target.z;
o += e.target.o;
if (!e.action.summonGO.targetsummon)
GetBaseObject()->SummonGameObject(e.action.summonGO.entry, x, y, z, o, 0, 0, 0, 0, e.action.summonGO.despawnTime);
GetBaseObject()->SummonGameObject(e.action.summonGO.entry, x, y, z, o, 0, 0, 0, 0, e.action.summonGO.despawnTime, GOSummonType(e.action.summonGO.summonType));
else
(*itr)->SummonGameObject(e.action.summonGO.entry, GetBaseObject()->GetPositionX(), GetBaseObject()->GetPositionY(), GetBaseObject()->GetPositionZ(), GetBaseObject()->GetOrientation(), 0, 0, 0, 0, e.action.summonGO.despawnTime);
}
@@ -1595,7 +1597,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
if (e.GetTargetType() != SMART_TARGET_POSITION)
break;
GetBaseObject()->SummonGameObject(e.action.summonGO.entry, e.target.x, e.target.y, e.target.z, e.target.o, 0, 0, 0, 0, e.action.summonGO.despawnTime);
GetBaseObject()->SummonGameObject(e.action.summonGO.entry, e.target.x, e.target.y, e.target.z, e.target.o, 0, 0, 0, 0, e.action.summonGO.despawnTime, GOSummonType(e.action.summonGO.summonType));
break;
}
case SMART_ACTION_KILL_UNIT:
@@ -3561,48 +3563,48 @@ ObjectList* SmartScript::GetTargets(SmartScriptHolder const& e, Unit* invoker /*
case SMART_TARGET_HOSTILE_SECOND_AGGRO:
if (me)
{
if (e.target.hostilRandom.powerType)
if (e.target.hostileRandom.powerType)
{
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MaxThreat, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly)))
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MaxThreat, 1, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
l->push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MaxThreat, 1, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MaxThreat, 1, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
l->push_back(u);
}
break;
case SMART_TARGET_HOSTILE_LAST_AGGRO:
if (me)
{
if (e.target.hostilRandom.powerType)
if (e.target.hostileRandom.powerType)
{
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinThreat, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly)))
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinThreat, 0, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
l->push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinThreat, 0, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinThreat, 0, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
l->push_back(u);
}
break;
case SMART_TARGET_HOSTILE_RANDOM:
if (me)
{
if (e.target.hostilRandom.powerType)
if (e.target.hostileRandom.powerType)
{
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 0, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly)))
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 0, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
l->push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 0, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 0, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
l->push_back(u);
}
break;
case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP:
if (me)
{
if (e.target.hostilRandom.powerType)
if (e.target.hostileRandom.powerType)
{
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 1, PowerUsersSelector(me, Powers(e.target.hostilRandom.powerType - 1), (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly)))
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 1, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
l->push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 1, (float)e.target.hostilRandom.maxDist, e.target.hostilRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 1, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
l->push_back(u);
}
break;
@@ -4456,6 +4458,16 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
ProcessAction(e, unit, var0, var1);
break;
}
case SMART_EVENT_EVENT_PHASE_CHANGE:
{
if (!IsInPhase(e.event.eventPhaseChange.phasemask))
{
return;
}
ProcessAction(e, GetLastInvoker());
break;
}
case SMART_EVENT_GAME_EVENT_START:
case SMART_EVENT_GAME_EVENT_END:
{
@@ -5063,6 +5075,12 @@ void SmartScript::SetScript9(SmartScriptHolder& e, uint32 entry)
return;
}
// Do NOT allow to start a new actionlist if a previous one is already running. We need to always finish the current actionlist
if (!mTimedActionList.empty())
{
return;
}
mTimedActionList.clear();
mTimedActionList = sSmartScriptMgr->GetScript(entry, SMART_SCRIPT_TYPE_TIMED_ACTIONLIST);
if (mTimedActionList.empty())
@@ -5092,3 +5110,43 @@ Unit* SmartScript::GetLastInvoker(Unit* invoker)
return ObjectAccessor::GetUnit(*invoker, mLastInvoker);
return nullptr;
}
void SmartScript::IncPhase(uint32 p)
{
// protect phase from overflowing
SetPhase(std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p));
}
void SmartScript::DecPhase(uint32 p)
{
if (p >= mEventPhase)
{
SetPhase(0);
}
else
{
SetPhase(mEventPhase - p);
}
}
void SmartScript::SetPhase(uint32 p)
{
uint32 oldPhase = mEventPhase;
mEventPhase = p;
if (oldPhase != mEventPhase)
{
ProcessEventsFor(SMART_EVENT_EVENT_PHASE_CHANGE);
}
}
bool SmartScript::IsInPhase(uint32 p) const
{
if (mEventPhase == 0)
{
return false;
}
return ((1 << (mEventPhase - 1)) & p) != 0;
}
+4 -20
View File
@@ -258,26 +258,10 @@ public:
void SetPhaseReset(bool allow) { _allowPhaseReset = allow; }
private:
void IncPhase(uint32 p)
{
// Xinef: protect phase from overflowing
mEventPhase = std::min<uint32>(SMART_EVENT_PHASE_12, mEventPhase + p);
}
void DecPhase(uint32 p)
{
if (p >= mEventPhase)
mEventPhase = 0;
else
mEventPhase -= p;
}
bool IsInPhase(uint32 p) const
{
if (mEventPhase == 0)
return false;
return (1 << (mEventPhase - 1)) & p;
}
void SetPhase(uint32 p = 0) { mEventPhase = p; }
void IncPhase(uint32 p);
void DecPhase(uint32 p);
void SetPhase(uint32 p);
bool IsInPhase(uint32 p) const;
SmartAIEventList mEvents;
SmartAIEventList mInstallEvents;
@@ -410,7 +410,7 @@ bool SmartAIMgr::IsTargetValid(SmartScriptHolder const& e)
case SMART_TARGET_HOSTILE_LAST_AGGRO:
case SMART_TARGET_HOSTILE_RANDOM:
case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP:
AC_SAI_IS_BOOLEAN_VALID(e, e.target.hostilRandom.playerOnly);
AC_SAI_IS_BOOLEAN_VALID(e, e.target.hostileRandom.playerOnly);
break;
case SMART_TARGET_FARTHEST:
AC_SAI_IS_BOOLEAN_VALID(e, e.target.farthest.playerOnly);
@@ -528,7 +528,7 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
case SMART_EVENT_JUST_CREATED: return NO_PARAMS;
case SMART_EVENT_GOSSIP_HELLO: return sizeof(SmartEvent::gossipHello);
case SMART_EVENT_FOLLOW_COMPLETED: return NO_PARAMS;
// case SMART_EVENT_EVENT_PHASE_CHANGE: return sizeof(SmartEvent::eventPhaseChange);
case SMART_EVENT_EVENT_PHASE_CHANGE: return sizeof(SmartEvent::eventPhaseChange);
case SMART_EVENT_IS_BEHIND_TARGET: return sizeof(SmartEvent::behindTarget);
case SMART_EVENT_GAME_EVENT_START: return sizeof(SmartEvent::gameEvent);
case SMART_EVENT_GAME_EVENT_END: return sizeof(SmartEvent::gameEvent);
@@ -756,10 +756,10 @@ bool SmartAIMgr::CheckUnusedTargetParams(SmartScriptHolder const& e)
case SMART_TARGET_NONE: return NO_PARAMS;
case SMART_TARGET_SELF: return NO_PARAMS;
case SMART_TARGET_VICTIM: return NO_PARAMS;
case SMART_TARGET_HOSTILE_SECOND_AGGRO: return sizeof(SmartTarget::hostilRandom);
case SMART_TARGET_HOSTILE_LAST_AGGRO: return sizeof(SmartTarget::hostilRandom);
case SMART_TARGET_HOSTILE_RANDOM: return sizeof(SmartTarget::hostilRandom);
case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP: return sizeof(SmartTarget::hostilRandom);
case SMART_TARGET_HOSTILE_SECOND_AGGRO: return sizeof(SmartTarget::hostileRandom);
case SMART_TARGET_HOSTILE_LAST_AGGRO: return sizeof(SmartTarget::hostileRandom);
case SMART_TARGET_HOSTILE_RANDOM: return sizeof(SmartTarget::hostileRandom);
case SMART_TARGET_HOSTILE_RANDOM_NOT_TOP: return sizeof(SmartTarget::hostileRandom);
case SMART_TARGET_ACTION_INVOKER: return NO_PARAMS;
case SMART_TARGET_POSITION: return NO_PARAMS; //uses x,y,z,o
case SMART_TARGET_CREATURE_RANGE: return sizeof(SmartTarget::unitRange);
@@ -1076,6 +1076,27 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
}
break;
}
case SMART_EVENT_EVENT_PHASE_CHANGE:
{
if (!e.event.eventPhaseChange.phasemask)
{
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has no param set, event won't be executed!.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
return false;
}
if (e.event.eventPhaseChange.phasemask > SMART_EVENT_PHASE_ALL)
{
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} uses invalid phasemask {}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.eventPhaseChange.phasemask);
return false;
}
if (e.event.event_phase_mask && !(e.event.event_phase_mask & e.event.eventPhaseChange.phasemask))
{
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} uses event phasemask {} and incompatible event_param1 {}, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.event_phase_mask, e.event.eventPhaseChange.phasemask);
return false;
}
break;
}
case SMART_EVENT_IS_BEHIND_TARGET:
if (!IsMinMaxValid(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax))
return false;
@@ -170,7 +170,7 @@ enum SMART_EVENT
SMART_EVENT_JUST_CREATED = 63, // none
SMART_EVENT_GOSSIP_HELLO = 64, // event_para_1 (only) 0 = no filter set, always execute action, 1 = GossipHello only filter set, skip action if reportUse, 2 = reportUse only filter set, skip action if GossipHello
SMART_EVENT_FOLLOW_COMPLETED = 65, // none
SMART_EVENT_UNUSED_66 = 66, // UNUSED
SMART_EVENT_EVENT_PHASE_CHANGE = 66, // event phase mask (<= SMART_EVENT_PHASE_ALL)
SMART_EVENT_IS_BEHIND_TARGET = 67, // cooldownMin, CooldownMax
SMART_EVENT_GAME_EVENT_START = 68, // game_event.Entry
SMART_EVENT_GAME_EVENT_END = 69, // game_event.Entry
@@ -376,6 +376,17 @@ struct SmartEvent
uint32 action;
} gossip;
struct
{
uint32 spell;
uint32 effIndex;
} dummy;
struct
{
uint32 phasemask;
} eventPhaseChange;
struct
{
uint32 cooldownMin;
@@ -923,6 +934,7 @@ struct SmartAction
uint32 entry;
uint32 despawnTime;
uint32 targetsummon;
uint32 summonType;
} summonGO;
struct
@@ -1410,7 +1422,7 @@ struct SmartTarget
uint32 maxDist;
SAIBool playerOnly;
uint32 powerType;
} hostilRandom;
} hostileRandom;
struct
{
@@ -1677,7 +1689,7 @@ const uint32 SmartAIEventMask[SMART_EVENT_AC_END][2] =
{SMART_EVENT_JUST_CREATED, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_GOSSIP_HELLO, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_FOLLOW_COMPLETED, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_UNUSED_66, 0},
{SMART_EVENT_EVENT_PHASE_CHANGE, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_IS_BEHIND_TARGET, SMART_SCRIPT_TYPE_MASK_CREATURE },
{SMART_EVENT_GAME_EVENT_START, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
{SMART_EVENT_GAME_EVENT_END, SMART_SCRIPT_TYPE_MASK_CREATURE + SMART_SCRIPT_TYPE_MASK_GAMEOBJECT },
@@ -16,6 +16,7 @@
*/
#include "BattlegroundAV.h"
#include "CreatureTextMgr.h"
#include "Formulas.h"
#include "GameEventMgr.h"
#include "GameGraveyard.h"
@@ -391,9 +392,13 @@ void BattlegroundAV::PostUpdateImpl(uint32 diff)
for (uint8 i = 0; i <= 1; i++) //0=alliance, 1=horde
{
if (!m_CaptainAlive[i])
{
continue;
}
if (m_CaptainBuffTimer[i] > diff)
{
m_CaptainBuffTimer[i] -= diff;
}
else
{
if (i == 0)
@@ -401,14 +406,20 @@ void BattlegroundAV::PostUpdateImpl(uint32 diff)
CastSpellOnTeam(AV_BUFF_A_CAPTAIN, TEAM_ALLIANCE);
Creature* creature = GetBGCreature(AV_CPLACE_MAX + 61);
if (creature)
YellToAll(creature, LANG_BG_AV_A_CAPTAIN_BUFF, LANG_COMMON);
{
std::string creatureText = sCreatureTextMgr->GetLocalizedChatString(creature->GetEntry(), creature->getGender(), 0, 0, DEFAULT_LOCALE);
YellToAll(creature, creatureText.c_str(), LANG_COMMON);
}
}
else
{
CastSpellOnTeam(AV_BUFF_H_CAPTAIN, TEAM_HORDE);
Creature* creature = GetBGCreature(AV_CPLACE_MAX + 59); //TODO: make the captains a dynamic creature
if (creature)
YellToAll(creature, LANG_BG_AV_H_CAPTAIN_BUFF, LANG_ORCISH);
{
std::string creatureText = sCreatureTextMgr->GetLocalizedChatString(creature->GetEntry(), creature->getGender(), 2, 0, DEFAULT_LOCALE);
YellToAll(creature, creatureText.c_str(), LANG_ORCISH);
}
}
m_CaptainBuffTimer[i] = 120000 + urand(0, 4) * 60000; //as far as i could see, the buff is randomly so i make 2minutes (thats the duration of the buff itself) + 0-4minutes TODO get the right times
}
@@ -729,7 +740,10 @@ void BattlegroundAV::ChangeMineOwner(uint8 mine, TeamId teamId, bool initial)
if (mine == AV_SOUTH_MINE) //i think this gets called all the time
{
if (Creature* creature = GetBGCreature(AV_CPLACE_MINE_S_3))
YellToAll(creature, LANG_BG_AV_S_MINE_BOSS_CLAIMS, LANG_UNIVERSAL);
{
std::string creatureText = sCreatureTextMgr->GetLocalizedChatString(creature->GetEntry(), creature->getGender(), 0, 0, DEFAULT_LOCALE);
YellToAll(creature, creatureText.c_str(), LANG_UNIVERSAL);
}
}
}
}
@@ -21,10 +21,6 @@
#include "Battleground.h"
#include "BattlegroundScore.h"
#define LANG_BG_AV_A_CAPTAIN_BUFF "Begone. Uncouth scum! The Alliance shall prevail in Alterac Valley!"
#define LANG_BG_AV_H_CAPTAIN_BUFF "Now is the time to attack! For the Horde!"
#define LANG_BG_AV_S_MINE_BOSS_CLAIMS "Snivvle is here! Snivvle claims the Coldtooth Mine!"
#define BG_AV_CAPTIME 240000 //4:00
#define BG_AV_SNOWFALL_FIRSTCAP 300000 //5:00 but i also have seen 4:05
+18 -18
View File
@@ -472,7 +472,7 @@ void ThreatMgr::modifyThreatPercent(Unit* victim, int32 percent)
//============================================================
Unit* ThreatMgr::getHostilTarget()
Unit* ThreatMgr::getHostileTarget()
{
iThreatContainer.update();
HostileReference* nextVictim = iThreatContainer.selectNextVictim(GetOwner()->ToCreature(), getCurrentVictim());
@@ -546,49 +546,49 @@ void ThreatMgr::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusCh
{
threatRefStatusChangeEvent->setThreatMgr(this); // now we can set the threat manager
HostileReference* hostilRef = threatRefStatusChangeEvent->getReference();
HostileReference* hostileRef = threatRefStatusChangeEvent->getReference();
switch (threatRefStatusChangeEvent->getType())
{
case UEV_THREAT_REF_THREAT_CHANGE:
if ((getCurrentVictim() == hostilRef && threatRefStatusChangeEvent->getFValue() < 0.0f) ||
(getCurrentVictim() != hostilRef && threatRefStatusChangeEvent->getFValue() > 0.0f))
if ((getCurrentVictim() == hostileRef && threatRefStatusChangeEvent->getFValue() < 0.0f) ||
(getCurrentVictim() != hostileRef && threatRefStatusChangeEvent->getFValue() > 0.0f))
setDirty(true); // the order in the threat list might have changed
break;
case UEV_THREAT_REF_ONLINE_STATUS:
if (!hostilRef->isOnline())
if (!hostileRef->isOnline())
{
if (hostilRef == getCurrentVictim())
if (hostileRef == getCurrentVictim())
{
setCurrentVictim(nullptr);
setDirty(true);
}
if (GetOwner() && GetOwner()->IsInWorld())
if (Unit* target = ObjectAccessor::GetUnit(*GetOwner(), hostilRef->getUnitGuid()))
if (Unit* target = ObjectAccessor::GetUnit(*GetOwner(), hostileRef->getUnitGuid()))
if (GetOwner()->IsInMap(target))
GetOwner()->SendRemoveFromThreatListOpcode(hostilRef);
iThreatContainer.remove(hostilRef);
iThreatOfflineContainer.addReference(hostilRef);
GetOwner()->SendRemoveFromThreatListOpcode(hostileRef);
iThreatContainer.remove(hostileRef);
iThreatOfflineContainer.addReference(hostileRef);
}
else
{
if (getCurrentVictim() && hostilRef->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
if (getCurrentVictim() && hostileRef->getThreat() > (1.1f * getCurrentVictim()->getThreat()))
setDirty(true);
iThreatContainer.addReference(hostilRef);
iThreatOfflineContainer.remove(hostilRef);
iThreatContainer.addReference(hostileRef);
iThreatOfflineContainer.remove(hostileRef);
}
break;
case UEV_THREAT_REF_REMOVE_FROM_LIST:
if (hostilRef == getCurrentVictim())
if (hostileRef == getCurrentVictim())
{
setCurrentVictim(nullptr);
setDirty(true);
}
iOwner->SendRemoveFromThreatListOpcode(hostilRef);
if (hostilRef->isOnline())
iThreatContainer.remove(hostilRef);
iOwner->SendRemoveFromThreatListOpcode(hostileRef);
if (hostileRef->isOnline())
iThreatContainer.remove(hostileRef);
else
iThreatOfflineContainer.remove(hostilRef);
iThreatOfflineContainer.remove(hostileRef);
break;
}
}
+1 -1
View File
@@ -221,7 +221,7 @@ public:
[[nodiscard]] Unit* GetOwner() const { return iOwner; }
Unit* getHostilTarget();
Unit* getHostileTarget();
void tauntApply(Unit* taunter);
void tauntFadeOut(Unit* taunter);
+12 -1
View File
@@ -202,7 +202,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
}
Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(), m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipientGroup(0),
m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f),
m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_wanderDistance(0.0f), m_boundaryCheckTime(2500),
m_transportCheckTimer(1000), lootPickPocketRestoreTime(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE),
m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false),
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
@@ -689,6 +689,17 @@ void Creature::Update(uint32 diff)
SelectVictim();
}
// periodic check to see if the creature has passed an evade boundary
if (IsAIEnabled && !IsInEvadeMode() && IsInCombat())
{
if (diff >= m_boundaryCheckTime)
{
AI()->CheckInRoom();
m_boundaryCheckTime = 2500;
} else
m_boundaryCheckTime -= diff;
}
Unit* owner = GetCharmerOrOwner();
if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false))
{
@@ -285,6 +285,8 @@ public:
[[nodiscard]] float GetWanderDistance() const { return m_wanderDistance; }
void SetWanderDistance(float dist) { m_wanderDistance = dist; }
void DoImmediateBoundaryCheck() { m_boundaryCheckTime = 0; }
uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint32 lootingGroupLowGUID; // used to find group which is looting corpse
@@ -401,6 +403,7 @@ protected:
uint32 m_respawnDelay; // (secs) delay between corpse disappearance and respawning
uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
float m_wanderDistance;
uint32 m_boundaryCheckTime; // (msecs) remaining time for next evade boundary check
uint16 m_transportCheckTimer;
uint32 lootPickPocketRestoreTime;
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Define.h"
#include "Item.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>
+2 -2
View File
@@ -2304,7 +2304,7 @@ TempSummon* WorldObject::SummonCreature(uint32 entry, const Position& pos, TempS
return nullptr;
}
GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime, bool checkTransport)
GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime, bool checkTransport, GOSummonType summonType)
{
if (!IsInWorld())
return nullptr;
@@ -2330,7 +2330,7 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float
if (respawnTime)
go->SetSpellId(1);
if (GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT) //not sure how to handle this
if (GetTypeId() == TYPEID_PLAYER || (GetTypeId() == TYPEID_UNIT && summonType == GO_SUMMON_TIMED_OR_CORPSE_DESPAWN)) //not sure how to handle this
ToUnit()->AddGameObject(go);
else
go->SetSpawnedByDefault(false);
+7 -1
View File
@@ -65,6 +65,12 @@ enum NotifyFlags
NOTIFY_ALL = 0xFF
};
enum GOSummonType
{
GO_SUMMON_TIMED_OR_CORPSE_DESPAWN = 0, // despawns after a specified time OR when the summoner dies
GO_SUMMON_TIMED_DESPAWN = 1 // despawns after a specified time
};
class WorldPacket;
class UpdateData;
class ByteBuffer;
@@ -516,7 +522,7 @@ public:
TempSummon* SummonCreature(uint32 id, const Position& pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0, SummonPropertiesEntry const* properties = nullptr) const;
TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, SummonPropertiesEntry const* properties = nullptr);
GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime, bool checkTransport = true);
GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime, bool checkTransport = true, GOSummonType summonType = GO_SUMMON_TIMED_OR_CORPSE_DESPAWN);
Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, bool setLevel = false, CreatureAI * (*GetAI)(Creature*) = nullptr);
void SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list = nullptr);
+72 -18
View File
@@ -36,7 +36,9 @@
#include "PlayerSettings.h"
#include "PlayerTaxi.h"
#include "QuestDef.h"
#include "SpellAuras.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "TradeData.h"
#include "Unit.h"
#include "WorldSession.h"
@@ -2923,32 +2925,28 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& bas
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId);
if (!spellInfo)
{
return 0;
}
float totalmul = 1.0f;
int32 totalflat = 0;
// Drop charges for triggering spells instead of triggered ones
if (m_spellModTakingSpell)
spell = m_spellModTakingSpell;
for (auto mod : m_spellMods[op])
auto calculateSpellMod = [&](SpellModifier* mod)
{
// Charges can be set only for mods with auras
if (!mod->ownerAura)
ASSERT(mod->charges == 0);
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
continue;
// xinef: temporary pets cannot use charged mods of owner, needed for mirror image QQ they should use their own auras
if (temporaryPet && mod->charges != 0)
continue;
{
return;
}
if (mod->type == SPELLMOD_FLAT)
{
// xinef: do not allow to consume more than one 100% crit increasing spell
if (mod->op == SPELLMOD_CRITICAL_CHANCE && totalflat >= 100)
continue;
{
return;
}
totalflat += mod->value;
}
@@ -2956,32 +2954,88 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& bas
{
// skip percent mods for null basevalue (most important for spell mods with charges)
if (basevalue == T(0) || totalmul == 0.0f)
continue;
{
return;
}
// special case (skip > 10sec spell casts for instant cast setting)
if (mod->op == SPELLMOD_CASTING_TIME && basevalue >= T(10000) && mod->value <= -100)
continue;
{
return;
}
// xinef: special exception for surge of light, dont affect crit chance if previous mods were not applied
else if (mod->op == SPELLMOD_CRITICAL_CHANCE && spell && !HasSpellMod(mod, spell))
continue;
{
return;
}
// xinef: special case for backdraft gcd reduce with backlast time reduction, dont affect gcd if cast time was not applied
else if (mod->op == SPELLMOD_GLOBAL_COOLDOWN && spell && !HasSpellMod(mod, spell))
continue;
{
return;
}
// xinef: those two mods should be multiplicative (Glyph of Renew)
if (mod->op == SPELLMOD_DAMAGE || mod->op == SPELLMOD_DOT)
{
totalmul *= CalculatePct(1.0f, 100.0f + mod->value);
}
else
{
totalmul += CalculatePct(1.0f, mod->value);
}
}
DropModCharge(mod, spell);
};
// Drop charges for triggering spells instead of triggered ones
if (m_spellModTakingSpell)
{
spell = m_spellModTakingSpell;
}
SpellModifier* chargedMod = nullptr;
for (auto mod : m_spellMods[op])
{
// Charges can be set only for mods with auras
if (!mod->ownerAura)
{
ASSERT(!mod->charges);
}
if (!IsAffectedBySpellmod(spellInfo, mod, spell))
{
continue;
}
if (mod->ownerAura->IsUsingCharges())
{
if (!chargedMod || (chargedMod->ownerAura->GetSpellInfo()->SpellPriority < mod->ownerAura->GetSpellInfo()->SpellPriority))
{
chargedMod = mod;
}
continue;
}
calculateSpellMod(mod);
}
if (chargedMod)
{
calculateSpellMod(chargedMod);
}
float diff = 0.0f;
if (op == SPELLMOD_CASTING_TIME || op == SPELLMOD_DURATION)
{
diff = ((float)basevalue + totalflat) * (totalmul - 1.0f) + (float)totalflat;
}
else
{
diff = (float)basevalue * (totalmul - 1.0f) + (float)totalflat;
}
basevalue = T((float)basevalue + diff);
return T(diff);
}
@@ -1616,24 +1616,24 @@ QuestGiverStatus Player::GetQuestDialogStatus(Object* questgiver)
continue;
QuestStatus status = GetQuestStatus(questId);
if ((status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(questId)) || (quest->IsAutoComplete() && CanTakeQuest(quest, false)))
if (status == QUEST_STATUS_COMPLETE && !GetQuestRewardStatus(questId))
{
if (quest->IsRepeatable() || quest->IsDailyOrWeekly())
{
result2 = DIALOG_STATUS_REWARD_REP;
}
else
{
result2 = DIALOG_STATUS_REWARD;
}
result2 = DIALOG_STATUS_REWARD;
}
else if (status == QUEST_STATUS_INCOMPLETE)
{
result2 = DIALOG_STATUS_INCOMPLETE;
}
if (quest->IsAutoComplete() && CanTakeQuest(quest, false) && quest->IsRepeatable() && !quest->IsDailyOrWeekly())
{
result2 = DIALOG_STATUS_REWARD_REP;
}
if (result2 > result)
{
result = result2;
}
}
for (QuestRelations::const_iterator i = qr.first; i != qr.second; ++i)
+38 -3
View File
@@ -14056,7 +14056,7 @@ void Unit::TauntFadeOut(Unit* taunter)
if (m_ThreatMgr.isThreatListEmpty())
{
if (creature->IsAIEnabled)
creature->AI()->EnterEvadeMode();
creature->AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
return;
}
@@ -14094,7 +14094,7 @@ Unit* Creature::SelectVictim()
if (CanHaveThreatList())
{
if (!target && !m_ThreatMgr.isThreatListEmpty())
target = m_ThreatMgr.getHostilTarget();
target = m_ThreatMgr.getHostileTarget();
}
else if (!HasReactState(REACT_PASSIVE))
{
@@ -14142,7 +14142,7 @@ Unit* Creature::SelectVictim()
for (Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr)
if ((*itr)->GetBase()->IsPermanent())
{
AI()->EnterEvadeMode();
AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES);
break;
}
return nullptr;
@@ -17916,6 +17916,8 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
// Set charmed
charmer->SetCharm(this, true);
StopAttackingInvalidTarget();
if (GetTypeId() == TYPEID_UNIT)
{
if (MovementGenerator* movementGenerator = GetMotionMaster()->GetMotionSlot(MOTION_SLOT_IDLE))
@@ -18080,6 +18082,8 @@ void Unit::RemoveCharmedBy(Unit* charmer)
charmer->SetCharm(this, false);
StopAttackingInvalidTarget();
Player* playerCharmer = charmer->ToPlayer();
if (playerCharmer)
{
@@ -19623,6 +19627,37 @@ void Unit::StopAttackFaction(uint32 faction_id)
(*itr)->StopAttackFaction(faction_id);
}
void Unit::StopAttackingInvalidTarget()
{
AttackerSet const& attackers = getAttackers();
for (AttackerSet::const_iterator itr = attackers.begin(); itr != attackers.end();)
{
Unit* attacker = (*itr);
if (!attacker->IsValidAttackTarget(this))
{
attacker->AttackStop();
if (attacker->GetTypeId() == TYPEID_PLAYER)
{
attacker->ToPlayer()->SendAttackSwingCancelAttack();
}
for (Unit* controled : attacker->m_Controlled)
{
if (controled->GetVictim() == this && !controled->IsValidAttackTarget(this))
{
controled->AttackStop();
}
}
itr = attackers.begin();
}
else
{
++itr;
}
}
}
void Unit::OutDebugInfo() const
{
LOG_ERROR("entities.unit", "Unit::OutDebugInfo");
+1
View File
@@ -1360,6 +1360,7 @@ public:
void CombatStop(bool includingCast = false);
void CombatStopWithPets(bool includingCast = false);
void StopAttackFaction(uint32 faction_id);
void StopAttackingInvalidTarget();
Unit* SelectNearbyTarget(Unit* exclude = nullptr, float dist = NOMINAL_MELEE_RANGE) const;
Unit* SelectNearbyNoTotemTarget(Unit* exclude = nullptr, float dist = NOMINAL_MELEE_RANGE) const;
void SendMeleeAttackStop(Unit* victim = nullptr);
@@ -15,9 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Unit.h"
#include "Define.h"
#include "SmartEnum.h"
#include "Unit.h"
#include <stdexcept>
namespace Acore::Impl::EnumUtilsImpl
+14 -23
View File
@@ -31,6 +31,12 @@
#include "Spell.h"
#include "WorldSession.h"
BossBoundaryData::~BossBoundaryData()
{
for (const_iterator it = begin(); it != end(); ++it)
delete it->boundary;
}
void InstanceScript::SaveToDB()
{
std::string data = GetSaveData();
@@ -120,6 +126,13 @@ bool InstanceScript::IsEncounterInProgress() const
return false;
}
void InstanceScript::LoadBossBoundaries(const BossBoundaryData& data)
{
for (BossBoundaryEntry const& entry : data)
if (entry.bossId < bosses.size())
bosses[entry.bossId].boundary.push_back(entry.boundary);
}
void InstanceScript::LoadMinionData(const MinionData* data)
{
while (data->entry)
@@ -137,7 +150,7 @@ void InstanceScript::LoadDoorData(const DoorData* data)
while (data->entry)
{
if (data->bossId < bosses.size())
doors.insert(std::make_pair(data->entry, DoorInfo(&bosses[data->bossId], data->type, BoundaryType(data->boundary))));
doors.insert(std::make_pair(data->entry, DoorInfo(&bosses[data->bossId], data->type)));
++data;
}
@@ -268,28 +281,6 @@ void InstanceScript::AddDoor(GameObject* door, bool add)
if (add)
{
data.bossInfo->door[data.type].insert(door);
switch (data.boundary)
{
default:
case BOUNDARY_NONE:
break;
case BOUNDARY_N:
case BOUNDARY_S:
data.bossInfo->boundary[data.boundary] = door->GetPositionX();
break;
case BOUNDARY_E:
case BOUNDARY_W:
data.bossInfo->boundary[data.boundary] = door->GetPositionY();
break;
case BOUNDARY_NW:
case BOUNDARY_SE:
data.bossInfo->boundary[data.boundary] = door->GetPositionX() + door->GetPositionY();
break;
case BOUNDARY_NE:
case BOUNDARY_SW:
data.bossInfo->boundary[data.boundary] = door->GetPositionX() - door->GetPositionY();
break;
}
}
else
data.bossInfo->door[data.type].erase(door);
+27 -25
View File
@@ -18,9 +18,11 @@
#ifndef ACORE_INSTANCE_DATA_H
#define ACORE_INSTANCE_DATA_H
#include "CreatureAI.h"
#include "ObjectMgr.h"
#include "World.h"
#include "ZoneScript.h"
#include <set>
#define OUT_SAVE_INST_DATA LOG_DEBUG("scripts.ai", "Saving Instance Data for Instance {} (Map {}, Instance Id {})", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
#define OUT_SAVE_INST_DATA_COMPLETE LOG_DEBUG("scripts.ai", "Saving Instance Data for Instance {} (Map {}, Instance Id {}) completed.", instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
@@ -67,30 +69,30 @@ enum DoorType
MAX_DOOR_TYPES,
};
enum BoundaryType
{
BOUNDARY_NONE = 0,
BOUNDARY_N,
BOUNDARY_S,
BOUNDARY_E,
BOUNDARY_W,
BOUNDARY_NE,
BOUNDARY_NW,
BOUNDARY_SE,
BOUNDARY_SW,
BOUNDARY_MAX_X = BOUNDARY_N,
BOUNDARY_MIN_X = BOUNDARY_S,
BOUNDARY_MAX_Y = BOUNDARY_W,
BOUNDARY_MIN_Y = BOUNDARY_E,
};
typedef std::map<BoundaryType, float> BossBoundaryMap;
struct DoorData
{
uint32 entry, bossId;
DoorType type;
uint32 boundary;
};
struct BossBoundaryEntry
{
uint32 const bossId;
AreaBoundary const* const boundary;
};
struct BossBoundaryData
{
typedef std::vector<BossBoundaryEntry> StorageType;
typedef StorageType::const_iterator const_iterator;
BossBoundaryData(std::initializer_list<BossBoundaryEntry> data) : _data(data) { }
~BossBoundaryData();
const_iterator begin() const { return _data.begin(); }
const_iterator end() const { return _data.end(); }
private:
StorageType _data;
};
struct MinionData
@@ -110,16 +112,15 @@ struct BossInfo
EncounterState state;
DoorSet door[MAX_DOOR_TYPES];
MinionSet minion;
BossBoundaryMap boundary;
CreatureBoundary boundary;
};
struct DoorInfo
{
explicit DoorInfo(BossInfo* _bossInfo, DoorType _type, BoundaryType _boundary)
: bossInfo(_bossInfo), type(_type), boundary(_boundary) {}
explicit DoorInfo(BossInfo* _bossInfo, DoorType _type)
: bossInfo(_bossInfo), type(_type) { }
BossInfo* bossInfo;
DoorType type;
BoundaryType boundary;
};
struct MinionInfo
@@ -225,7 +226,7 @@ public:
virtual bool SetBossState(uint32 id, EncounterState state);
EncounterState GetBossState(uint32 id) const { return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED; }
static std::string GetBossStateName(uint8 state);
BossBoundaryMap const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : nullptr; }
CreatureBoundary const* GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : nullptr; }
BossInfo const* GetBossInfo(uint32 id) const { return &bosses[id]; }
// Achievement criteria additional requirements check
@@ -255,6 +256,7 @@ public:
virtual void DoAction(int32 /*action*/) {}
protected:
void SetBossNumber(uint32 number) { bosses.resize(number); }
void LoadBossBoundaries(BossBoundaryData const& data);
void LoadDoorData(DoorData const* data);
void LoadMinionData(MinionData const* data);
void LoadObjectData(ObjectData const* creatureData, ObjectData const* gameObjectData);
+113
View File
@@ -0,0 +1,113 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "AreaBoundary.h"
#include "Unit.h"
#include "TemporarySummon.h"
// ---== RECTANGLE ==---
RectangleBoundary::RectangleBoundary(float southX, float northX, float eastY, float westY, bool isInverted) :
AreaBoundary(isInverted), _minX(southX), _maxX(northX), _minY(eastY), _maxY(westY) { }
bool RectangleBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return !(
pos->GetPositionX() < _minX ||
pos->GetPositionX() > _maxX ||
pos->GetPositionY() < _minY ||
pos->GetPositionY() > _maxY
);
}
// ---== CIRCLE ==---
CircleBoundary::CircleBoundary(Position const& center, double radius, bool isInverted) :
AreaBoundary(isInverted), _center(center), _radiusSq(radius*radius) { }
CircleBoundary::CircleBoundary(Position const& center, Position const& pointOnCircle, bool isInverted) :
AreaBoundary(isInverted), _center(center), _radiusSq(_center.GetDoubleExactDist2dSq(pointOnCircle)) { }
bool CircleBoundary::IsWithinBoundaryArea(Position const* pos) const
{
double offX = _center.GetDoublePositionX() - pos->GetPositionX();
double offY = _center.GetDoublePositionY() - pos->GetPositionY();
return offX * offX + offY * offY <= _radiusSq;
}
// ---== ELLIPSE ==---
EllipseBoundary::EllipseBoundary(Position const& center, double radiusX, double radiusY, bool isInverted) :
AreaBoundary(isInverted), _center(center), _radiusYSq(radiusY*radiusY), _scaleXSq(_radiusYSq / (radiusX*radiusX)) { }
bool EllipseBoundary::IsWithinBoundaryArea(Position const* pos) const
{
double offX = _center.GetDoublePositionX() - pos->GetPositionX();
double offY = _center.GetDoublePositionY() - pos->GetPositionY();
return (offX*offX)*_scaleXSq + (offY*offY) <= _radiusYSq;
}
// ---== TRIANGLE ==---
TriangleBoundary::TriangleBoundary(Position const& pointA, Position const& pointB, Position const& pointC, bool isInverted) :
AreaBoundary(isInverted), _a(pointA), _b(pointB), _c(pointC), _abx(_b.GetDoublePositionX()-_a.GetDoublePositionX()), _bcx(_c.GetDoublePositionX()-_b.GetDoublePositionX()), _cax(_a.GetDoublePositionX() - _c.GetDoublePositionX()), _aby(_b.GetDoublePositionY()-_a.GetDoublePositionY()), _bcy(_c.GetDoublePositionY()-_b.GetDoublePositionY()), _cay(_a.GetDoublePositionY() - _c.GetDoublePositionY()) { }
bool TriangleBoundary::IsWithinBoundaryArea(Position const* pos) const
{
// half-plane signs
bool sign1 = ((-_b.GetDoublePositionX() + pos->GetPositionX()) * _aby - (-_b.GetDoublePositionY() + pos->GetPositionY()) * _abx) < 0;
bool sign2 = ((-_c.GetDoublePositionX() + pos->GetPositionX()) * _bcy - (-_c.GetDoublePositionY() + pos->GetPositionY()) * _bcx) < 0;
bool sign3 = ((-_a.GetDoublePositionX() + pos->GetPositionX()) * _cay - (-_a.GetDoublePositionY() + pos->GetPositionY()) * _cax) < 0;
// if all signs are the same, the point is inside the triangle
return ((sign1 == sign2) && (sign2 == sign3));
}
// ---== PARALLELOGRAM ==---
ParallelogramBoundary::ParallelogramBoundary(Position const& cornerA, Position const& cornerB, Position const& cornerD, bool isInverted) :
AreaBoundary(isInverted), _a(cornerA), _b(cornerB), _d(cornerD), _c(DoublePosition(_d.GetDoublePositionX() + (_b.GetDoublePositionX() - _a.GetDoublePositionX()), _d.GetDoublePositionY() + (_b.GetDoublePositionY() - _a.GetDoublePositionY()))), _abx(_b.GetDoublePositionX() - _a.GetDoublePositionX()), _dax(_a.GetDoublePositionX() - _d.GetDoublePositionX()), _aby(_b.GetDoublePositionY() - _a.GetDoublePositionY()), _day(_a.GetDoublePositionY() - _d.GetDoublePositionY()) { }
bool ParallelogramBoundary::IsWithinBoundaryArea(Position const* pos) const
{
// half-plane signs
bool sign1 = ((-_b.GetDoublePositionX() + pos->GetPositionX()) * _aby - (-_b.GetDoublePositionY() + pos->GetPositionY()) * _abx) < 0;
bool sign2 = ((-_a.GetDoublePositionX() + pos->GetPositionX()) * _day - (-_a.GetDoublePositionY() + pos->GetPositionY()) * _dax) < 0;
bool sign3 = ((-_d.GetDoublePositionY() + pos->GetPositionY()) * _abx - (-_d.GetDoublePositionX() + pos->GetPositionX()) * _aby) < 0; // AB = -CD
bool sign4 = ((-_c.GetDoublePositionY() + pos->GetPositionY()) * _dax - (-_c.GetDoublePositionX() + pos->GetPositionX()) * _day) < 0; // DA = -BC
// if all signs are equal, the point is inside
return ((sign1 == sign2) && (sign2 == sign3) && (sign3 == sign4));
}
// ---== Z RANGE ==---
ZRangeBoundary::ZRangeBoundary(float minZ, float maxZ, bool isInverted) :
AreaBoundary(isInverted), _minZ(minZ), _maxZ(maxZ) { }
bool ZRangeBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return (_minZ <= pos->GetPositionZ() && pos->GetPositionZ() <= _maxZ);
}
// ---== UNION OF 2 BOUNDARIES ==---
BoundaryUnionBoundary::BoundaryUnionBoundary(AreaBoundary const* b1, AreaBoundary const* b2, bool isInverted) :
AreaBoundary(isInverted), _b1(b1), _b2(b2)
{
ASSERT(b1 && b2);
}
BoundaryUnionBoundary::~BoundaryUnionBoundary()
{
delete _b1;
delete _b2;
}
bool BoundaryUnionBoundary::IsWithinBoundaryArea(Position const* pos) const
{
return (_b1->IsWithinBoundary(pos) || _b2->IsWithinBoundary(pos));
}
+168
View File
@@ -0,0 +1,168 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef ACORE_AREA_BOUNDARY_H
#define ACORE_AREA_BOUNDARY_H
#include "Position.h"
class AC_GAME_API AreaBoundary
{
public:
bool IsWithinBoundary(Position const* pos) const { return pos && (IsWithinBoundaryArea(pos) != _isInvertedBoundary); }
bool IsWithinBoundary(Position const& pos) const { return IsWithinBoundary(&pos); }
virtual ~AreaBoundary() { }
protected:
explicit AreaBoundary(bool isInverted) : _isInvertedBoundary(isInverted) { }
struct DoublePosition : Position
{
DoublePosition(double x = 0.0, double y = 0.0, double z = 0.0, float o = 0.0f)
: Position(float(x), float(y), float(z), o), DoublePosX(x), DoublePosY(y), DoublePosZ(z) { }
DoublePosition(float x, float y = 0.0f, float z = 0.0f, float o = 0.0f)
: Position(x, y, z, o), DoublePosX(x), DoublePosY(y), DoublePosZ(z) { }
DoublePosition(Position const& pos)
: Position(pos), DoublePosX(pos.m_positionX), DoublePosY(pos.m_positionY), DoublePosZ(pos.m_positionZ) { }
double GetDoublePositionX() const { return DoublePosX; }
double GetDoublePositionY() const { return DoublePosY; }
double GetDoublePositionZ() const { return DoublePosZ; }
double GetDoubleExactDist2dSq(DoublePosition const& pos) const
{
double const offX = GetDoublePositionX() - pos.GetDoublePositionX();
double const offY = GetDoublePositionY() - pos.GetDoublePositionY();
return (offX * offX) + (offY * offY);
}
Position* sync()
{
m_positionX = float(DoublePosX);
m_positionY = float(DoublePosY);
m_positionZ = float(DoublePosZ);
return this;
}
double DoublePosX;
double DoublePosY;
double DoublePosZ;
};
virtual bool IsWithinBoundaryArea(Position const* pos) const = 0;
private:
bool _isInvertedBoundary;
};
class AC_GAME_API RectangleBoundary : public AreaBoundary
{
public:
// X axis is north/south, Y axis is east/west, larger values are northwest
RectangleBoundary(float southX, float northX, float eastY, float westY, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
float const _minX, _maxX, _minY, _maxY;
};
class AC_GAME_API CircleBoundary : public AreaBoundary
{
public:
CircleBoundary(Position const& center, double radius, bool isInverted = false);
CircleBoundary(Position const& center, Position const& pointOnCircle, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _center;
double const _radiusSq;
};
class AC_GAME_API EllipseBoundary : public AreaBoundary
{
public:
EllipseBoundary(Position const& center, double radiusX, double radiusY, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _center;
double const _radiusYSq, _scaleXSq;
};
class AC_GAME_API TriangleBoundary : public AreaBoundary
{
public:
TriangleBoundary(Position const& pointA, Position const& pointB, Position const& pointC, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _a, _b, _c;
double const _abx, _bcx, _cax, _aby, _bcy, _cay;
};
class AC_GAME_API ParallelogramBoundary : public AreaBoundary
{
public:
// Note: AB must be orthogonal to AD
ParallelogramBoundary(Position const& cornerA, Position const& cornerB, Position const& cornerD, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
DoublePosition const _a, _b, _d, _c;
double const _abx, _dax, _aby, _day;
};
class AC_GAME_API ZRangeBoundary : public AreaBoundary
{
public:
ZRangeBoundary(float minZ, float maxZ, bool isInverted = false);
protected:
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
float const _minZ, _maxZ;
};
class AC_GAME_API BoundaryUnionBoundary : public AreaBoundary
{
public:
BoundaryUnionBoundary(AreaBoundary const* b1, AreaBoundary const* b2, bool isInverted = false);
protected:
virtual ~BoundaryUnionBoundary();
bool IsWithinBoundaryArea(Position const* pos) const override;
private:
AreaBoundary const* const _b1;
AreaBoundary const* const _b2;
};
#endif //ACORE_AREA_BOUNDARY_H
+2 -2
View File
@@ -1965,9 +1965,9 @@ inline LiquidData const GridMap::GetLiquidData(float x, float y, float z, float
if (delta > collisionHeight)
liquidData.Status = LIQUID_MAP_UNDER_WATER;
else if (delta > 0.2f)
else if (delta > 0.0f)
liquidData.Status = LIQUID_MAP_IN_WATER;
else if (delta > -0.2f)
else if (delta > -0.1f)
liquidData.Status = LIQUID_MAP_WATER_WALK;
else
liquidData.Status = LIQUID_MAP_ABOVE_WATER;
+1 -1
View File
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "Define.h"
#include "QuestDef.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>
+28 -4
View File
@@ -4495,13 +4495,25 @@ void Spell::SendSpellStart()
if (m_spellInfo->RuneCostID && m_spellInfo->PowerType == POWER_RUNE)
castFlags |= CAST_FLAG_NO_GCD; // not needed, but Blizzard sends it
PackedGuid realCasterGUID = m_caster->GetPackGUID();
if (TempSummon const* tempSummon = m_caster->ToTempSummon())
{
if (tempSummon->GetEntry() == WORLD_TRIGGER)
{
if (GameObject* casterGameobject = tempSummon->GetSummonerGameObject())
{
realCasterGUID = casterGameobject->GetPackGUID();
}
}
}
WorldPacket data(SMSG_SPELL_START, (8 + 8 + 4 + 4 + 2));
if (m_CastItem)
data << m_CastItem->GetPackGUID();
else
data << m_caster->GetPackGUID();
data << realCasterGUID;
data << m_caster->GetPackGUID();
data << realCasterGUID;
data << uint8(m_cast_count); // pending spell cast?
data << uint32(m_spellInfo->Id); // spellId
data << uint32(castFlags); // cast flags
@@ -4581,14 +4593,26 @@ void Spell::SendSpellGo()
if (!m_spellInfo->StartRecoveryTime)
castFlags |= CAST_FLAG_NO_GCD;
PackedGuid realCasterGUID = m_caster->GetPackGUID();
if (TempSummon const* tempSummon = m_caster->ToTempSummon())
{
if (tempSummon->GetEntry() == WORLD_TRIGGER)
{
if (GameObject* casterGameobject = tempSummon->GetSummonerGameObject())
{
realCasterGUID = casterGameobject->GetPackGUID();
}
}
}
WorldPacket data(SMSG_SPELL_GO, 150); // guess size
if (m_CastItem)
data << m_CastItem->GetPackGUID();
else
data << m_caster->GetPackGUID();
data << realCasterGUID;
data << m_caster->GetPackGUID();
data << realCasterGUID;
data << uint8(m_cast_count); // pending spell cast?
data << uint32(m_spellInfo->Id); // spellId
data << uint32(castFlags); // cast flags
-19
View File
@@ -3808,25 +3808,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex)
m_caster->CastSpell(unitTarget, 22682, true);
return;
}
// Mirren's Drinking Hat
case 29830:
{
uint32 itemId = 23586; // Aerie Peak Pale Ale
switch (urand(0, 5))
{
case 0:
case 1:
case 2:
itemId = 23584;
break; // Loch Modan Lager
case 3:
case 4:
itemId = 23585;
break; // Stouthammer Lite
}
DoCreateItem(effIndex, itemId);
break;
}
case 20589: // Escape artist
case 30918: // Improved Sprint
{
+1
View File
@@ -831,6 +831,7 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry)
SpellVisual = spellEntry->SpellVisual;
SpellIconID = spellEntry->SpellIconID;
ActiveIconID = spellEntry->ActiveIconID;
SpellPriority = spellEntry->SpellPriority;
SpellName = spellEntry->SpellName;
Rank = spellEntry->Rank;
MaxTargetLevel = spellEntry->MaxTargetLevel;
+1
View File
@@ -377,6 +377,7 @@ public:
std::array<uint32, 2> SpellVisual;
uint32 SpellIconID;
uint32 ActiveIconID;
uint32 SpellPriority;
std::array<char const*, 16> SpellName;
std::array<char const*, 16> Rank;
uint32 MaxTargetLevel;
@@ -244,7 +244,6 @@ void SpellMgr::LoadSpellInfoCorrections()
48108, // Hot Streak
51124, // Killing Machine
54741, // Firestarter
57761, // Fireball!
64823, // Item - Druid T8 Balance 4P Bonus
34477, // Misdirection
44401, // Missile Barrage
@@ -254,6 +253,13 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->ProcCharges = 1;
});
// Fireball
ApplySpellFix({ 57761 }, [](SpellInfo* spellInfo)
{
spellInfo->ProcCharges = 1;
spellInfo->SpellPriority = 50;
});
// Tidal Wave
ApplySpellFix({ 53390 }, [](SpellInfo* spellInfo)
{
@@ -15,9 +15,9 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "WardenCheckMgr.h"
#include "Define.h"
#include "SmartEnum.h"
#include "WardenCheckMgr.h"
#include <stdexcept>
namespace Acore::Impl::EnumUtilsImpl