Merge branch 'master' into Playerbot
# Conflicts: # src/server/game/Entities/Creature/Creature.cpp # src/server/game/Entities/Creature/Creature.h # src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
This commit is contained in:
@@ -26,11 +26,11 @@
|
||||
// AggressorAI
|
||||
/////////////////
|
||||
|
||||
int AggressorAI::Permissible(Creature const* creature)
|
||||
int32 AggressorAI::Permissible(Creature const* creature)
|
||||
{
|
||||
// have some hostile factions, it will be selected by IsHostileTo check at MoveInLineOfSight
|
||||
if (!creature->IsCivilian() && !creature->IsNeutralToAll())
|
||||
return PERMIT_BASE_PROACTIVE;
|
||||
return PERMIT_BASE_REACTIVE;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
@@ -320,3 +320,11 @@ void VehicleAI::CheckConditions(uint32 diff)
|
||||
else
|
||||
m_ConditionsTimer -= diff;
|
||||
}
|
||||
|
||||
int32 VehicleAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->IsVehicle())
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
explicit AggressorAI(Creature* c) : CreatureAI(c) {}
|
||||
|
||||
void UpdateAI(uint32) override;
|
||||
static int Permissible(Creature const*);
|
||||
static int32 Permissible(Creature const* creature);
|
||||
};
|
||||
|
||||
typedef std::vector<uint32> SpellVct;
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
void JustDied(Unit* killer) override;
|
||||
void UpdateAI(uint32 diff) override;
|
||||
|
||||
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
|
||||
protected:
|
||||
EventMap events;
|
||||
@@ -73,7 +73,7 @@ public:
|
||||
void AttackStart(Unit* who) override;
|
||||
void UpdateAI(uint32 diff) override;
|
||||
|
||||
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
|
||||
protected:
|
||||
float m_minRange;
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
void AttackStart(Unit* who) override;
|
||||
void UpdateAI(uint32 diff) override;
|
||||
|
||||
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
|
||||
protected:
|
||||
float m_minRange;
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
void AttackStart(Unit*) override {}
|
||||
void OnCharmed(bool apply) override;
|
||||
|
||||
static int Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
static int32 Permissible(Creature const* creature);
|
||||
|
||||
private:
|
||||
void LoadConditions();
|
||||
|
||||
@@ -17,12 +17,15 @@
|
||||
|
||||
#include "GameObjectAI.h"
|
||||
|
||||
//GameObjectAI::GameObjectAI(GameObject* g) : go(g) {}
|
||||
int GameObjectAI::Permissible(GameObject const* go)
|
||||
int32 GameObjectAI::Permissible(GameObject const* /*go*/)
|
||||
{
|
||||
if (go->GetAIName() == "GameObjectAI")
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
NullGameObjectAI::NullGameObjectAI(GameObject* g) : GameObjectAI(g) {}
|
||||
NullGameObjectAI::NullGameObjectAI(GameObject* go) : GameObjectAI(go) { }
|
||||
|
||||
int32 NullGameObjectAI::Permissible(GameObject const* /*go*/)
|
||||
{
|
||||
return PERMIT_BASE_IDLE;
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
virtual void SetGUID(ObjectGuid /*guid*/, int32 /*id = 0 */) {}
|
||||
virtual ObjectGuid GetGUID(int32 /*id = 0 */) const { return ObjectGuid::Empty; }
|
||||
|
||||
static int Permissible(GameObject const* go);
|
||||
static int32 Permissible(GameObject const* go);
|
||||
|
||||
virtual bool GossipHello(Player* /*player*/, bool /*reportUse*/) { return false; }
|
||||
virtual bool GossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { return false; }
|
||||
@@ -76,6 +76,6 @@ public:
|
||||
|
||||
void UpdateAI(uint32 /*diff*/) override {}
|
||||
|
||||
static int Permissible(GameObject const* /*go*/) { return PERMIT_BASE_IDLE; }
|
||||
static int32 Permissible(GameObject const* go);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -19,10 +19,10 @@
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "Player.h"
|
||||
|
||||
int GuardAI::Permissible(Creature const* creature)
|
||||
int32 GuardAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->IsGuard())
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
return PERMIT_BASE_PROACTIVE;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ class GuardAI : public ScriptedAI
|
||||
public:
|
||||
explicit GuardAI(Creature* creature);
|
||||
|
||||
static int Permissible(Creature const* creature);
|
||||
static int32 Permissible(Creature const* creature);
|
||||
|
||||
void Reset() override;
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override;
|
||||
|
||||
@@ -23,6 +23,17 @@ PassiveAI::PassiveAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASS
|
||||
PossessedAI::PossessedAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
|
||||
NullCreatureAI::NullCreatureAI(Creature* c) : CreatureAI(c) { me->SetReactState(REACT_PASSIVE); }
|
||||
|
||||
int32 NullCreatureAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK))
|
||||
return PERMIT_BASE_PROACTIVE + 50;
|
||||
|
||||
if (creature->IsTrigger())
|
||||
return PERMIT_BASE_REACTIVE;
|
||||
|
||||
return PERMIT_BASE_IDLE;
|
||||
}
|
||||
|
||||
void PassiveAI::UpdateAI(uint32)
|
||||
{
|
||||
if (me->IsInCombat() && me->getAttackers().empty())
|
||||
@@ -84,8 +95,24 @@ void CritterAI::UpdateAI(uint32 diff)
|
||||
}
|
||||
}
|
||||
|
||||
int32 CritterAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->IsCritter() && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
|
||||
return PERMIT_BASE_PROACTIVE;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
void TriggerAI::IsSummonedBy(Unit* summoner)
|
||||
{
|
||||
if (me->m_spells[0])
|
||||
me->CastSpell(me, me->m_spells[0], false, 0, 0, summoner ? summoner->GetGUID() : ObjectGuid::Empty);
|
||||
}
|
||||
|
||||
int32 TriggerAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->IsTrigger() && creature->m_spells[0])
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ public:
|
||||
void AttackStart(Unit*) override {}
|
||||
void UpdateAI(uint32) override;
|
||||
|
||||
static int Permissible(Creature const*) { return PERMIT_BASE_IDLE; }
|
||||
static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
};
|
||||
|
||||
class PossessedAI : public CreatureAI
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
void JustDied(Unit*) override;
|
||||
void KilledUnit(Unit* victim) override;
|
||||
|
||||
static int Permissible(Creature const*) { return PERMIT_BASE_IDLE; }
|
||||
static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
};
|
||||
|
||||
class NullCreatureAI : public CreatureAI
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
void EnterEvadeMode(EvadeReason /*why*/) override {}
|
||||
void OnCharmed(bool /*apply*/) override {}
|
||||
|
||||
static int Permissible(Creature const*) { return PERMIT_BASE_IDLE; }
|
||||
static int32 Permissible(Creature const* creature);
|
||||
};
|
||||
|
||||
class CritterAI : public PassiveAI
|
||||
@@ -72,6 +72,7 @@ public:
|
||||
void EnterEvadeMode(EvadeReason why) override;
|
||||
void UpdateAI(uint32) override;
|
||||
|
||||
static int32 Permissible(Creature const* creature);
|
||||
// Xinef: Added
|
||||
private:
|
||||
uint32 _combatTimer;
|
||||
@@ -82,6 +83,8 @@ class TriggerAI : public NullCreatureAI
|
||||
public:
|
||||
explicit TriggerAI(Creature* c) : NullCreatureAI(c) {}
|
||||
void IsSummonedBy(Unit* summoner) override;
|
||||
|
||||
static int32 Permissible(Creature const* creature);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -28,10 +28,14 @@
|
||||
#include "SpellMgr.h"
|
||||
#include "Util.h"
|
||||
|
||||
int PetAI::Permissible(Creature const* creature)
|
||||
int32 PetAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->IsPet())
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
|
||||
{
|
||||
if (reinterpret_cast<Guardian const*>(creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)
|
||||
return PERMIT_BASE_PROACTIVE;
|
||||
return PERMIT_BASE_REACTIVE;
|
||||
}
|
||||
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ public:
|
||||
explicit PetAI(Creature* c);
|
||||
|
||||
void UpdateAI(uint32) override;
|
||||
static int Permissible(Creature const*);
|
||||
static int32 Permissible(Creature const* creature);
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override;
|
||||
void AttackStart(Unit* target) override;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "CreatureAIImpl.h"
|
||||
#include "Errors.h"
|
||||
|
||||
int ReactorAI::Permissible(Creature const* creature)
|
||||
int32 ReactorAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->IsCivilian() || creature->IsNeutralToAll())
|
||||
return PERMIT_BASE_REACTIVE;
|
||||
|
||||
@@ -30,6 +30,6 @@ public:
|
||||
void MoveInLineOfSight(Unit*) override {}
|
||||
void UpdateAI(uint32 diff) override;
|
||||
|
||||
static int Permissible(Creature const*);
|
||||
static int32 Permissible(Creature const* creature);
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
#include "SpellMgr.h"
|
||||
#include "Totem.h"
|
||||
|
||||
int TotemAI::Permissible(Creature const* creature)
|
||||
int32 TotemAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->IsTotem())
|
||||
return PERMIT_BASE_PROACTIVE;
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
void DoAction(int32 param) override;
|
||||
|
||||
void UpdateAI(uint32 diff) override;
|
||||
static int Permissible(Creature const* creature);
|
||||
static int32 Permissible(Creature const* creature);
|
||||
|
||||
private:
|
||||
ObjectGuid i_victimGuid;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "Vehicle.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "Language.h"
|
||||
#include "ZoneScript.h"
|
||||
|
||||
class PhasedRespawn : public BasicEvent
|
||||
{
|
||||
@@ -306,7 +307,10 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/)
|
||||
me->SetLootRecipient(nullptr);
|
||||
me->ResetPlayerDamageReq();
|
||||
me->SetLastDamagedTime(0);
|
||||
me->SetCannotReachTarget(false);
|
||||
me->SetCannotReachTarget();
|
||||
|
||||
if (ZoneScript* zoneScript = me->GetZoneScript() ? me->GetZoneScript() : (ZoneScript*)me->GetInstanceScript())
|
||||
zoneScript->OnCreatureEvade(me);
|
||||
|
||||
if (me->IsInEvadeMode())
|
||||
{
|
||||
|
||||
@@ -88,6 +88,7 @@ public:
|
||||
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_NO_PATH, // the creature was unable to reach its target for over 5 seconds
|
||||
EVADE_REASON_OTHER
|
||||
};
|
||||
|
||||
@@ -210,6 +211,8 @@ public:
|
||||
|
||||
virtual void CalculateThreat(Unit* /*hatedUnit*/, float& /*threat*/, SpellInfo const* /*threatSpell*/) { }
|
||||
|
||||
virtual bool OnTeleportUnreacheablePlayer(Player* /*player*/) { return false; }
|
||||
|
||||
protected:
|
||||
virtual void MoveInLineOfSight(Unit* /*who*/);
|
||||
|
||||
@@ -222,7 +225,7 @@ private:
|
||||
bool m_MoveInLineOfSight_locked;
|
||||
};
|
||||
|
||||
enum Permitions
|
||||
enum Permitions : int32
|
||||
{
|
||||
PERMIT_BASE_NO = -1,
|
||||
PERMIT_BASE_IDLE = 1,
|
||||
|
||||
@@ -19,60 +19,32 @@
|
||||
#define ACORE_CREATUREAIFACTORY_H
|
||||
|
||||
#include "FactoryHolder.h"
|
||||
#include "GameObjectAI.h"
|
||||
#include "ObjectRegistry.h"
|
||||
|
||||
struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature>
|
||||
typedef FactoryHolder<CreatureAI, Creature> CreatureAICreator;
|
||||
|
||||
struct SelectableAI : public CreatureAICreator, public Permissible<Creature>
|
||||
{
|
||||
SelectableAI(const char* id) : FactoryHolder<CreatureAI>(id) {}
|
||||
SelectableAI(std::string const& name) : CreatureAICreator(name), Permissible<Creature>() { }
|
||||
};
|
||||
|
||||
template<class REAL_AI>
|
||||
struct CreatureAIFactory : public SelectableAI
|
||||
{
|
||||
CreatureAIFactory(const char* name) : SelectableAI(name) {}
|
||||
CreatureAIFactory(std::string const& name) : SelectableAI(name) { }
|
||||
|
||||
CreatureAI* Create(void*) const;
|
||||
inline CreatureAI* Create(Creature* c) const override
|
||||
{
|
||||
return new REAL_AI(c);
|
||||
}
|
||||
|
||||
int Permit(Creature const* c) const { return REAL_AI::Permissible(c); }
|
||||
int32 Permit(Creature const* c) const override
|
||||
{
|
||||
return REAL_AI::Permissible(c);
|
||||
}
|
||||
};
|
||||
|
||||
template<class REAL_AI>
|
||||
inline CreatureAI*
|
||||
CreatureAIFactory<REAL_AI>::Create(void* data) const
|
||||
{
|
||||
Creature* creature = reinterpret_cast<Creature*>(data);
|
||||
return (new REAL_AI(creature));
|
||||
}
|
||||
|
||||
typedef FactoryHolder<CreatureAI> CreatureAICreator;
|
||||
typedef FactoryHolder<CreatureAI>::FactoryHolderRegistry CreatureAIRegistry;
|
||||
|
||||
//GO
|
||||
struct SelectableGameObjectAI : public FactoryHolder<GameObjectAI>, public Permissible<GameObject>
|
||||
{
|
||||
SelectableGameObjectAI(const char* id) : FactoryHolder<GameObjectAI>(id) {}
|
||||
};
|
||||
|
||||
template<class REAL_GO_AI>
|
||||
struct GameObjectAIFactory : public SelectableGameObjectAI
|
||||
{
|
||||
GameObjectAIFactory(const char* name) : SelectableGameObjectAI(name) {}
|
||||
|
||||
GameObjectAI* Create(void*) const;
|
||||
|
||||
int Permit(GameObject const* g) const { return REAL_GO_AI::Permissible(g); }
|
||||
};
|
||||
|
||||
template<class REAL_GO_AI>
|
||||
inline GameObjectAI*
|
||||
GameObjectAIFactory<REAL_GO_AI>::Create(void* data) const
|
||||
{
|
||||
GameObject* go = reinterpret_cast<GameObject*>(data);
|
||||
return (new REAL_GO_AI(go));
|
||||
}
|
||||
|
||||
typedef FactoryHolder<GameObjectAI> GameObjectAICreator;
|
||||
typedef FactoryHolder<GameObjectAI>::FactoryHolderRegistry GameObjectAIRegistry;
|
||||
typedef CreatureAICreator::FactoryHolderRegistry CreatureAIRegistry;
|
||||
#define sCreatureAIRegistry CreatureAIRegistry::instance()
|
||||
|
||||
#endif
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include "CombatAI.h"
|
||||
#include "CreatureAIFactory.h"
|
||||
#include "GuardAI.h"
|
||||
#include "MovementGeneratorImpl.h"
|
||||
#include "MovementGenerator.h"
|
||||
#include "PassiveAI.h"
|
||||
#include "PetAI.h"
|
||||
#include "RandomMovementGenerator.h"
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "SmartAI.h"
|
||||
#include "TotemAI.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
#include "GameObjectAIFactory.h"
|
||||
|
||||
namespace AIRegistry
|
||||
{
|
||||
@@ -47,10 +48,12 @@ namespace AIRegistry
|
||||
(new CreatureAIFactory<VehicleAI>("VehicleAI"))->RegisterSelf();
|
||||
(new CreatureAIFactory<SmartAI>("SmartAI"))->RegisterSelf();
|
||||
|
||||
(new GameObjectAIFactory<NullGameObjectAI>("NullGameObjectAI"))->RegisterSelf();
|
||||
(new GameObjectAIFactory<GameObjectAI>("GameObjectAI"))->RegisterSelf();
|
||||
(new GameObjectAIFactory<SmartGameObjectAI>("SmartGameObjectAI"))->RegisterSelf();
|
||||
|
||||
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
|
||||
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
|
||||
(new IdleMovementFactory())->RegisterSelf();
|
||||
(new MovementGeneratorFactory<RandomMovementGenerator<Creature>>(RANDOM_MOTION_TYPE))->RegisterSelf();
|
||||
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature>>(WAYPOINT_MOTION_TYPE))->RegisterSelf();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,132 +15,97 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureAISelector.h"
|
||||
#include "Creature.h"
|
||||
#include "CreatureAISelector.h"
|
||||
#include "CreatureAIFactory.h"
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "PassiveAI.h"
|
||||
#include "Pet.h"
|
||||
|
||||
#include "GameObject.h"
|
||||
#include "GameObjectAIFactory.h"
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "TemporarySummon.h"
|
||||
|
||||
namespace FactorySelector
|
||||
{
|
||||
CreatureAI* selectAI(Creature* creature)
|
||||
template <class T, class Value>
|
||||
inline int32 GetPermitFor(T const* obj, Value const& value)
|
||||
{
|
||||
const CreatureAICreator* ai_factory = nullptr;
|
||||
CreatureAIRegistry& ai_registry(*CreatureAIRegistry::instance());
|
||||
|
||||
// xinef: if we have controlable guardian, define petai for players as they can steer him, otherwise db / normal ai
|
||||
// xinef: dont remember why i changed this qq commented out as may break some quests
|
||||
if (creature->IsPet()/* || (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)*/)
|
||||
ai_factory = ai_registry.GetRegistryItem("PetAI");
|
||||
|
||||
//scriptname in db
|
||||
if (!ai_factory)
|
||||
if (CreatureAI* scriptedAI = sScriptMgr->GetCreatureAI(creature))
|
||||
return scriptedAI;
|
||||
|
||||
// AIname in db
|
||||
std::string ainame = creature->GetAIName();
|
||||
if (!ai_factory && !ainame.empty())
|
||||
ai_factory = ai_registry.GetRegistryItem(ainame);
|
||||
|
||||
// select by NPC flags
|
||||
if (!ai_factory)
|
||||
{
|
||||
if (creature->IsVehicle())
|
||||
ai_factory = ai_registry.GetRegistryItem("VehicleAI");
|
||||
else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN) && ((Guardian*)creature)->GetOwner()->GetTypeId() == TYPEID_PLAYER)
|
||||
ai_factory = ai_registry.GetRegistryItem("PetAI");
|
||||
else if (creature->HasNpcFlag(UNIT_NPC_FLAG_SPELLCLICK))
|
||||
ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
|
||||
else if (creature->IsGuard())
|
||||
ai_factory = ai_registry.GetRegistryItem("GuardAI");
|
||||
else if (creature->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
|
||||
ai_factory = ai_registry.GetRegistryItem("PetAI");
|
||||
else if (creature->IsTotem())
|
||||
ai_factory = ai_registry.GetRegistryItem("TotemAI");
|
||||
else if (creature->IsTrigger())
|
||||
{
|
||||
if (creature->m_spells[0])
|
||||
ai_factory = ai_registry.GetRegistryItem("TriggerAI");
|
||||
else
|
||||
ai_factory = ai_registry.GetRegistryItem("NullCreatureAI");
|
||||
}
|
||||
else if (creature->IsCritter() && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
|
||||
ai_factory = ai_registry.GetRegistryItem("CritterAI");
|
||||
}
|
||||
|
||||
// select by permit check
|
||||
if (!ai_factory)
|
||||
{
|
||||
int best_val = -1;
|
||||
typedef CreatureAIRegistry::RegistryMapType RMT;
|
||||
RMT const& l = ai_registry.GetRegisteredItems();
|
||||
for (RMT::const_iterator iter = l.begin(); iter != l.end(); ++iter)
|
||||
{
|
||||
const CreatureAICreator* factory = iter->second;
|
||||
const SelectableAI* p = dynamic_cast<const SelectableAI*>(factory);
|
||||
ASSERT(p);
|
||||
int val = p->Permit(creature);
|
||||
if (val > best_val)
|
||||
{
|
||||
best_val = val;
|
||||
ai_factory = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// select NullCreatureAI if not another cases
|
||||
ainame = (!ai_factory) ? "NullCreatureAI" : ai_factory->key();
|
||||
LOG_DEBUG("scripts.ai", "Creature {} used AI is {}.", creature->GetGUID().ToString(), ainame);
|
||||
return (!ai_factory ? new NullCreatureAI(creature) : ai_factory->Create(creature));
|
||||
Permissible<T> const* const p = ASSERT_NOTNULL(dynamic_cast<Permissible<T> const*>(value.second.get()));
|
||||
return p->Permit(obj);
|
||||
}
|
||||
|
||||
MovementGenerator* selectMovementGenerator(Creature* creature)
|
||||
template <class T>
|
||||
struct PermissibleOrderPred
|
||||
{
|
||||
MovementGeneratorRegistry& mv_registry(*MovementGeneratorRegistry::instance());
|
||||
ASSERT(creature->GetCreatureTemplate());
|
||||
const MovementGeneratorCreator* mv_factory = mv_registry.GetRegistryItem(creature->GetDefaultMovementType());
|
||||
public:
|
||||
PermissibleOrderPred(T const* obj) : _obj(obj) { }
|
||||
|
||||
/* if (mv_factory == nullptr)
|
||||
template <class Value>
|
||||
bool operator()(Value const& left, Value const& right) const
|
||||
{
|
||||
int best_val = -1;
|
||||
std::vector<std::string> l;
|
||||
mv_registry.GetRegisteredItems(l);
|
||||
for (std::vector<std::string>::iterator iter = l.begin(); iter != l.end(); ++iter)
|
||||
{
|
||||
const MovementGeneratorCreator *factory = mv_registry.GetRegistryItem((*iter).c_str());
|
||||
const SelectableMovement *p = dynamic_cast<const SelectableMovement *>(factory);
|
||||
ASSERT(p != nullptr);
|
||||
int val = p->Permit(creature);
|
||||
if (val > best_val)
|
||||
{
|
||||
best_val = val;
|
||||
mv_factory = p;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
return GetPermitFor(_obj, left) < GetPermitFor(_obj, right);
|
||||
}
|
||||
|
||||
return (!mv_factory ? nullptr : mv_factory->Create(creature));
|
||||
private:
|
||||
T const* const _obj;
|
||||
};
|
||||
|
||||
template <class AI, class T>
|
||||
inline FactoryHolder<AI, T> const* SelectFactory(T* obj)
|
||||
{
|
||||
static_assert(std::is_same<AI, CreatureAI>::value || std::is_same<AI, GameObjectAI>::value, "Invalid template parameter");
|
||||
static_assert(std::is_same<AI, CreatureAI>::value == std::is_same<T, Creature>::value, "Incompatible AI for type");
|
||||
static_assert(std::is_same<AI, GameObjectAI>::value == std::is_same<T, GameObject>::value, "Incompatible AI for type");
|
||||
|
||||
using AIRegistry = typename FactoryHolder<AI, T>::FactoryHolderRegistry;
|
||||
|
||||
// AIName in db
|
||||
std::string const& aiName = obj->GetAIName();
|
||||
if (!aiName.empty())
|
||||
return AIRegistry::instance()->GetRegistryItem(aiName);
|
||||
|
||||
// select by permit check
|
||||
typename AIRegistry::RegistryMapType const& items = AIRegistry::instance()->GetRegisteredItems();
|
||||
auto itr = std::max_element(items.begin(), items.end(), PermissibleOrderPred<T>(obj));
|
||||
if (itr != items.end() && GetPermitFor(obj, *itr) >= 0)
|
||||
return itr->second.get();
|
||||
|
||||
// should _never_ happen, Null AI types defined as PERMIT_BASE_IDLE, it must've been found
|
||||
ABORT();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CreatureAI* SelectAI(Creature* creature)
|
||||
{
|
||||
// special pet case, if a tamed creature uses AIName (example SmartAI) we need to override it
|
||||
if (creature->IsPet())
|
||||
return ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI"))->Create(creature);
|
||||
|
||||
// scriptname in db
|
||||
if (CreatureAI* scriptedAI = sScriptMgr->GetCreatureAI(creature))
|
||||
return scriptedAI;
|
||||
|
||||
return SelectFactory<CreatureAI>(creature)->Create(creature);
|
||||
}
|
||||
|
||||
MovementGenerator* SelectMovementGenerator(Unit* unit)
|
||||
{
|
||||
MovementGeneratorType type = IDLE_MOTION_TYPE;
|
||||
if (Creature* creature = unit->ToCreature())
|
||||
if (!creature->GetCharmerOrOwnerPlayerOrPlayerItself())
|
||||
type = creature->GetDefaultMovementType();
|
||||
|
||||
MovementGeneratorCreator const* mv_factory = sMovementGeneratorRegistry->GetRegistryItem(type);
|
||||
return ASSERT_NOTNULL(mv_factory)->Create(unit);
|
||||
}
|
||||
|
||||
GameObjectAI* SelectGameObjectAI(GameObject* go)
|
||||
{
|
||||
const GameObjectAICreator* ai_factory = nullptr;
|
||||
GameObjectAIRegistry& ai_registry(*GameObjectAIRegistry::instance());
|
||||
|
||||
// scriptname in db
|
||||
if (GameObjectAI* scriptedAI = sScriptMgr->GetGameObjectAI(go))
|
||||
return scriptedAI;
|
||||
|
||||
ai_factory = ai_registry.GetRegistryItem(go->GetAIName());
|
||||
|
||||
//future goAI types go here
|
||||
|
||||
std::string ainame = (!ai_factory || go->GetScriptId()) ? "NullGameObjectAI" : ai_factory->key();
|
||||
LOG_DEBUG("scripts.ai", "GameObject {} used AI is {}.", go->GetGUID().ToString(), ainame);
|
||||
|
||||
return (!ai_factory ? new NullGameObjectAI(go) : ai_factory->Create(go));
|
||||
return SelectFactory<GameObjectAI>(go)->Create(go);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,13 +21,14 @@
|
||||
class CreatureAI;
|
||||
class Creature;
|
||||
class MovementGenerator;
|
||||
class Unit;
|
||||
class GameObjectAI;
|
||||
class GameObject;
|
||||
|
||||
namespace FactorySelector
|
||||
{
|
||||
CreatureAI* selectAI(Creature*);
|
||||
MovementGenerator* selectMovementGenerator(Creature*);
|
||||
GameObjectAI* SelectGameObjectAI(GameObject*);
|
||||
AC_GAME_API CreatureAI* SelectAI(Creature* creature);
|
||||
AC_GAME_API MovementGenerator* SelectMovementGenerator(Unit* unit);
|
||||
AC_GAME_API GameObjectAI* SelectGameObjectAI(GameObject* go);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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_GAMEOBJECTAIFACTORY_H
|
||||
#define ACORE_GAMEOBJECTAIFACTORY_H
|
||||
|
||||
#include "ObjectRegistry.h"
|
||||
#include "FactoryHolder.h"
|
||||
|
||||
typedef FactoryHolder<GameObjectAI, GameObject> GameObjectAICreator;
|
||||
|
||||
struct SelectableGameObjectAI : public GameObjectAICreator, public Permissible<GameObject>
|
||||
{
|
||||
SelectableGameObjectAI(std::string const& name) : GameObjectAICreator(name), Permissible<GameObject>() { }
|
||||
};
|
||||
|
||||
template<class REAL_GO_AI>
|
||||
struct GameObjectAIFactory : public SelectableGameObjectAI
|
||||
{
|
||||
GameObjectAIFactory(std::string const& name) : SelectableGameObjectAI(name) { }
|
||||
|
||||
GameObjectAI* Create(GameObject* go) const override
|
||||
{
|
||||
return new REAL_GO_AI(go);
|
||||
}
|
||||
|
||||
int32 Permit(GameObject const* go) const override
|
||||
{
|
||||
return REAL_GO_AI::Permissible(go);
|
||||
}
|
||||
};
|
||||
|
||||
typedef GameObjectAICreator::FactoryHolderRegistry GameObjectAIRegistry;
|
||||
|
||||
#define sGameObjectAIRegistry GameObjectAIRegistry::instance()
|
||||
|
||||
#endif
|
||||
@@ -297,12 +297,12 @@ void SmartAI::EndPath(bool fail)
|
||||
mEscortNPCFlags = 0;
|
||||
}
|
||||
|
||||
ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS);
|
||||
ObjectVector const* targets = GetScript()->GetStoredTargetVector(SMART_ESCORT_TARGETS, *me);
|
||||
if (targets && mEscortQuestID)
|
||||
{
|
||||
if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin())))
|
||||
{
|
||||
Player* player = (*targets->begin())->ToPlayer();
|
||||
Player* player = targets->front()->ToPlayer();
|
||||
if (Group* group = player->GetGroup())
|
||||
{
|
||||
for (GroupReference* groupRef = group->GetFirstMember(); groupRef != nullptr; groupRef = groupRef->next())
|
||||
@@ -327,11 +327,11 @@ void SmartAI::EndPath(bool fail)
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ObjectList::iterator iter = targets->begin(); iter != targets->end(); ++iter)
|
||||
for (WorldObject* target : *targets)
|
||||
{
|
||||
if (GetScript()->IsPlayer((*iter)))
|
||||
if (GetScript()->IsPlayer(target))
|
||||
{
|
||||
Player* player = (*iter)->ToPlayer();
|
||||
Player* player = target->ToPlayer();
|
||||
if (!fail && player->IsAtGroupRewardDistance(me) && !player->HasCorpse())
|
||||
player->AreaExploredOrEventHappens(mEscortQuestID);
|
||||
else if (fail && player->GetQuestStatus(mEscortQuestID) == QUEST_STATUS_INCOMPLETE)
|
||||
@@ -535,8 +535,7 @@ void SmartAI::UpdateAI(uint32 diff)
|
||||
|
||||
bool SmartAI::IsEscortInvokerInRange()
|
||||
{
|
||||
ObjectList* targets = GetScript()->GetTargetList(SMART_ESCORT_TARGETS);
|
||||
if (targets)
|
||||
if (ObjectVector const* targets = GetScript()->GetStoredTargetVector(SMART_ESCORT_TARGETS, *me))
|
||||
{
|
||||
float checkDist = me->GetInstanceScript() ? SMART_ESCORT_MAX_PLAYER_DIST * 2 : SMART_ESCORT_MAX_PLAYER_DIST;
|
||||
if (targets->size() == 1 && GetScript()->IsPlayer((*targets->begin())))
|
||||
@@ -558,11 +557,11 @@ bool SmartAI::IsEscortInvokerInRange()
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ObjectList::iterator iter = targets->begin(); iter != targets->end(); ++iter)
|
||||
for (WorldObject* target : *targets)
|
||||
{
|
||||
if (GetScript()->IsPlayer((*iter)))
|
||||
if (GetScript()->IsPlayer(target))
|
||||
{
|
||||
if (me->GetDistance((*iter)->ToPlayer()) <= checkDist)
|
||||
if (me->GetDistance(target->ToPlayer()) <= checkDist)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -763,13 +762,6 @@ void SmartAI::JustRespawned()
|
||||
mFollowArrivedAlive = true;
|
||||
}
|
||||
|
||||
int SmartAI::Permissible(Creature const* creature)
|
||||
{
|
||||
if (creature->GetAIName() == "SmartAI")
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
void SmartAI::JustReachedHome()
|
||||
{
|
||||
GetScript()->OnReset();
|
||||
@@ -1133,13 +1125,6 @@ void SmartGameObjectAI::SummonedCreatureDies(Creature* summon, Unit* /*killer*/)
|
||||
GetScript()->ProcessEventsFor(SMART_EVENT_SUMMONED_UNIT_DIES, summon);
|
||||
}
|
||||
|
||||
int SmartGameObjectAI::Permissible(GameObject const* g)
|
||||
{
|
||||
if (g->GetAIName() == "SmartGameObjectAI")
|
||||
return PERMIT_BASE_SPECIAL;
|
||||
return PERMIT_BASE_NO;
|
||||
}
|
||||
|
||||
void SmartGameObjectAI::UpdateAI(uint32 diff)
|
||||
{
|
||||
GetScript()->OnUpdate(diff);
|
||||
|
||||
@@ -164,7 +164,7 @@ public:
|
||||
ObjectGuid GetGUID(int32 id = 0) const override;
|
||||
|
||||
//core related
|
||||
static int32 Permissible(Creature const*);
|
||||
static int32 Permissible(Creature const* /*creature*/) { return PERMIT_BASE_NO; }
|
||||
|
||||
// Called at movepoint reached
|
||||
void MovepointReached(uint32 id);
|
||||
@@ -259,7 +259,7 @@ public:
|
||||
void InitializeAI() override;
|
||||
void Reset() override;
|
||||
SmartScript* GetScript() { return &mScript; }
|
||||
static int32 Permissible(GameObject const* g);
|
||||
static int32 Permissible(GameObject const* /*go*/) { return PERMIT_BASE_NO; }
|
||||
|
||||
bool GossipHello(Player* player, bool reportUse) override;
|
||||
bool GossipSelect(Player* player, uint32 sender, uint32 action) override;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,19 +39,19 @@ public:
|
||||
void ProcessEventsFor(SMART_EVENT e, Unit* unit = nullptr, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, SpellInfo const* spell = nullptr, GameObject* gob = nullptr);
|
||||
void ProcessEvent(SmartScriptHolder& e, Unit* unit = nullptr, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, SpellInfo const* spell = nullptr, GameObject* gob = nullptr);
|
||||
bool CheckTimer(SmartScriptHolder const& e) const;
|
||||
void RecalcTimer(SmartScriptHolder& e, uint32 min, uint32 max);
|
||||
static void RecalcTimer(SmartScriptHolder& e, uint32 min, uint32 max);
|
||||
void UpdateTimer(SmartScriptHolder& e, uint32 const diff);
|
||||
void InitTimer(SmartScriptHolder& e);
|
||||
static void InitTimer(SmartScriptHolder& e);
|
||||
void ProcessAction(SmartScriptHolder& e, Unit* unit = nullptr, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, SpellInfo const* spell = nullptr, GameObject* gob = nullptr);
|
||||
void ProcessTimedAction(SmartScriptHolder& e, uint32 const& min, uint32 const& max, Unit* unit = nullptr, uint32 var0 = 0, uint32 var1 = 0, bool bvar = false, SpellInfo const* spell = nullptr, GameObject* gob = nullptr);
|
||||
ObjectList* GetTargets(SmartScriptHolder const& e, Unit* invoker = nullptr);
|
||||
ObjectList* GetWorldObjectsInDist(float dist);
|
||||
void GetTargets(ObjectVector& targets, SmartScriptHolder const& e, Unit* invoker = nullptr) const;
|
||||
void GetWorldObjectsInDist(ObjectVector& objects, float dist) const;
|
||||
void InstallTemplate(SmartScriptHolder const& e);
|
||||
SmartScriptHolder CreateSmartEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, uint32 event_param5, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 target_param4, uint32 phaseMask);
|
||||
static SmartScriptHolder CreateSmartEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, uint32 event_param5, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 target_param4, uint32 phaseMask);
|
||||
void AddEvent(SMART_EVENT e, uint32 event_flags, uint32 event_param1, uint32 event_param2, uint32 event_param3, uint32 event_param4, uint32 event_param5, SMART_ACTION action, uint32 action_param1, uint32 action_param2, uint32 action_param3, uint32 action_param4, uint32 action_param5, uint32 action_param6, SMARTAI_TARGETS t, uint32 target_param1, uint32 target_param2, uint32 target_param3, uint32 target_param4, uint32 phaseMask);
|
||||
void SetPathId(uint32 id) { mPathId = id; }
|
||||
uint32 GetPathId() const { return mPathId; }
|
||||
WorldObject* GetBaseObject()
|
||||
WorldObject* GetBaseObject() const
|
||||
{
|
||||
WorldObject* obj = nullptr;
|
||||
if (me)
|
||||
@@ -70,27 +70,17 @@ public:
|
||||
void OnUpdate(const uint32 diff);
|
||||
void OnMoveInLineOfSight(Unit* who);
|
||||
|
||||
Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
|
||||
Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff) const;
|
||||
Unit* DoSelectLowestHpPercentFriendly(float range, uint32 minHpPct, uint32 maxHpPct) const;
|
||||
void DoFindFriendlyCC(std::list<Creature*>& _list, float range);
|
||||
void DoFindFriendlyMissingBuff(std::list<Creature*>& list, float range, uint32 spellid);
|
||||
Unit* DoFindClosestFriendlyInRange(float range, bool playerOnly);
|
||||
void DoFindFriendlyCC(std::vector<Creature*>& creatures, float range) const;
|
||||
void DoFindFriendlyMissingBuff(std::vector<Creature*>& creatures, float range, uint32 spellid) const;
|
||||
Unit* DoFindClosestFriendlyInRange(float range, bool playerOnly) const;
|
||||
|
||||
void StoreTargetList(ObjectList* targets, uint32 id)
|
||||
void StoreTargetList(ObjectVector const& targets, uint32 id)
|
||||
{
|
||||
if (!targets)
|
||||
return;
|
||||
|
||||
if (mTargetStorage->find(id) != mTargetStorage->end())
|
||||
{
|
||||
// check if already stored
|
||||
if ((*mTargetStorage)[id]->Equals(targets))
|
||||
return;
|
||||
|
||||
delete (*mTargetStorage)[id];
|
||||
}
|
||||
|
||||
(*mTargetStorage)[id] = new ObjectGuidList(targets, GetBaseObject());
|
||||
// insert or replace
|
||||
_storedTargets.erase(id);
|
||||
_storedTargets.emplace(id, ObjectGuidVector(targets));
|
||||
}
|
||||
|
||||
bool IsSmart(Creature* c = nullptr)
|
||||
@@ -122,11 +112,11 @@ public:
|
||||
return smart;
|
||||
}
|
||||
|
||||
ObjectList* GetTargetList(uint32 id)
|
||||
ObjectVector const* GetStoredTargetVector(uint32 id, WorldObject const& ref) const
|
||||
{
|
||||
ObjectListMap::iterator itr = mTargetStorage->find(id);
|
||||
if (itr != mTargetStorage->end())
|
||||
return (*itr).second->GetObjectList();
|
||||
auto itr = _storedTargets.find(id);
|
||||
if (itr != _storedTargets.end())
|
||||
return itr->second.GetObjectVector(ref);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -187,8 +177,6 @@ public:
|
||||
return creatureItr != bounds.second ? creatureItr->second : bounds.first->second;
|
||||
}
|
||||
|
||||
ObjectListMap* mTargetStorage;
|
||||
|
||||
void OnReset();
|
||||
void ResetBaseObject()
|
||||
{
|
||||
@@ -223,7 +211,7 @@ public:
|
||||
|
||||
//TIMED_ACTIONLIST (script type 9 aka script9)
|
||||
void SetScript9(SmartScriptHolder& e, uint32 entry);
|
||||
Unit* GetLastInvoker(Unit* invoker = nullptr);
|
||||
Unit* GetLastInvoker(Unit* invoker = nullptr) const;
|
||||
ObjectGuid mLastInvoker;
|
||||
typedef std::unordered_map<uint32, uint32> CounterMap;
|
||||
CounterMap mCounterList;
|
||||
@@ -284,6 +272,8 @@ private:
|
||||
// Xinef: misc
|
||||
bool _allowPhaseReset;
|
||||
|
||||
ObjectVectorMap _storedTargets;
|
||||
|
||||
SMARTAI_TEMPLATE mTemplate;
|
||||
void InstallEvents();
|
||||
|
||||
|
||||
@@ -679,7 +679,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
|
||||
case SMART_ACTION_SEND_GOSSIP_MENU: return sizeof(SmartAction::sendGossipMenu);
|
||||
case SMART_ACTION_GO_SET_LOOT_STATE: return sizeof(SmartAction::setGoLootState);
|
||||
case SMART_ACTION_SEND_TARGET_TO_TARGET: return sizeof(SmartAction::sendTargetToTarget);
|
||||
case SMART_ACTION_SET_HOME_POS: return NO_PARAMS;
|
||||
case SMART_ACTION_SET_HOME_POS: return sizeof(SmartAction::setHomePos);
|
||||
case SMART_ACTION_SET_HEALTH_REGEN: return sizeof(SmartAction::setHealthRegen);
|
||||
case SMART_ACTION_SET_ROOT: return sizeof(SmartAction::setRoot);
|
||||
case SMART_ACTION_SET_GO_FLAG: return sizeof(SmartAction::goFlag);
|
||||
@@ -1358,24 +1358,39 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_RANDOM_EMOTE:
|
||||
if (e.action.randomEmote.emote1 && !IsEmoteValid(e, e.action.randomEmote.emote1))
|
||||
return false;
|
||||
{
|
||||
if (std::all_of(e.action.randomEmote.emotes.begin(), e.action.randomEmote.emotes.end(), [](uint32 emote) { return emote == 0; }))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} does not have any non-zero emote",
|
||||
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (e.action.randomEmote.emote2 && !IsEmoteValid(e, e.action.randomEmote.emote2))
|
||||
return false;
|
||||
|
||||
if (e.action.randomEmote.emote3 && !IsEmoteValid(e, e.action.randomEmote.emote3))
|
||||
return false;
|
||||
|
||||
if (e.action.randomEmote.emote4 && !IsEmoteValid(e, e.action.randomEmote.emote4))
|
||||
return false;
|
||||
|
||||
if (e.action.randomEmote.emote5 && !IsEmoteValid(e, e.action.randomEmote.emote5))
|
||||
return false;
|
||||
|
||||
if (e.action.randomEmote.emote6 && !IsEmoteValid(e, e.action.randomEmote.emote6))
|
||||
return false;
|
||||
break;
|
||||
for (uint32 emote : e.action.randomEmote.emotes)
|
||||
if (emote && !IsEmoteValid(e, emote))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST:
|
||||
{
|
||||
if (std::all_of(e.action.randTimedActionList.actionLists.begin(), e.action.randTimedActionList.actionLists.end(), [](uint32 actionList) { return actionList == 0; }))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} does not have any non-zero action list",
|
||||
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_START_CLOSEST_WAYPOINT:
|
||||
{
|
||||
if (std::all_of(e.action.closestWaypointFromList.wps.begin(), e.action.closestWaypointFromList.wps.end(), [](uint32 wp) { return wp == 0; }))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} does not have any non-zero waypoint id",
|
||||
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SMART_ACTION_CAST:
|
||||
case SMART_ACTION_INVOKER_CAST:
|
||||
if (!IsSpellValid(e, e.action.cast.spell))
|
||||
@@ -1434,36 +1449,29 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
break;
|
||||
case SMART_ACTION_RANDOM_PHASE:
|
||||
{
|
||||
if (e.action.randomPhase.phase1 >= SMART_EVENT_PHASE_MAX ||
|
||||
e.action.randomPhase.phase2 >= SMART_EVENT_PHASE_MAX ||
|
||||
e.action.randomPhase.phase3 >= SMART_EVENT_PHASE_MAX ||
|
||||
e.action.randomPhase.phase4 >= SMART_EVENT_PHASE_MAX ||
|
||||
e.action.randomPhase.phase5 >= SMART_EVENT_PHASE_MAX ||
|
||||
e.action.randomPhase.phase6 >= SMART_EVENT_PHASE_MAX)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
return false;
|
||||
}
|
||||
if (e.action.randomPhase.phase1 == 0 &&
|
||||
e.action.randomPhase.phase2 == 0 &&
|
||||
e.action.randomPhase.phase3 == 0 &&
|
||||
e.action.randomPhase.phase4 == 0 &&
|
||||
e.action.randomPhase.phase5 == 0 &&
|
||||
e.action.randomPhase.phase6 == 0)
|
||||
if (std::all_of(e.action.randomPhase.phases.begin(), e.action.randomPhase.phases.end(), [](uint32 phase) { return phase == 0; }))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} does not have any non-zero phase",
|
||||
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (std::any_of(e.action.randomPhase.phases.begin(), e.action.randomPhase.phases.end(), [](uint32 phase) { return phase >= SMART_EVENT_PHASE_MAX; }))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SMART_ACTION_RANDOM_PHASE_RANGE: //PhaseMin, PhaseMax
|
||||
{
|
||||
if (e.action.randomPhaseRange.phaseMin >= SMART_EVENT_PHASE_MAX ||
|
||||
e.action.randomPhaseRange.phaseMax >= SMART_EVENT_PHASE_MAX)
|
||||
e.action.randomPhaseRange.phaseMax >= SMART_EVENT_PHASE_MAX)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} attempts to set invalid phase, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!IsMinMaxValid(e, e.action.randomPhaseRange.phaseMin, e.action.randomPhaseRange.phaseMax))
|
||||
return false;
|
||||
break;
|
||||
@@ -1570,7 +1578,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
}
|
||||
case SMART_ACTION_CALL_RANDOM_RANGE_TIMED_ACTIONLIST:
|
||||
{
|
||||
if (!IsMinMaxValid(e, e.action.randRangeTimedActionList.idMin, e.action.randRangeTimedActionList.idMax))
|
||||
if (!IsMinMaxValid(e, e.action.randTimedActionList.actionLists[0], e.action.randTimedActionList.actionLists[1]))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
@@ -1625,12 +1633,10 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
{
|
||||
if (e.GetScriptType() == SMART_SCRIPT_TYPE_CREATURE)
|
||||
{
|
||||
int8 equipId = (int8)e.action.equip.entry;
|
||||
|
||||
if (equipId)
|
||||
if (int8 equipId = static_cast<int8>(e.action.equip.entry))
|
||||
{
|
||||
EquipmentInfo const* einfo = sObjectMgr->GetEquipmentInfo(e.entryOrGuid, equipId);
|
||||
if (!einfo)
|
||||
EquipmentInfo const* eInfo = sObjectMgr->GetEquipmentInfo(e.entryOrGuid, equipId);
|
||||
if (!eInfo)
|
||||
{
|
||||
LOG_ERROR("scripts.ai.sai", "SmartScript: SMART_ACTION_EQUIP uses non-existent equipment info id {} for creature {}, skipped.", equipId, e.entryOrGuid);
|
||||
return false;
|
||||
@@ -1783,7 +1789,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
case SMART_ACTION_MOVE_TO_POS:
|
||||
case SMART_ACTION_EVADE:
|
||||
case SMART_ACTION_SET_ACTIVE:
|
||||
case SMART_ACTION_START_CLOSEST_WAYPOINT:
|
||||
case SMART_ACTION_FOLLOW:
|
||||
case SMART_ACTION_SET_ORIENTATION:
|
||||
case SMART_ACTION_STORE_TARGET_LIST:
|
||||
@@ -1818,7 +1823,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
|
||||
case SMART_ACTION_SET_NPC_FLAG:
|
||||
case SMART_ACTION_ADD_NPC_FLAG:
|
||||
case SMART_ACTION_REMOVE_NPC_FLAG:
|
||||
case SMART_ACTION_CALL_RANDOM_TIMED_ACTIONLIST:
|
||||
case SMART_ACTION_RANDOM_MOVE:
|
||||
case SMART_ACTION_SET_UNIT_FIELD_BYTES_1:
|
||||
case SMART_ACTION_REMOVE_UNIT_FIELD_BYTES_1:
|
||||
|
||||
@@ -49,6 +49,16 @@ struct WayPoint
|
||||
uint32 delay;
|
||||
};
|
||||
|
||||
enum eSmartAI
|
||||
{
|
||||
SMART_EVENT_PARAM_COUNT = 4,
|
||||
SMART_ACTION_PARAM_COUNT = 6,
|
||||
SMART_SUMMON_COUNTER = 0xFFFFFF,
|
||||
SMART_ESCORT_LAST_OOC_POINT = 0xFFFFFF,
|
||||
SMART_RANDOM_POINT = 0xFFFFFE,
|
||||
SMART_ESCORT_TARGETS = 0xFFFFFF
|
||||
};
|
||||
|
||||
enum SMART_EVENT_PHASE
|
||||
{
|
||||
SMART_EVENT_PHASE_ALWAYS = 0,
|
||||
@@ -758,12 +768,7 @@ struct SmartAction
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 emote1;
|
||||
uint32 emote2;
|
||||
uint32 emote3;
|
||||
uint32 emote4;
|
||||
uint32 emote5;
|
||||
uint32 emote6;
|
||||
std::array<uint32, SMART_ACTION_PARAM_COUNT> emotes;
|
||||
} randomEmote;
|
||||
|
||||
struct
|
||||
@@ -858,12 +863,7 @@ struct SmartAction
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 phase1;
|
||||
uint32 phase2;
|
||||
uint32 phase3;
|
||||
uint32 phase4;
|
||||
uint32 phase5;
|
||||
uint32 phase6;
|
||||
std::array<uint32, SMART_ACTION_PARAM_COUNT> phases;
|
||||
} randomPhase;
|
||||
|
||||
struct
|
||||
@@ -1049,9 +1049,7 @@ struct SmartAction
|
||||
{
|
||||
uint32 entry;
|
||||
uint32 mask;
|
||||
uint32 slot1;
|
||||
uint32 slot2;
|
||||
uint32 slot3;
|
||||
std::array<uint32, MAX_EQUIPMENT_ITEMS> slots;
|
||||
} equip;
|
||||
|
||||
struct
|
||||
@@ -1086,12 +1084,7 @@ struct SmartAction
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 entry1;
|
||||
uint32 entry2;
|
||||
uint32 entry3;
|
||||
uint32 entry4;
|
||||
uint32 entry5;
|
||||
uint32 entry6;
|
||||
std::array<uint32, SMART_ACTION_PARAM_COUNT> actionLists;
|
||||
} randTimedActionList;
|
||||
|
||||
struct
|
||||
@@ -1209,12 +1202,7 @@ struct SmartAction
|
||||
|
||||
struct
|
||||
{
|
||||
uint32 wp1;
|
||||
uint32 wp2;
|
||||
uint32 wp3;
|
||||
uint32 wp4;
|
||||
uint32 wp5;
|
||||
uint32 wp6;
|
||||
std::array<uint32, SMART_ACTION_PARAM_COUNT> wps;
|
||||
} closestWaypointFromList;
|
||||
|
||||
struct
|
||||
@@ -1584,16 +1572,6 @@ enum SmartTargetRoleFlags
|
||||
SMART_TARGET_ROLE_FLAG_DAMAGERS = 0x004
|
||||
};
|
||||
|
||||
enum eSmartAI
|
||||
{
|
||||
SMART_EVENT_PARAM_COUNT = 4,
|
||||
SMART_ACTION_PARAM_COUNT = 6,
|
||||
SMART_SUMMON_COUNTER = 0xFFFFFF,
|
||||
SMART_ESCORT_LAST_OOC_POINT = 0xFFFFFF,
|
||||
SMART_RANDOM_POINT = 0xFFFFFE,
|
||||
SMART_ESCORT_TARGETS = 0xFFFFFF
|
||||
};
|
||||
|
||||
enum SmartScriptType
|
||||
{
|
||||
SMART_SCRIPT_TYPE_CREATURE = 0, //done
|
||||
@@ -1802,60 +1780,42 @@ public:
|
||||
|
||||
typedef std::unordered_map<uint32, WayPoint*> WPPath;
|
||||
|
||||
typedef std::list<WorldObject*> ObjectList;
|
||||
typedef std::vector<WorldObject*> ObjectVector;
|
||||
|
||||
class ObjectGuidList
|
||||
class ObjectGuidVector
|
||||
{
|
||||
ObjectList* m_objectList;
|
||||
GuidList* m_guidList;
|
||||
WorldObject* m_baseObject;
|
||||
|
||||
public:
|
||||
ObjectGuidList(ObjectList* objectList, WorldObject* baseObject)
|
||||
explicit ObjectGuidVector(ObjectVector const& objectVector) : _objectVector(objectVector)
|
||||
{
|
||||
ASSERT(objectList);
|
||||
m_objectList = objectList;
|
||||
m_baseObject = baseObject;
|
||||
m_guidList = new GuidList();
|
||||
|
||||
for (ObjectList::iterator itr = objectList->begin(); itr != objectList->end(); ++itr)
|
||||
{
|
||||
m_guidList->push_back((*itr)->GetGUID());
|
||||
}
|
||||
_guidVector.reserve(_objectVector.size());
|
||||
for (WorldObject* obj : _objectVector)
|
||||
_guidVector.push_back(obj->GetGUID());
|
||||
}
|
||||
|
||||
ObjectList* GetObjectList()
|
||||
ObjectVector const* GetObjectVector(WorldObject const& ref) const
|
||||
{
|
||||
if (m_baseObject)
|
||||
{
|
||||
//sanitize list using m_guidList
|
||||
m_objectList->clear();
|
||||
|
||||
for (GuidList::iterator itr = m_guidList->begin(); itr != m_guidList->end(); ++itr)
|
||||
{
|
||||
if (WorldObject* obj = ObjectAccessor::GetWorldObject(*m_baseObject, *itr))
|
||||
m_objectList->push_back(obj);
|
||||
//else
|
||||
// LOG_DEBUG("scripts.ai", "SmartScript::mTargetStorage stores a guid to an invalid object: {}", (*itr).ToString());
|
||||
}
|
||||
}
|
||||
|
||||
return m_objectList;
|
||||
UpdateObjects(ref);
|
||||
return &_objectVector;
|
||||
}
|
||||
|
||||
bool Equals(ObjectList* objectList)
|
||||
{
|
||||
return m_objectList == objectList;
|
||||
}
|
||||
~ObjectGuidVector() { }
|
||||
|
||||
~ObjectGuidList()
|
||||
private:
|
||||
mutable ObjectVector _objectVector;
|
||||
|
||||
GuidVector _guidVector;
|
||||
|
||||
//sanitize vector using _guidVector
|
||||
void UpdateObjects(WorldObject const& ref) const
|
||||
{
|
||||
delete m_objectList;
|
||||
delete m_guidList;
|
||||
_objectVector.clear();
|
||||
|
||||
for (ObjectGuid const& guid : _guidVector)
|
||||
if (WorldObject* obj = ObjectAccessor::GetWorldObject(ref, guid))
|
||||
_objectVector.push_back(obj);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::unordered_map<uint32, ObjectGuidList*> ObjectListMap;
|
||||
typedef std::unordered_map<uint32, ObjectGuidVector> ObjectVectorMap;
|
||||
|
||||
class SmartWaypointMgr
|
||||
{
|
||||
|
||||
@@ -261,12 +261,18 @@ HostileReference* ThreatContainer::getReferenceByTarget(Unit* victim) const
|
||||
if (!victim)
|
||||
return nullptr;
|
||||
|
||||
ObjectGuid const guid = victim->GetGUID();
|
||||
return getReferenceByTarget(victim->GetGUID());
|
||||
}
|
||||
|
||||
HostileReference* ThreatContainer::getReferenceByTarget(ObjectGuid const& guid) const
|
||||
{
|
||||
for (ThreatContainer::StorageType::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
|
||||
{
|
||||
HostileReference* ref = (*i);
|
||||
if (ref && ref->getUnitGuid() == guid)
|
||||
{
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@@ -164,6 +164,7 @@ public:
|
||||
}
|
||||
|
||||
HostileReference* getReferenceByTarget(Unit* victim) const;
|
||||
HostileReference* getReferenceByTarget(ObjectGuid const& guid) const;
|
||||
|
||||
[[nodiscard]] StorageType const& getThreatList() const { return iThreatList; }
|
||||
|
||||
|
||||
@@ -219,8 +219,8 @@ Creature::Creature(bool isWorldObject): Unit(isWorldObject), MovableMapObject(),
|
||||
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),
|
||||
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), _lastDamagedTime(nullptr), m_cannotReachTarget(false), m_cannotReachTimer(0),
|
||||
m_AlreadySearchedAssistance(false), m_regenHealth(true), m_regenPower(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_moveInLineOfSightDisabled(false), m_moveInLineOfSightStrictlyDisabled(false),
|
||||
m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_detectionDistance(20.0f), m_waypointID(0), m_path_id(0), m_formation(nullptr), _lastDamagedTime(nullptr), m_cannotReachTimer(0),
|
||||
_isMissingSwimmingFlagOutOfCombat(false), m_assistanceTimer(0), _playerDamageReq(0), _damagedByPlayer(false)
|
||||
{
|
||||
m_regenTimer = CREATURE_REGEN_INTERVAL;
|
||||
@@ -628,207 +628,238 @@ void Creature::Update(uint32 diff)
|
||||
LOG_ERROR("entities.unit", "Creature ({}) in wrong state: JUST_DEAD (1)", GetGUID().ToString());
|
||||
break;
|
||||
case DEAD:
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
if (m_respawnTime <= now)
|
||||
{
|
||||
time_t now = GameTime::GetGameTime().count();
|
||||
if (m_respawnTime <= now)
|
||||
|
||||
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_RESPAWN, GetEntry());
|
||||
|
||||
if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
|
||||
{
|
||||
|
||||
ConditionList conditions = sConditionMgr->GetConditionsForNotGroupedEntry(CONDITION_SOURCE_TYPE_CREATURE_RESPAWN, GetEntry());
|
||||
|
||||
if (!sConditionMgr->IsObjectMeetToConditions(this, conditions))
|
||||
{
|
||||
// Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn.
|
||||
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay;
|
||||
break;
|
||||
}
|
||||
|
||||
bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning
|
||||
if (!allowed) // Will be rechecked on next Update call
|
||||
break;
|
||||
|
||||
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(GetEntry(), m_spawnId);
|
||||
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
|
||||
if (!linkedRespawntime) // Can respawn
|
||||
Respawn();
|
||||
else // the master is dead
|
||||
{
|
||||
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
|
||||
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
|
||||
SetRespawnTime(DAY);
|
||||
else
|
||||
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
|
||||
SaveRespawnTime(); // also save to DB immediately
|
||||
}
|
||||
// Creature should not respawn, reset respawn timer. Conditions will be checked again the next time it tries to respawn.
|
||||
m_respawnTime = GameTime::GetGameTime().count() + m_respawnDelay;
|
||||
break;
|
||||
}
|
||||
|
||||
bool allowed = !IsAIEnabled || AI()->CanRespawn(); // First check if there are any scripts that prevent us respawning
|
||||
if (!allowed) // Will be rechecked on next Update call
|
||||
break;
|
||||
|
||||
ObjectGuid dbtableHighGuid = ObjectGuid::Create<HighGuid::Unit>(GetEntry(), m_spawnId);
|
||||
time_t linkedRespawntime = GetMap()->GetLinkedRespawnTime(dbtableHighGuid);
|
||||
if (!linkedRespawntime) // Can respawn
|
||||
Respawn();
|
||||
else // the master is dead
|
||||
{
|
||||
ObjectGuid targetGuid = sObjectMgr->GetLinkedRespawnGuid(dbtableHighGuid);
|
||||
if (targetGuid == dbtableHighGuid) // if linking self, never respawn (check delayed to next day)
|
||||
SetRespawnTime(DAY);
|
||||
else
|
||||
m_respawnTime = (now > linkedRespawntime ? now : linkedRespawntime) + urand(5, MINUTE); // else copy time from master and add a little
|
||||
SaveRespawnTime(); // also save to DB immediately
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CORPSE:
|
||||
{
|
||||
Unit::Update(diff);
|
||||
// deathstate changed on spells update, prevent problems
|
||||
if (m_deathState != CORPSE)
|
||||
break;
|
||||
|
||||
if (m_groupLootTimer && lootingGroupLowGUID)
|
||||
{
|
||||
if (m_groupLootTimer <= diff)
|
||||
{
|
||||
Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID);
|
||||
if (group)
|
||||
group->EndRoll(&loot, GetMap());
|
||||
m_groupLootTimer = 0;
|
||||
lootingGroupLowGUID = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_groupLootTimer -= diff;
|
||||
}
|
||||
}
|
||||
else if (m_corpseRemoveTime <= GameTime::GetGameTime().count())
|
||||
{
|
||||
RemoveCorpse(false);
|
||||
LOG_DEBUG("entities.unit", "Removing corpse... {} ", GetUInt32Value(OBJECT_FIELD_ENTRY));
|
||||
}
|
||||
{
|
||||
Unit::Update(diff);
|
||||
// deathstate changed on spells update, prevent problems
|
||||
if (m_deathState != CORPSE)
|
||||
break;
|
||||
}
|
||||
case ALIVE:
|
||||
|
||||
if (m_groupLootTimer && lootingGroupLowGUID)
|
||||
{
|
||||
Unit::Update(diff);
|
||||
|
||||
// creature can be dead after Unit::Update call
|
||||
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
|
||||
if (!IsAlive())
|
||||
break;
|
||||
|
||||
// if creature is charmed, switch to charmed AI
|
||||
if (NeedChangeAI)
|
||||
if (m_groupLootTimer <= diff)
|
||||
{
|
||||
UpdateCharmAI();
|
||||
NeedChangeAI = false;
|
||||
IsAIEnabled = true;
|
||||
Group* group = sGroupMgr->GetGroupByGUID(lootingGroupLowGUID);
|
||||
if (group)
|
||||
group->EndRoll(&loot, GetMap());
|
||||
m_groupLootTimer = 0;
|
||||
lootingGroupLowGUID = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_groupLootTimer -= diff;
|
||||
}
|
||||
}
|
||||
else if (m_corpseRemoveTime <= GameTime::GetGameTime().count())
|
||||
{
|
||||
RemoveCorpse(false);
|
||||
LOG_DEBUG("entities.unit", "Removing corpse... {} ", GetUInt32Value(OBJECT_FIELD_ENTRY));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ALIVE:
|
||||
{
|
||||
Unit::Update(diff);
|
||||
|
||||
// xinef: update combat state, if npc is not in combat - return to spawn correctly by calling EnterEvadeMode
|
||||
SelectVictim();
|
||||
// creature can be dead after Unit::Update call
|
||||
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
|
||||
if (!IsAlive())
|
||||
break;
|
||||
|
||||
// if creature is charmed, switch to charmed AI
|
||||
if (NeedChangeAI)
|
||||
{
|
||||
UpdateCharmAI();
|
||||
NeedChangeAI = false;
|
||||
IsAIEnabled = true;
|
||||
|
||||
// xinef: update combat state, if npc is not in combat - return to spawn correctly by calling EnterEvadeMode
|
||||
SelectVictim();
|
||||
}
|
||||
|
||||
// periodic check to see if the creature has passed an evade boundary
|
||||
if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
|
||||
{
|
||||
if (diff >= m_boundaryCheckTime)
|
||||
{
|
||||
AI()->CheckInRoom();
|
||||
m_boundaryCheckTime = 2500;
|
||||
}
|
||||
else
|
||||
m_boundaryCheckTime -= diff;
|
||||
}
|
||||
|
||||
Unit* owner = GetCharmerOrOwner();
|
||||
if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false))
|
||||
{
|
||||
RemoveCharmAuras();
|
||||
}
|
||||
|
||||
if (Unit* victim = GetVictim())
|
||||
{
|
||||
// If we are closer than 50% of the combat reach we are going to reposition the victim
|
||||
if (diff >= m_moveBackwardsMovementTime)
|
||||
{
|
||||
float MaxRange = GetCollisionRadius() + GetVictim()->GetCollisionRadius();
|
||||
|
||||
if (IsInDist(victim, MaxRange))
|
||||
AI()->MoveBackwardsChecks();
|
||||
|
||||
m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL, MOVE_BACKWARDS_CHECK_INTERVAL * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_moveBackwardsMovementTime -= diff;
|
||||
}
|
||||
|
||||
// periodic check to see if the creature has passed an evade boundary
|
||||
if (IsAIEnabled && !IsInEvadeMode() && IsEngaged())
|
||||
// Circling the target
|
||||
if (diff >= m_moveCircleMovementTime)
|
||||
{
|
||||
if (diff >= m_boundaryCheckTime)
|
||||
{
|
||||
AI()->CheckInRoom();
|
||||
m_boundaryCheckTime = 2500;
|
||||
} else
|
||||
m_boundaryCheckTime -= diff;
|
||||
AI()->MoveCircleChecks();
|
||||
m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL, MOVE_CIRCLE_CHECK_INTERVAL * 2);
|
||||
}
|
||||
|
||||
Unit* owner = GetCharmerOrOwner();
|
||||
if (IsCharmed() && !IsWithinDistInMap(owner, GetMap()->GetVisibilityRange(), true, false))
|
||||
else
|
||||
{
|
||||
RemoveCharmAuras();
|
||||
m_moveCircleMovementTime -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (Unit *victim = GetVictim())
|
||||
// Call for assistance if not disabled
|
||||
if (m_assistanceTimer)
|
||||
{
|
||||
if (m_assistanceTimer <= diff)
|
||||
{
|
||||
// If we are closer than 50% of the combat reach we are going to reposition the victim
|
||||
if (diff >= m_moveBackwardsMovementTime)
|
||||
if (CanPeriodicallyCallForAssistance())
|
||||
{
|
||||
float MaxRange = GetCollisionRadius() + GetVictim()->GetCollisionRadius();
|
||||
|
||||
if (IsInDist(victim, MaxRange))
|
||||
AI()->MoveBackwardsChecks();
|
||||
|
||||
m_moveBackwardsMovementTime = urand(MOVE_BACKWARDS_CHECK_INTERVAL, MOVE_BACKWARDS_CHECK_INTERVAL * 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_moveBackwardsMovementTime -= diff;
|
||||
}
|
||||
|
||||
// Circling the target
|
||||
if (diff >= m_moveCircleMovementTime)
|
||||
{
|
||||
AI()->MoveCircleChecks();
|
||||
m_moveCircleMovementTime = urand(MOVE_CIRCLE_CHECK_INTERVAL, MOVE_CIRCLE_CHECK_INTERVAL * 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_moveCircleMovementTime -= diff;
|
||||
SetNoCallAssistance(false);
|
||||
CallAssistance();
|
||||
}
|
||||
m_assistanceTimer = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD);
|
||||
}
|
||||
|
||||
// Call for assistance if not disabled
|
||||
if (m_assistanceTimer)
|
||||
else
|
||||
{
|
||||
if (m_assistanceTimer <= diff)
|
||||
m_assistanceTimer -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsInEvadeMode() && IsAIEnabled)
|
||||
{
|
||||
// do not allow the AI to be changed during update
|
||||
m_AI_locked = true;
|
||||
i_AI->UpdateAI(diff);
|
||||
m_AI_locked = false;
|
||||
}
|
||||
|
||||
// creature can be dead after UpdateAI call
|
||||
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
|
||||
if (!IsAlive())
|
||||
break;
|
||||
|
||||
m_regenTimer -= diff;
|
||||
if (m_regenTimer <= 0)
|
||||
{
|
||||
if (!IsInEvadeMode())
|
||||
{
|
||||
// regenerate health if not in combat or if polymorphed)
|
||||
if (!IsInCombat() || IsPolymorphed())
|
||||
RegenerateHealth();
|
||||
else if (IsNotReachableAndNeedRegen())
|
||||
{
|
||||
if (CanPeriodicallyCallForAssistance())
|
||||
// regenerate health if cannot reach the target and the setting is set to do so.
|
||||
// this allows to disable the health regen of raid bosses if pathfinding has issues for whatever reason
|
||||
if (sWorld->getBoolConfig(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID) || !GetMap()->IsRaid())
|
||||
{
|
||||
SetNoCallAssistance(false);
|
||||
CallAssistance();
|
||||
}
|
||||
m_assistanceTimer = sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_PERIOD);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_assistanceTimer -= diff;
|
||||
}
|
||||
}
|
||||
|
||||
if (!IsInEvadeMode() && IsAIEnabled)
|
||||
{
|
||||
// do not allow the AI to be changed during update
|
||||
m_AI_locked = true;
|
||||
i_AI->UpdateAI(diff);
|
||||
m_AI_locked = false;
|
||||
}
|
||||
|
||||
// creature can be dead after UpdateAI call
|
||||
// CORPSE/DEAD state will processed at next tick (in other case death timer will be updated unexpectedly)
|
||||
if (!IsAlive())
|
||||
break;
|
||||
|
||||
m_regenTimer -= diff;
|
||||
if (m_regenTimer <= 0)
|
||||
{
|
||||
if (!IsInEvadeMode())
|
||||
{
|
||||
// regenerate health if not in combat or if polymorphed)
|
||||
if (!IsInCombat() || IsPolymorphed())
|
||||
RegenerateHealth();
|
||||
else if (IsNotReachableAndNeedRegen())
|
||||
LOG_DEBUG("entities.unit", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo());
|
||||
}
|
||||
else
|
||||
LOG_DEBUG("entities.unit", "RegenerateHealth() disabled even if the Creature cannot reach the target. Detail: {}", GetDebugInfo());
|
||||
}
|
||||
}
|
||||
|
||||
if (getPowerType() == POWER_ENERGY)
|
||||
Regenerate(POWER_ENERGY);
|
||||
else
|
||||
Regenerate(POWER_MANA);
|
||||
|
||||
m_regenTimer += CREATURE_REGEN_INTERVAL;
|
||||
}
|
||||
|
||||
if (CanNotReachTarget() && !IsInEvadeMode())
|
||||
{
|
||||
m_cannotReachTimer += diff;
|
||||
if (m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_EVADE_IF_NOT_REACHABLE) * IN_MILLISECONDS))
|
||||
{
|
||||
Player* cannotReachPlayer = ObjectAccessor::GetPlayer(*this, m_cannotReachTarget);
|
||||
if (cannotReachPlayer && IsEngagedBy(cannotReachPlayer) && IsAIEnabled && AI()->OnTeleportUnreacheablePlayer(cannotReachPlayer))
|
||||
{
|
||||
SetCannotReachTarget();
|
||||
}
|
||||
else if (!GetMap()->IsRaid())
|
||||
{
|
||||
auto EnterEvade = [&]()
|
||||
{
|
||||
// regenerate health if cannot reach the target and the setting is set to do so.
|
||||
// this allows to disable the health regen of raid bosses if pathfinding has issues for whatever reason
|
||||
if (sWorld->getBoolConfig(CONFIG_REGEN_HP_CANNOT_REACH_TARGET_IN_RAID) || !GetMap()->IsRaid())
|
||||
if (CreatureAI* ai = AI())
|
||||
{
|
||||
RegenerateHealth();
|
||||
LOG_DEBUG("entities.unit", "RegenerateHealth() enabled because Creature cannot reach the target. Detail: {}", GetDebugInfo());
|
||||
ai->EnterEvadeMode(CreatureAI::EvadeReason::EVADE_REASON_NO_PATH);
|
||||
}
|
||||
};
|
||||
|
||||
if (GetThreatMgr().getThreatList().size() <= 1)
|
||||
{
|
||||
EnterEvade();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (HostileReference* ref = GetThreatMgr().getOnlineContainer().getReferenceByTarget(m_cannotReachTarget))
|
||||
{
|
||||
ref->removeReference();
|
||||
SetCannotReachTarget();
|
||||
}
|
||||
else
|
||||
LOG_DEBUG("entities.unit", "RegenerateHealth() disabled even if the Creature cannot reach the target. Detail: {}", GetDebugInfo());
|
||||
{
|
||||
EnterEvade();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getPowerType() == POWER_ENERGY)
|
||||
Regenerate(POWER_ENERGY);
|
||||
else
|
||||
Regenerate(POWER_MANA);
|
||||
|
||||
m_regenTimer += CREATURE_REGEN_INTERVAL;
|
||||
}
|
||||
|
||||
if (CanNotReachTarget() && !IsInEvadeMode() && !GetMap()->IsRaid())
|
||||
{
|
||||
m_cannotReachTimer += diff;
|
||||
if (IsNotReachable() && IsAIEnabled)
|
||||
{
|
||||
AI()->EnterEvadeMode();
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -883,6 +914,11 @@ void Creature::Regenerate(Powers power)
|
||||
if (!HasUnitFlag2(UNIT_FLAG2_REGENERATE_POWER) && !GetOwnerGUID().IsPlayer())
|
||||
return;
|
||||
|
||||
if (!m_regenPower)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (curValue >= maxValue)
|
||||
return;
|
||||
|
||||
@@ -1020,7 +1056,7 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
|
||||
// Xinef: called in add to world
|
||||
//Motion_Initialize();
|
||||
|
||||
i_AI = ai ? ai : FactorySelector::selectAI(this);
|
||||
i_AI = ai ? ai : FactorySelector::SelectAI(this);
|
||||
delete oldAI;
|
||||
IsAIEnabled = true;
|
||||
i_AI->InitializeAI();
|
||||
@@ -1927,7 +1963,7 @@ void Creature::setDeathState(DeathState s, bool despawn)
|
||||
SetFullHealth();
|
||||
SetLootRecipient(nullptr);
|
||||
ResetPlayerDamageReq();
|
||||
SetCannotReachTarget(false);
|
||||
SetCannotReachTarget();
|
||||
CreatureTemplate const* cinfo = GetCreatureTemplate();
|
||||
// Xinef: npc run by default
|
||||
//SetWalk(true);
|
||||
@@ -2861,7 +2897,7 @@ uint8 Creature::getLevelForTarget(WorldObject const* target) const
|
||||
return uint8(level);
|
||||
}
|
||||
|
||||
std::string Creature::GetAIName() const
|
||||
std::string const& Creature::GetAIName() const
|
||||
{
|
||||
return sObjectMgr->GetCreatureTemplate(GetEntry())->AIName;
|
||||
}
|
||||
@@ -3468,7 +3504,24 @@ bool Creature::SetCannotReachTarget(bool cannotReach, bool isChase /*= true*/)
|
||||
m_cannotReachTimer = 0;
|
||||
|
||||
if (cannotReach)
|
||||
{
|
||||
LOG_DEBUG("entities.unit", "Creature::SetCannotReachTarget() called with true. Details: {}", GetDebugInfo());
|
||||
}
|
||||
}
|
||||
|
||||
bool Creature::CanNotReachTarget() const
|
||||
{
|
||||
return m_cannotReachTarget;
|
||||
}
|
||||
|
||||
bool Creature::IsNotReachableAndNeedRegen() const
|
||||
{
|
||||
if (CanNotReachTarget())
|
||||
{
|
||||
return m_cannotReachTimer >= (sWorld->getIntConfig(CONFIG_NPC_REGEN_TIME_IF_NOT_REACHABLE_IN_RAID) * IN_MILLISECONDS);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
time_t Creature::GetLastDamagedTime() const
|
||||
|
||||
@@ -198,7 +198,7 @@ public:
|
||||
void SetDetectionDistance(float dist){ m_detectionDistance = dist; }
|
||||
[[nodiscard]] CreatureAddon const* GetCreatureAddon() const;
|
||||
|
||||
[[nodiscard]] std::string GetAIName() const;
|
||||
[[nodiscard]] std::string const& GetAIName() const;
|
||||
[[nodiscard]] std::string GetScriptName() const;
|
||||
[[nodiscard]] uint32 GetScriptId() const;
|
||||
|
||||
@@ -300,6 +300,7 @@ public:
|
||||
|
||||
bool isRegeneratingHealth() { return m_regenHealth; }
|
||||
void SetRegeneratingHealth(bool c) { m_regenHealth = c; }
|
||||
void SetRegeneratingPower(bool c) { m_regenPower = c; }
|
||||
[[nodiscard]] virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; }
|
||||
[[nodiscard]] virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const
|
||||
{
|
||||
@@ -417,6 +418,7 @@ protected:
|
||||
bool m_AlreadyCallAssistance;
|
||||
bool m_AlreadySearchedAssistance;
|
||||
bool m_regenHealth;
|
||||
bool m_regenPower;
|
||||
bool m_AI_locked;
|
||||
|
||||
SpellSchoolMask m_meleeDamageSchoolMask;
|
||||
@@ -455,7 +457,7 @@ private:
|
||||
|
||||
mutable std::shared_ptr<time_t> _lastDamagedTime; // Part of Evade mechanics
|
||||
|
||||
bool m_cannotReachTarget;
|
||||
ObjectGuid m_cannotReachTarget;
|
||||
uint32 m_cannotReachTimer;
|
||||
|
||||
Spell const* _focusSpell; ///> Locks the target during spell cast for proper facing
|
||||
|
||||
@@ -80,8 +80,7 @@ enum CreatureFlagsExtra : uint32
|
||||
CREATURE_FLAG_EXTRA_HARD_RESET = 0x80000000,
|
||||
|
||||
// Masks
|
||||
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12 | CREATURE_FLAG_EXTRA_UNUSED_26 |
|
||||
CREATURE_FLAG_EXTRA_UNUSED_27 | CREATURE_FLAG_EXTRA_UNUSED_28), // SKIP
|
||||
CREATURE_FLAG_EXTRA_UNUSED = (CREATURE_FLAG_EXTRA_UNUSED_12 | CREATURE_FLAG_EXTRA_UNUSED_26 | CREATURE_FLAG_EXTRA_UNUSED_27 | CREATURE_FLAG_EXTRA_UNUSED_28), // SKIP
|
||||
|
||||
CREATURE_FLAG_EXTRA_DB_ALLOWED = (0xFFFFFFFF & ~(CREATURE_FLAG_EXTRA_UNUSED | CREATURE_FLAG_EXTRA_DUNGEON_BOSS)) // SKIP
|
||||
};
|
||||
|
||||
@@ -381,3 +381,40 @@ void CreatureGroup::LeaderMoveTo(float x, float y, float z, bool run)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CreatureGroup::RespawnFormation(bool force)
|
||||
{
|
||||
for (auto const& itr : m_members)
|
||||
{
|
||||
if (itr.first && !itr.first->IsAlive())
|
||||
{
|
||||
itr.first->Respawn(force);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CreatureGroup::IsFormationInCombat()
|
||||
{
|
||||
for (auto const& itr : m_members)
|
||||
{
|
||||
if (itr.first && itr.first->IsInCombat())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CreatureGroup::IsAnyMemberAlive()
|
||||
{
|
||||
for (auto const& itr : m_members)
|
||||
{
|
||||
if (itr.first && itr.first->IsAlive())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -109,6 +109,9 @@ public:
|
||||
void LeaderMoveTo(float x, float y, float z, bool run);
|
||||
void MemberEngagingTarget(Creature* member, Unit* target);
|
||||
void MemberEvaded(Creature* member);
|
||||
void RespawnFormation(bool force = false);
|
||||
[[nodiscard]] bool IsFormationInCombat();
|
||||
[[nodiscard]] bool IsAnyMemberAlive();
|
||||
|
||||
private:
|
||||
Creature* m_leader; //Important do not forget sometimes to work with pointers instead synonims :D:D
|
||||
|
||||
@@ -96,7 +96,7 @@ bool GameObject::AIM_Initialize()
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string GameObject::GetAIName() const
|
||||
std::string const& GameObject::GetAIName() const
|
||||
{
|
||||
return sObjectMgr->GetGameObjectTemplate(GetEntry())->AIName;
|
||||
}
|
||||
|
||||
@@ -1001,7 +1001,7 @@ public:
|
||||
[[nodiscard]] virtual uint32 GetScriptId() const;
|
||||
[[nodiscard]] GameObjectAI* AI() const { return m_AI; }
|
||||
|
||||
[[nodiscard]] std::string GetAIName() const;
|
||||
[[nodiscard]] std::string const& GetAIName() const;
|
||||
void SetDisplayId(uint32 displayid);
|
||||
[[nodiscard]] uint32 GetDisplayId() const { return GetUInt32Value(GAMEOBJECT_DISPLAYID); }
|
||||
|
||||
|
||||
@@ -1154,7 +1154,9 @@ bool Guardian::InitStatsForLevel(uint8 petlevel)
|
||||
{
|
||||
// xinef: Glyph of Felguard, so ugly im crying... no appropriate spell
|
||||
if (AuraEffect* aurEff = owner->GetAuraEffectDummy(SPELL_GLYPH_OF_FELGUARD))
|
||||
SetModifierValue(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, 1.0f + float(aurEff->GetAmount() / 100.0f));
|
||||
{
|
||||
HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, aurEff->GetAmount(), true);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -8911,7 +8911,7 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
|
||||
return;
|
||||
}
|
||||
|
||||
if (returnreagent && (pet || m_temporaryUnsummonedPetNumber) && !InBattleground())
|
||||
if (returnreagent && (pet || (m_temporaryUnsummonedPetNumber && (!m_session || !m_session->PlayerLogout()))) && !InBattleground())
|
||||
{
|
||||
//returning of reagents only for players, so best done here
|
||||
uint32 spellId = pet ? pet->GetUInt32Value(UNIT_CREATED_BY_SPELL) : m_oldpetspell;
|
||||
@@ -12573,8 +12573,13 @@ void Player::SetMover(Unit* target)
|
||||
LOG_INFO("misc", "Player::SetMover (B2) - {}, {}, {}, {}, {}, {}, {}, {}", target->GetGUID().ToString(), target->GetMapId(), target->GetInstanceId(), target->FindMap()->GetId(), target->IsInWorld() ? 1 : 0, target->IsDuringRemoveFromWorld() ? 1 : 0, (target->ToPlayer() && target->ToPlayer()->IsBeingTeleported() ? 1 : 0), target->isBeingLoaded() ? 1 : 0);
|
||||
}
|
||||
m_mover->m_movedByPlayer = nullptr;
|
||||
if (m_mover->GetTypeId() == TYPEID_UNIT)
|
||||
m_mover->GetMotionMaster()->Initialize();
|
||||
|
||||
m_mover = target;
|
||||
m_mover->m_movedByPlayer = this;
|
||||
if (m_mover->GetTypeId() == TYPEID_UNIT)
|
||||
m_mover->GetMotionMaster()->Initialize();
|
||||
}
|
||||
|
||||
uint32 Player::GetCorpseReclaimDelay(bool pvp) const
|
||||
|
||||
@@ -289,8 +289,8 @@ typedef std::list<PlayerCreateInfoItem> PlayerCreateInfoItems;
|
||||
struct PlayerClassLevelInfo
|
||||
{
|
||||
PlayerClassLevelInfo() = default;
|
||||
uint16 basehealth{0};
|
||||
uint16 basemana{0};
|
||||
uint32 basehealth{0};
|
||||
uint32 basemana{0};
|
||||
};
|
||||
|
||||
struct PlayerClassInfo
|
||||
@@ -302,9 +302,12 @@ struct PlayerClassInfo
|
||||
|
||||
struct PlayerLevelInfo
|
||||
{
|
||||
PlayerLevelInfo() { for (unsigned char & stat : stats) stat = 0; }
|
||||
PlayerLevelInfo()
|
||||
{
|
||||
stats.fill(0);
|
||||
}
|
||||
|
||||
uint8 stats[MAX_STATS];
|
||||
std::array<uint32, MAX_STATS> stats = { };
|
||||
};
|
||||
|
||||
typedef std::list<uint32> PlayerCreateInfoSpells;
|
||||
@@ -2732,7 +2735,7 @@ public:
|
||||
ActionButtonList m_actionButtons;
|
||||
|
||||
float m_auraBaseMod[BASEMOD_END][MOD_END];
|
||||
int16 m_baseRatingValue[MAX_COMBAT_RATING];
|
||||
int32 m_baseRatingValue[MAX_COMBAT_RATING];
|
||||
uint32 m_baseSpellPower;
|
||||
uint32 m_baseFeralAP;
|
||||
uint32 m_baseManaRegen;
|
||||
|
||||
@@ -108,12 +108,11 @@ void Player::_SavePlayerSettings(CharacterDatabaseTransaction trans)
|
||||
void Player::UpdatePlayerSetting(std::string source, uint8 index, uint32 value)
|
||||
{
|
||||
auto itr = m_charSettingsMap.find(source);
|
||||
uint8 size = index + 1;
|
||||
|
||||
if (itr == m_charSettingsMap.end())
|
||||
{
|
||||
// Settings not found, initialize a new entry.
|
||||
uint8 size = index ? index : index + 1;
|
||||
|
||||
PlayerSettingVector setting;
|
||||
setting.resize(size);
|
||||
|
||||
@@ -129,6 +128,10 @@ void Player::UpdatePlayerSetting(std::string source, uint8 index, uint32 value)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (size > itr->second.size())
|
||||
{
|
||||
itr->second.resize(size);
|
||||
}
|
||||
itr->second[index].value = value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,20 +34,13 @@ Totem::Totem(SummonPropertiesEntry const* properties, ObjectGuid owner) : Minion
|
||||
void Totem::Update(uint32 time)
|
||||
{
|
||||
Unit* owner = GetOwner();
|
||||
if (!owner || !owner->IsAlive() || !IsAlive())
|
||||
if (!owner || !owner->IsAlive() || !IsAlive() || m_duration <= time)
|
||||
{
|
||||
UnSummon(); // remove self
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_duration <= time)
|
||||
{
|
||||
UnSummon(); // remove self
|
||||
return;
|
||||
}
|
||||
else
|
||||
m_duration -= time;
|
||||
|
||||
m_duration -= time;
|
||||
Creature::Update(time);
|
||||
}
|
||||
|
||||
|
||||
@@ -1034,7 +1034,14 @@ uint32 Unit::DealDamage(Unit* attacker, Unit* victim, uint32 damage, CleanDamage
|
||||
victim->ToCreature()->SetLastDamagedTime(GameTime::GetGameTime().count() + MAX_AGGRO_RESET_TIME);
|
||||
|
||||
if (attacker)
|
||||
{
|
||||
if (spellProto && !victim->IsInCombatWith(attacker))
|
||||
{
|
||||
victim->CombatStart(attacker, !(spellProto->AttributesEx3 & SPELL_ATTR3_SUPRESS_TARGET_PROCS));
|
||||
}
|
||||
|
||||
victim->AddThreat(attacker, float(damage), damageSchoolMask, spellProto);
|
||||
}
|
||||
}
|
||||
else // victim is a player
|
||||
{
|
||||
@@ -8921,6 +8928,15 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
|
||||
// Lightning Shield (overwrite non existing triggered spell call in spell.dbc
|
||||
if (auraSpellInfo->SpellFamilyFlags[0] & 0x400)
|
||||
{
|
||||
// Do not proc off from self-casted items
|
||||
if (Spell const* spell = eventInfo.GetProcSpell())
|
||||
{
|
||||
if (spell->m_castItemGUID && victim->GetGUID() == GetGUID())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
trigger_spell_id = sSpellMgr->GetSpellWithRank(26364, auraSpellInfo->GetRank());
|
||||
}
|
||||
// Nature's Guardian
|
||||
|
||||
@@ -22,10 +22,12 @@
|
||||
#include "CharacterCache.h"
|
||||
#include "Chat.h"
|
||||
#include "Common.h"
|
||||
#include "CreatureAIFactory.h"
|
||||
#include "Config.h"
|
||||
#include "Containers.h"
|
||||
#include "DatabaseEnv.h"
|
||||
#include "DisableMgr.h"
|
||||
#include "GameObjectAIFactory.h"
|
||||
#include "GameEventMgr.h"
|
||||
#include "GameTime.h"
|
||||
#include "GossipDef.h"
|
||||
@@ -979,6 +981,12 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo)
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (!cInfo->AIName.empty() && !sCreatureAIRegistry->HasItem(cInfo->AIName))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-registered `AIName` '{}' set, removing", cInfo->Entry, cInfo->AIName);
|
||||
const_cast<CreatureTemplate*>(cInfo)->AIName.clear();
|
||||
}
|
||||
|
||||
FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(cInfo->faction);
|
||||
if (!factionTemplate)
|
||||
LOG_ERROR("sql.sql", "Creature (Entry: {}) has non-existing faction template ({}).", cInfo->Entry, cInfo->faction);
|
||||
@@ -2162,6 +2170,11 @@ void ObjectMgr::LoadCreatures()
|
||||
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entries: {}, {}, {}) with a `creature_template`.`flags_extra` in one or more entries including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.",
|
||||
spawnId, data.id1, data.id2, data.id3);
|
||||
}
|
||||
if (data.movementType >= MAX_DB_MOTION_TYPE)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` has creature (SpawnId: {} Entries: {}, {}, {}) with wrong movement generator type ({}), ignored and set to IDLE.", spawnId, data.id1, data.id2, data.id3, data.movementType);
|
||||
data.movementType = IDLE_MOTION_TYPE;
|
||||
}
|
||||
if (data.wander_distance < 0.0f)
|
||||
{
|
||||
LOG_ERROR("sql.sql", "Table `creature` have creature (SpawnId: {} Entries: {}, {}, {}) with `wander_distance`< 0, set to 0.", spawnId, data.id1, data.id2, data.id3);
|
||||
@@ -2665,7 +2678,7 @@ void ObjectMgr::LoadItemTemplates()
|
||||
for (uint8 i = 0; i < itemTemplate.StatsCount; ++i)
|
||||
{
|
||||
itemTemplate.ItemStat[i].ItemStatType = uint32(fields[28 + i * 2].Get<uint8>());
|
||||
itemTemplate.ItemStat[i].ItemStatValue = int32(fields[29 + i * 2].Get<int16>());
|
||||
itemTemplate.ItemStat[i].ItemStatValue = fields[29 + i * 2].Get<int32>();
|
||||
}
|
||||
|
||||
itemTemplate.ScalingStatDistribution = uint32(fields[48].Get<uint16>());
|
||||
@@ -2678,13 +2691,13 @@ void ObjectMgr::LoadItemTemplates()
|
||||
itemTemplate.Damage[i].DamageType = uint32(fields[52 + i * 3].Get<uint8>());
|
||||
}
|
||||
|
||||
itemTemplate.Armor = uint32(fields[56].Get<uint16>());
|
||||
itemTemplate.HolyRes = uint32(fields[57].Get<uint8>());
|
||||
itemTemplate.FireRes = uint32(fields[58].Get<uint8>());
|
||||
itemTemplate.NatureRes = uint32(fields[59].Get<uint8>());
|
||||
itemTemplate.FrostRes = uint32(fields[60].Get<uint8>());
|
||||
itemTemplate.ShadowRes = uint32(fields[61].Get<uint8>());
|
||||
itemTemplate.ArcaneRes = uint32(fields[62].Get<uint8>());
|
||||
itemTemplate.Armor = fields[56].Get<uint32>();
|
||||
itemTemplate.HolyRes = fields[57].Get<uint32>();
|
||||
itemTemplate.FireRes = fields[58].Get<uint32>();
|
||||
itemTemplate.NatureRes = fields[59].Get<uint32>();
|
||||
itemTemplate.FrostRes = fields[60].Get<uint32>();
|
||||
itemTemplate.ShadowRes = fields[61].Get<uint32>();
|
||||
itemTemplate.ArcaneRes = fields[62].Get<uint32>();
|
||||
itemTemplate.Delay = uint32(fields[63].Get<uint16>());
|
||||
itemTemplate.AmmoType = uint32(fields[64].Get<uint8>());
|
||||
itemTemplate.RangedModRange = fields[65].Get<float>();
|
||||
@@ -3435,14 +3448,14 @@ void ObjectMgr::LoadPetLevelInfo()
|
||||
// data for level 1 stored in [0] array element, ...
|
||||
PetLevelInfo* pLevelInfo = &pInfoMapEntry[current_level - 1];
|
||||
|
||||
pLevelInfo->health = fields[2].Get<uint16>();
|
||||
pLevelInfo->mana = fields[3].Get<uint16>();
|
||||
pLevelInfo->health = fields[2].Get<uint32>();
|
||||
pLevelInfo->mana = fields[3].Get<uint32>();
|
||||
pLevelInfo->armor = fields[9].Get<uint32>();
|
||||
pLevelInfo->min_dmg = fields[10].Get<uint16>();
|
||||
pLevelInfo->max_dmg = fields[11].Get<uint16>();
|
||||
for (int i = 0; i < MAX_STATS; i++)
|
||||
pLevelInfo->min_dmg = fields[10].Get<uint32>();
|
||||
pLevelInfo->max_dmg = fields[11].Get<uint32>();
|
||||
for (uint8 i = 0; i < MAX_STATS; i++)
|
||||
{
|
||||
pLevelInfo->stats[i] = fields[i + 4].Get<uint16>();
|
||||
pLevelInfo->stats[i] = fields[i + 4].Get<uint32>();
|
||||
}
|
||||
|
||||
++count;
|
||||
@@ -3960,8 +3973,8 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
|
||||
PlayerClassLevelInfo& levelInfo = info->levelInfo[current_level - 1];
|
||||
|
||||
levelInfo.basehealth = fields[2].Get<uint16>();
|
||||
levelInfo.basemana = fields[3].Get<uint16>();
|
||||
levelInfo.basehealth = fields[2].Get<uint32>();
|
||||
levelInfo.basemana = fields[3].Get<uint32>();
|
||||
|
||||
++count;
|
||||
} while (result->NextRow());
|
||||
@@ -4051,8 +4064,8 @@ void ObjectMgr::LoadPlayerInfo()
|
||||
info->levelInfo = new PlayerLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];
|
||||
|
||||
PlayerLevelInfo& levelInfo = info->levelInfo[current_level - 1];
|
||||
for (int i = 0; i < MAX_STATS; i++)
|
||||
levelInfo.stats[i] = fields[i + 3].Get<uint8>();
|
||||
for (uint8 i = 0; i < MAX_STATS; i++)
|
||||
levelInfo.stats[i] = fields[i + 3].Get<uint32>();
|
||||
}
|
||||
|
||||
++count;
|
||||
@@ -7040,6 +7053,10 @@ void ObjectMgr::LoadGameObjectTemplate()
|
||||
got.IsForQuests = false;
|
||||
|
||||
// Checks
|
||||
if (!got.AIName.empty() && !sGameObjectAIRegistry->HasItem(got.AIName))
|
||||
{
|
||||
LOG_ERROR("sql.sql", "GameObject (Entry: {}) has non-registered `AIName` '{}' set, removing", got.entry, got.AIName);
|
||||
}
|
||||
|
||||
switch (got.type)
|
||||
{
|
||||
@@ -9342,7 +9359,7 @@ void ObjectMgr::LoadCreatureClassLevelStats()
|
||||
|
||||
for (uint8 i = 0; i < MAX_EXPANSIONS; ++i)
|
||||
{
|
||||
stats.BaseHealth[i] = fields[2 + i].Get<uint16>();
|
||||
stats.BaseHealth[i] = fields[2 + i].Get<uint32>();
|
||||
|
||||
if (stats.BaseHealth[i] == 0)
|
||||
{
|
||||
@@ -9372,11 +9389,11 @@ void ObjectMgr::LoadCreatureClassLevelStats()
|
||||
}
|
||||
}
|
||||
|
||||
stats.BaseMana = fields[5].Get<uint16>();
|
||||
stats.BaseArmor = fields[6].Get<uint16>();
|
||||
stats.BaseMana = fields[5].Get<uint32>();
|
||||
stats.BaseArmor = fields[6].Get<uint32>();
|
||||
|
||||
stats.AttackPower = fields[7].Get<uint16>();
|
||||
stats.RangedAttackPower = fields[8].Get<uint16>();
|
||||
stats.AttackPower = fields[7].Get<uint32>();
|
||||
stats.RangedAttackPower = fields[8].Get<uint32>();
|
||||
|
||||
_creatureBaseStatsStore[MAKE_PAIR16(Level, Class)] = stats;
|
||||
|
||||
|
||||
@@ -520,14 +520,17 @@ typedef std::pair<QuestRelations::const_iterator, QuestRelations::const_iterator
|
||||
|
||||
struct PetLevelInfo
|
||||
{
|
||||
PetLevelInfo() { for (unsigned short & stat : stats) stat = 0; }
|
||||
PetLevelInfo()
|
||||
{
|
||||
stats.fill(0);
|
||||
}
|
||||
|
||||
uint16 stats[MAX_STATS];
|
||||
uint16 health{0};
|
||||
uint16 mana{0};
|
||||
std::array<uint32, MAX_STATS> stats = { };
|
||||
uint32 health{0};
|
||||
uint32 mana{0};
|
||||
uint32 armor{0};
|
||||
uint16 min_dmg{0};
|
||||
uint16 max_dmg{0};
|
||||
uint32 min_dmg{0};
|
||||
uint32 max_dmg{0};
|
||||
};
|
||||
|
||||
struct MailLevelReward
|
||||
|
||||
@@ -1448,7 +1448,8 @@ void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap)
|
||||
roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
|
||||
roll->getLoot()->unlootedCount--;
|
||||
AllowedLooterSet looters = item->GetAllowedLooters();
|
||||
player->StoreNewItem(dest, roll->itemid, true, item->randomPropertyId, looters);
|
||||
Item* _item = player->StoreNewItem(dest, roll->itemid, true, item->randomPropertyId, looters);
|
||||
sScriptMgr->OnGroupRollRewardItem(player, _item, _item->GetCount(), NEED, roll);
|
||||
player->UpdateLootAchievements(item, roll->getLoot());
|
||||
}
|
||||
else
|
||||
@@ -1516,7 +1517,8 @@ void Group::CountTheRoll(Rolls::iterator rollI, Map* allowedMap)
|
||||
roll->getLoot()->NotifyItemRemoved(roll->itemSlot);
|
||||
roll->getLoot()->unlootedCount--;
|
||||
AllowedLooterSet looters = item->GetAllowedLooters();
|
||||
player->StoreNewItem(dest, roll->itemid, true, item->randomPropertyId, looters);
|
||||
Item* _item = player->StoreNewItem(dest, roll->itemid, true, item->randomPropertyId, looters);
|
||||
sScriptMgr->OnGroupRollRewardItem(player, _item, _item->GetCount(), GREED, roll);
|
||||
player->UpdateLootAchievements(item, roll->getLoot());
|
||||
}
|
||||
else
|
||||
|
||||
@@ -2965,6 +2965,11 @@ bool InstanceMap::AddPlayerToMap(Player* player)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (player->GetSession()->PlayerLoading() && playerBind && playerBind->save != mapSave)
|
||||
{
|
||||
// Prevent "Convert to Raid" exploit to reset instances
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
playerBind = sInstanceSaveMgr->PlayerBindToInstance(player->GetGUID(), mapSave, false, player);
|
||||
|
||||
@@ -39,6 +39,7 @@ public:
|
||||
virtual void OnGameObjectRemove(GameObject*) { }
|
||||
|
||||
virtual void OnUnitDeath(Unit*) { }
|
||||
virtual void OnCreatureEvade(Creature*) { }
|
||||
|
||||
//All-purpose data storage 64 bit
|
||||
virtual ObjectGuid GetGuidData(uint32 /*DataId*/) const { return ObjectGuid::Empty; }
|
||||
|
||||
@@ -32,6 +32,11 @@
|
||||
#include "TargetedMovementGenerator.h"
|
||||
#include "WaypointMovementGenerator.h"
|
||||
|
||||
inline MovementGenerator* GetIdleMovementGenerator()
|
||||
{
|
||||
return sMovementGeneratorRegistry->GetRegistryItem(IDLE_MOTION_TYPE)->Create();
|
||||
}
|
||||
|
||||
// ---- ChaseRange ---- //
|
||||
|
||||
ChaseRange::ChaseRange(float range) : MinRange(range > CONTACT_DISTANCE ? 0 : range - CONTACT_DISTANCE), MinTolerance(range), MaxRange(range + CONTACT_DISTANCE), MaxTolerance(range) { }
|
||||
@@ -59,9 +64,9 @@ bool ChaseAngle::IsAngleOkay(float relativeAngle) const
|
||||
return (std::min(diff, float(2 * M_PI) - diff) <= Tolerance);
|
||||
}
|
||||
|
||||
inline bool isStatic(MovementGenerator* mv)
|
||||
inline bool isStatic(MovementGenerator* movement)
|
||||
{
|
||||
return (mv == &si_idleMovement);
|
||||
return (movement == GetIdleMovementGenerator());
|
||||
}
|
||||
|
||||
void MotionMaster::Initialize()
|
||||
@@ -80,16 +85,7 @@ void MotionMaster::Initialize()
|
||||
// set new default movement generator
|
||||
void MotionMaster::InitDefault()
|
||||
{
|
||||
// Xinef: Do not allow to initialize any motion generator for dead creatures
|
||||
if (_owner->GetTypeId() == TYPEID_UNIT && _owner->IsAlive())
|
||||
{
|
||||
MovementGenerator* movement = FactorySelector::selectMovementGenerator(_owner->ToCreature());
|
||||
Mutate(!movement ? &si_idleMovement : movement, MOTION_SLOT_IDLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
Mutate(&si_idleMovement, MOTION_SLOT_IDLE);
|
||||
}
|
||||
Mutate(FactorySelector::SelectMovementGenerator(_owner), MOTION_SLOT_IDLE);
|
||||
}
|
||||
|
||||
MotionMaster::~MotionMaster()
|
||||
@@ -236,7 +232,7 @@ void MotionMaster::MoveIdle()
|
||||
{
|
||||
//! Should be preceded by MovementExpired or Clear if there's an overlying movementgenerator active
|
||||
if (empty() || !isStatic(top()))
|
||||
Mutate(&si_idleMovement, MOTION_SLOT_IDLE);
|
||||
Mutate(GetIdleMovementGenerator(), MOTION_SLOT_IDLE);
|
||||
}
|
||||
|
||||
void MotionMaster::MoveRandom(float wanderDistance)
|
||||
|
||||
@@ -16,7 +16,14 @@
|
||||
*/
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
#include "IdleMovementGenerator.h"
|
||||
|
||||
MovementGenerator::~MovementGenerator()
|
||||
{
|
||||
}
|
||||
|
||||
MovementGenerator* IdleMovementFactory::Create(Unit* /*object*/) const
|
||||
{
|
||||
static IdleMovementGenerator instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
@@ -82,19 +82,28 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
struct SelectableMovement : public FactoryHolder<MovementGenerator, MovementGeneratorType>
|
||||
typedef FactoryHolder<MovementGenerator, Unit, MovementGeneratorType> MovementGeneratorCreator;
|
||||
|
||||
template<class Movement>
|
||||
struct MovementGeneratorFactory : public MovementGeneratorCreator
|
||||
{
|
||||
SelectableMovement(MovementGeneratorType mgt) : FactoryHolder<MovementGenerator, MovementGeneratorType>(mgt) {}
|
||||
MovementGeneratorFactory(MovementGeneratorType movementGeneratorType) : MovementGeneratorCreator(movementGeneratorType) { }
|
||||
|
||||
MovementGenerator* Create(Unit* /*object*/) const
|
||||
{
|
||||
return new Movement();
|
||||
}
|
||||
};
|
||||
|
||||
template<class REAL_MOVEMENT>
|
||||
struct MovementGeneratorFactory : public SelectableMovement
|
||||
struct IdleMovementFactory : public MovementGeneratorCreator
|
||||
{
|
||||
MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) {}
|
||||
IdleMovementFactory() : MovementGeneratorCreator(IDLE_MOTION_TYPE) { }
|
||||
|
||||
MovementGenerator* Create(void*) const;
|
||||
MovementGenerator* Create(Unit* object) const override;
|
||||
};
|
||||
|
||||
typedef FactoryHolder<MovementGenerator, MovementGeneratorType> MovementGeneratorCreator;
|
||||
typedef FactoryHolder<MovementGenerator, MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
|
||||
typedef MovementGeneratorCreator::FactoryHolderRegistry MovementGeneratorRegistry;
|
||||
|
||||
#define sMovementGeneratorRegistry MovementGeneratorRegistry::instance()
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*
|
||||
* 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_MOVEMENTGENERATOR_IMPL_H
|
||||
#define ACORE_MOVEMENTGENERATOR_IMPL_H
|
||||
|
||||
#include "MovementGenerator.h"
|
||||
|
||||
template<class MOVEMENT_GEN>
|
||||
inline MovementGenerator*
|
||||
MovementGeneratorFactory<MOVEMENT_GEN>::Create(void* /*data*/) const
|
||||
{
|
||||
return (new MOVEMENT_GEN());
|
||||
}
|
||||
#endif
|
||||
@@ -19,8 +19,6 @@
|
||||
#include "Creature.h"
|
||||
#include "CreatureAI.h"
|
||||
|
||||
IdleMovementGenerator si_idleMovement;
|
||||
|
||||
// StopMoving is needed to make unit stop if its last movement generator expires
|
||||
// But it should not be sent otherwise there are many redundent packets
|
||||
void IdleMovementGenerator::Initialize(Unit* owner)
|
||||
|
||||
@@ -30,8 +30,6 @@ public:
|
||||
MovementGeneratorType GetMovementGeneratorType() override { return IDLE_MOTION_TYPE; }
|
||||
};
|
||||
|
||||
extern IdleMovementGenerator si_idleMovement;
|
||||
|
||||
class RotateMovementGenerator : public MovementGenerator
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -41,7 +41,7 @@ void PointMovementGenerator<T>::DoInitialize(T* unit)
|
||||
unit->StopMoving();
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
|
||||
if (id == EVENT_CHARGE)
|
||||
if (id == EVENT_CHARGE || id == EVENT_CHARGE_PREPATH)
|
||||
{
|
||||
unit->AddUnitState(UNIT_STATE_CHARGING);
|
||||
}
|
||||
@@ -158,7 +158,7 @@ template<class T>
|
||||
void PointMovementGenerator<T>::DoFinalize(T* unit)
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
|
||||
if (id == EVENT_CHARGE)
|
||||
if (id == EVENT_CHARGE || id == EVENT_CHARGE_PREPATH)
|
||||
{
|
||||
unit->ClearUnitState(UNIT_STATE_CHARGING);
|
||||
|
||||
@@ -182,7 +182,7 @@ void PointMovementGenerator<T>::DoReset(T* unit)
|
||||
unit->StopMoving();
|
||||
|
||||
unit->AddUnitState(UNIT_STATE_ROAMING | UNIT_STATE_ROAMING_MOVE);
|
||||
if (id == EVENT_CHARGE)
|
||||
if (id == EVENT_CHARGE || id == EVENT_CHARGE_PREPATH)
|
||||
{
|
||||
unit->AddUnitState(UNIT_STATE_CHARGING);
|
||||
}
|
||||
|
||||
@@ -61,7 +61,10 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
{
|
||||
owner->StopMoving();
|
||||
_lastTargetPosition.reset();
|
||||
owner->SetCannotReachTarget(false);
|
||||
if (Creature* cOwner2 = owner->ToCreature())
|
||||
{
|
||||
cOwner2->SetCannotReachTarget();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -88,9 +91,17 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
|
||||
if (i_recalculateTravel && PositionOkay(owner, target, _movingTowards ? maxTarget : Optional<float>(), angle))
|
||||
{
|
||||
if (Creature* cOwner2 = owner->ToCreature())
|
||||
{
|
||||
cOwner2->SetCannotReachTarget(i_path && i_path->GetPathType() & PATHFIND_INCOMPLETE ? target->GetGUID() : ObjectGuid::Empty);
|
||||
}
|
||||
|
||||
i_recalculateTravel = false;
|
||||
i_path = nullptr;
|
||||
owner->SetCannotReachTarget(false);
|
||||
if (Creature* cOwner2 = owner->ToCreature())
|
||||
{
|
||||
cOwner2->SetCannotReachTarget(false);
|
||||
}
|
||||
|
||||
owner->StopMoving();
|
||||
owner->SetInFront(target);
|
||||
@@ -101,14 +112,24 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
|
||||
if (owner->HasUnitState(UNIT_STATE_CHASE_MOVE) && owner->movespline->Finalized())
|
||||
{
|
||||
i_recalculateTravel = false;
|
||||
i_path = nullptr;
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE_MOVE);
|
||||
owner->SetInFront(target);
|
||||
MovementInform(owner);
|
||||
|
||||
if (owner->IsWithinMeleeRange(this->i_target.getTarget()))
|
||||
{
|
||||
owner->Attack(this->i_target.getTarget(), true);
|
||||
}
|
||||
else if (i_path && i_path->GetPathType() & PATHFIND_INCOMPLETE)
|
||||
{
|
||||
if (Creature* cOwner2 = owner->ToCreature())
|
||||
{
|
||||
cOwner2->SetCannotReachTarget(this->i_target.getTarget()->GetGUID());
|
||||
}
|
||||
}
|
||||
|
||||
i_recalculateTravel = false;
|
||||
i_path = nullptr;
|
||||
}
|
||||
|
||||
if (_lastTargetPosition && i_target->GetPosition() == _lastTargetPosition.value() && mutualChase == _mutualChase)
|
||||
@@ -133,7 +154,7 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
// can we get to the target?
|
||||
if (cOwner && !target->isInAccessiblePlaceFor(cOwner))
|
||||
{
|
||||
cOwner->SetCannotReachTarget(true);
|
||||
cOwner->SetCannotReachTarget(target->GetGUID());
|
||||
cOwner->StopMoving();
|
||||
i_path = nullptr;
|
||||
return true;
|
||||
@@ -169,14 +190,21 @@ bool ChaseMovementGenerator<T>::DoUpdate(T* owner, uint32 time_diff)
|
||||
bool success = i_path->CalculatePath(x, y, z, forceDest);
|
||||
if (!success || i_path->GetPathType() & PATHFIND_NOPATH)
|
||||
{
|
||||
owner->SetCannotReachTarget(true);
|
||||
if (cOwner)
|
||||
{
|
||||
cOwner->SetCannotReachTarget(target->GetGUID());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (shortenPath)
|
||||
i_path->ShortenPathUntilDist(G3D::Vector3(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()), maxTarget);
|
||||
|
||||
owner->SetCannotReachTarget(false);
|
||||
if (cOwner)
|
||||
{
|
||||
cOwner->SetCannotReachTarget();
|
||||
}
|
||||
|
||||
bool walk = false;
|
||||
if (cOwner && !cOwner->IsPet())
|
||||
@@ -230,7 +258,10 @@ template<class T>
|
||||
void ChaseMovementGenerator<T>::DoFinalize(T* owner)
|
||||
{
|
||||
owner->ClearUnitState(UNIT_STATE_CHASE | UNIT_STATE_CHASE_MOVE);
|
||||
owner->SetCannotReachTarget(false);
|
||||
if (Creature* cOwner = owner->ToCreature())
|
||||
{
|
||||
cOwner->SetCannotReachTarget();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
|
||||
@@ -598,6 +598,14 @@ void ScriptMgr::OnQuestRewardItem(Player* player, Item* item, uint32 count)
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptMgr::OnGroupRollRewardItem(Player* player, Item* item, uint32 count, RollVote voteType, Roll* roll)
|
||||
{
|
||||
ExecuteScript<PlayerScript>([&](PlayerScript* script)
|
||||
{
|
||||
script->OnGroupRollRewardItem(player, item, count, voteType, roll);
|
||||
});
|
||||
}
|
||||
|
||||
void ScriptMgr::OnFirstLogin(Player* player)
|
||||
{
|
||||
ExecuteScript<PlayerScript>([&](PlayerScript* script)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "DBCStores.h"
|
||||
#include "DynamicObject.h"
|
||||
#include "GameEventMgr.h"
|
||||
#include "Group.h"
|
||||
#include "LFGMgr.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PetDefines.h"
|
||||
@@ -1174,6 +1175,9 @@ public:
|
||||
// After receiving item as a quest reward
|
||||
virtual void OnQuestRewardItem(Player* /*player*/, Item* /*item*/, uint32 /*count*/) { }
|
||||
|
||||
// After receiving item as a group roll reward
|
||||
virtual void OnGroupRollRewardItem(Player* /*player*/, Item* /*item*/, uint32 /*count*/, RollVote /*voteType*/, Roll* /*roll*/) { }
|
||||
|
||||
// After completed a quest
|
||||
[[nodiscard]] virtual bool OnBeforeQuestComplete(Player* /*player*/, uint32 /*quest_id*/) { return true; }
|
||||
|
||||
@@ -2285,6 +2289,7 @@ public: /* PlayerScript */
|
||||
void OnLootItem(Player* player, Item* item, uint32 count, ObjectGuid lootguid);
|
||||
void OnCreateItem(Player* player, Item* item, uint32 count);
|
||||
void OnQuestRewardItem(Player* player, Item* item, uint32 count);
|
||||
void OnGroupRollRewardItem(Player* player, Item* item, uint32 count, RollVote voteType, Roll* roll);
|
||||
bool OnBeforePlayerQuestComplete(Player* player, uint32 quest_id);
|
||||
void OnQuestComputeXP(Player* player, Quest const* quest, uint32& xpValue);
|
||||
void OnBeforePlayerDurabilityRepair(Player* player, ObjectGuid npcGUID, ObjectGuid itemGUID, float& discountMod, uint8 guildBank);
|
||||
|
||||
@@ -682,11 +682,12 @@ void WorldSession::LogoutPlayer(bool save)
|
||||
_player->GetGroup()->SendUpdate();
|
||||
_player->GetGroup()->ResetMaxEnchantingLevel();
|
||||
|
||||
Map::PlayerList const& playerList = _player->GetMap()->GetPlayers();
|
||||
|
||||
if (_player->GetMap()->IsDungeon() || _player->GetMap()->IsRaidOrHeroicDungeon())
|
||||
{
|
||||
Map::PlayerList const &playerList = _player->GetMap()->GetPlayers();
|
||||
if (playerList.IsEmpty())
|
||||
_player->TeleportToEntryPoint();
|
||||
}
|
||||
}
|
||||
|
||||
//! Broadcast a logout message to the player's friends
|
||||
|
||||
@@ -4323,6 +4323,12 @@ void SpellMgr::LoadSpellInfoCorrections()
|
||||
spellInfo->Effects[EFFECT_0].MiscValue = 8;
|
||||
});
|
||||
|
||||
// Elemental Mastery
|
||||
ApplySpellFix({ 16166 }, [](SpellInfo* spellInfo)
|
||||
{
|
||||
spellInfo->Effects[EFFECT_0].SpellClassMask = flag96(0x00000003, 0x00001000);
|
||||
});
|
||||
|
||||
for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i)
|
||||
{
|
||||
SpellInfo* spellInfo = mSpellInfoMap[i];
|
||||
|
||||
@@ -1568,6 +1568,9 @@ void World::SetInitialWorldSettings()
|
||||
LOG_INFO("server.loading", "Initializing PlayerDump tables...");
|
||||
PlayerDump::InitializeTables();
|
||||
|
||||
///- Initilize static helper structures
|
||||
AIRegistry::Initialize();
|
||||
|
||||
LOG_INFO("server.loading", "Loading SpellInfo store...");
|
||||
sSpellMgr->LoadSpellInfoStore();
|
||||
|
||||
@@ -2036,9 +2039,6 @@ void World::SetInitialWorldSettings()
|
||||
|
||||
mail_expire_check_timer = GameTime::GetGameTime() + 6h;
|
||||
|
||||
///- Initilize static helper structures
|
||||
AIRegistry::Initialize();
|
||||
|
||||
///- Initialize MapMgr
|
||||
LOG_INFO("server.loading", "Starting Map System");
|
||||
LOG_INFO("server.loading", " ");
|
||||
|
||||
@@ -186,6 +186,7 @@ public:
|
||||
static ChatCommandTable npcCommandTable =
|
||||
{
|
||||
{ "info", HandleNpcInfoCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "guid", HandleNpcGuidCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "near", HandleNpcNearCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "move", HandleNpcMoveCommand, SEC_GAMEMASTER, Console::No },
|
||||
{ "playemote", HandleNpcPlayEmoteCommand, SEC_GAMEMASTER, Console::No },
|
||||
@@ -652,6 +653,36 @@ public:
|
||||
|
||||
return true;
|
||||
}
|
||||
static bool HandleNpcGuidCommand(ChatHandler* handler)
|
||||
{
|
||||
Creature* target = handler->getSelectedCreature();
|
||||
|
||||
if (!target)
|
||||
{
|
||||
handler->SendSysMessage(LANG_SELECT_CREATURE);
|
||||
handler->SetSentErrorMessage(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 faction = target->GetFaction();
|
||||
uint32 npcflags = target->GetNpcFlags();
|
||||
uint32 displayid = target->GetDisplayId();
|
||||
uint32 nativeid = target->GetNativeDisplayId();
|
||||
uint32 entry = target->GetEntry();
|
||||
uint32 id1 = 0;
|
||||
uint32 id2 = 0;
|
||||
uint32 id3 = 0;
|
||||
if (CreatureData const* cData = target->GetCreatureData())
|
||||
{
|
||||
id1 = cData->id1;
|
||||
id2 = cData->id2;
|
||||
id3 = cData->id3;
|
||||
}
|
||||
|
||||
handler->PSendSysMessage(LANG_NPCINFO_CHAR, target->GetSpawnId(), target->GetGUID().GetCounter(), entry, id1, id2, id3, displayid, nativeid, faction, npcflags);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool HandleNpcNearCommand(ChatHandler* handler, Optional<float> dist)
|
||||
{
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_SUMMON_PROWLERS, 6000, 0, PHASE_ALL);
|
||||
events.ScheduleEvent(EVENT_MARK_OF_ARLOKK, urand(9000, 11000), 0, PHASE_ALL);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, urand(15000, 20000), 0, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, 30000, 0, PHASE_ONE);
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
// Sets up list of Panther spawners to cast on
|
||||
@@ -249,7 +249,7 @@ public:
|
||||
case EVENT_VANISH_2:
|
||||
DoCastSelf(SPELL_VANISH);
|
||||
DoCastSelf(SPELL_SUPER_INVIS);
|
||||
events.ScheduleEvent(EVENT_VISIBLE, urand(7000, 10000), 0, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_VISIBLE, urand(41000, 47000), 0, PHASE_ONE);
|
||||
break;
|
||||
case EVENT_VISIBLE:
|
||||
me->SetReactState(REACT_AGGRESSIVE);
|
||||
@@ -259,7 +259,7 @@ public:
|
||||
me->RemoveAura(SPELL_SUPER_INVIS);
|
||||
me->RemoveAura(SPELL_VANISH);
|
||||
events.ScheduleEvent(EVENT_RAVAGE, urand(10000, 14000), 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM_BACK, urand(15000, 18000), 0, PHASE_TWO);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM_BACK, urand(30000, 40000), 0, PHASE_TWO);
|
||||
events.SetPhase(PHASE_TWO);
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, true); // hack
|
||||
break;
|
||||
@@ -276,7 +276,7 @@ public:
|
||||
me->HandleStatModifier(UNIT_MOD_DAMAGE_MAINHAND, TOTAL_PCT, 35.0f, false); // hack
|
||||
events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(4000, 7000), 0, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_GOUGE, urand(12000, 15000), 0, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, urand(16000, 20000), 0, PHASE_ONE);
|
||||
events.ScheduleEvent(EVENT_TRANSFORM, 30000, 0, PHASE_ONE);
|
||||
events.SetPhase(PHASE_ONE);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,9 @@ struct boss_jindo : public BossAI
|
||||
events.ScheduleEvent(EVENT_TELEPORT, 5000);
|
||||
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
|
||||
_scheduler.CancelAll();
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* summon) override
|
||||
@@ -76,14 +79,15 @@ struct boss_jindo : public BossAI
|
||||
|
||||
switch (summon->GetEntry())
|
||||
{
|
||||
case NPC_BRAIN_WASH_TOTEM:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1))
|
||||
{
|
||||
summon->CastSpell(target, summon->m_spells[0], true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
case NPC_BRAIN_WASH_TOTEM:
|
||||
summon->SetReactState(REACT_PASSIVE);
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, me->GetThreatMgr().getThreatList().size() > 1 ? 1 : 0))
|
||||
{
|
||||
summon->CastSpell(target, summon->m_spells[0], true);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,20 +95,22 @@ struct boss_jindo : public BossAI
|
||||
{
|
||||
if (_EnterEvadeMode(evadeReason))
|
||||
{
|
||||
me->AddUnitState(UNIT_STATE_EVADE);
|
||||
Reset();
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_DANCE);
|
||||
|
||||
me->m_Events.AddEventAtOffset([&]()
|
||||
_scheduler.Schedule(4s, [this](TaskContext /*context*/)
|
||||
{
|
||||
me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
|
||||
me->AddUnitState(UNIT_STATE_EVADE);
|
||||
me->GetMotionMaster()->MoveTargetedHome();
|
||||
}, 4s);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
_scheduler.Update(diff);
|
||||
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
@@ -153,6 +159,9 @@ struct boss_jindo : public BossAI
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
//Healing Ward
|
||||
@@ -282,11 +291,23 @@ class spell_delusions_of_jindo : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
struct npc_brain_wash_totem : public ScriptedAI
|
||||
{
|
||||
npc_brain_wash_totem(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason /*evadeReason*/) override
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_jindo()
|
||||
{
|
||||
RegisterZulGurubCreatureAI(boss_jindo);
|
||||
RegisterZulGurubCreatureAI(npc_healing_ward);
|
||||
RegisterZulGurubCreatureAI(npc_shade_of_jindo);
|
||||
RegisterZulGurubCreatureAI(npc_brain_wash_totem);
|
||||
RegisterSpellScript(spell_random_aggro);
|
||||
RegisterSpellScript(spell_delusions_of_jindo);
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ public:
|
||||
if (_chargeTarget.first == hatedUnit->GetGUID())
|
||||
{
|
||||
// Do not count DOTs/HOTs
|
||||
if (!threatSpell || !threatSpell->HasAttribute(SPELL_ATTR0_CU_NO_INITIAL_THREAT))
|
||||
if (!(threatSpell && (threatSpell->HasAura(SPELL_AURA_DAMAGE_SHIELD) || threatSpell->HasAttribute(SPELL_ATTR0_CU_NO_INITIAL_THREAT))))
|
||||
{
|
||||
_chargeTarget.second += threat;
|
||||
}
|
||||
@@ -313,6 +313,12 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool OnTeleportUnreacheablePlayer(Player* player) override
|
||||
{
|
||||
DoCast(player, SPELL_SUMMON_PLAYER, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DoMeleeAttackIfReady(bool ignoreCasting)
|
||||
{
|
||||
if (!ignoreCasting && me->HasUnitState(UNIT_STATE_CASTING))
|
||||
|
||||
@@ -91,8 +91,6 @@ public:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
InstanceScript::OnCreatureCreate(creature);
|
||||
}
|
||||
|
||||
void OnGameObjectCreate(GameObject* go) override
|
||||
|
||||
@@ -25,7 +25,6 @@ enum Spells
|
||||
SPELL_STINGER_SPRAY = 25749,
|
||||
SPELL_POISON_STINGER = 25748,
|
||||
SPELL_PARALYZE = 25725,
|
||||
SPELL_TRASH = 3391,
|
||||
SPELL_FRENZY = 8269,
|
||||
SPELL_LASH = 25852,
|
||||
SPELL_FEED = 25721
|
||||
@@ -38,8 +37,7 @@ enum Events
|
||||
EVENT_SUMMON_SWARMER = 3,
|
||||
EVENT_SWARMER_ATTACK = 4,
|
||||
EVENT_PARALYZE = 5,
|
||||
EVENT_LASH = 6,
|
||||
EVENT_TRASH = 7
|
||||
EVENT_LASH = 6
|
||||
};
|
||||
|
||||
enum Emotes
|
||||
@@ -77,9 +75,7 @@ public:
|
||||
|
||||
struct boss_ayamissAI : public BossAI
|
||||
{
|
||||
boss_ayamissAI(Creature* creature) : BossAI(creature, DATA_AYAMISS)
|
||||
{
|
||||
}
|
||||
boss_ayamissAI(Creature* creature) : BossAI(creature, DATA_AYAMISS) {}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
@@ -101,7 +97,9 @@ public:
|
||||
break;
|
||||
case NPC_HORNET:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
|
||||
{
|
||||
who->AI()->AttackStart(target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -116,7 +114,7 @@ public:
|
||||
me->AddUnitState(UNIT_STATE_ROOT);
|
||||
break;
|
||||
case POINT_GROUND:
|
||||
me->ClearUnitState(UNIT_STATE_ROOT);
|
||||
me->GetMotionMaster()->MoveChase(me->GetVictim());
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -131,55 +129,51 @@ public:
|
||||
void EnterCombat(Unit* attacker) override
|
||||
{
|
||||
BossAI::EnterCombat(attacker);
|
||||
|
||||
events.ScheduleEvent(EVENT_STINGER_SPRAY, urand(20000, 30000));
|
||||
events.ScheduleEvent(EVENT_POISON_STINGER, 5000);
|
||||
events.ScheduleEvent(EVENT_SUMMON_SWARMER, 5000);
|
||||
events.ScheduleEvent(EVENT_SWARMER_ATTACK, 60000);
|
||||
events.ScheduleEvent(EVENT_PARALYZE, 15000);
|
||||
|
||||
me->SetCanFly(true);
|
||||
me->SetDisableGravity(true);
|
||||
me->GetMotionMaster()->MovePoint(POINT_AIR, AyamissAirPos);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (_phase == PHASE_AIR && me->GetHealthPct() < 70.0f)
|
||||
{
|
||||
_phase = PHASE_GROUND;
|
||||
SetCombatMovement(true);
|
||||
me->ClearUnitState(UNIT_STATE_ROOT);
|
||||
me->SetCanFly(false);
|
||||
me->SetDisableGravity(false);
|
||||
Position VictimPos = me->GetVictim()->GetPosition();
|
||||
me->GetMotionMaster()->MovePoint(POINT_GROUND, VictimPos);
|
||||
events.ScheduleEvent(EVENT_LASH, urand(5000, 8000));
|
||||
events.CancelEvent(EVENT_POISON_STINGER);
|
||||
DoResetThreat();
|
||||
}
|
||||
if (!_enraged && me->GetHealthPct() < 20.0f)
|
||||
{
|
||||
DoCastSelf(SPELL_FRENZY);
|
||||
Talk(EMOTE_FRENZY);
|
||||
_enraged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
if (_phase == PHASE_AIR && me->GetHealthPct() < 70.0f)
|
||||
{
|
||||
_phase = PHASE_GROUND;
|
||||
SetCombatMovement(true);
|
||||
me->SetCanFly(false);
|
||||
Position VictimPos = me->GetVictim()->GetPosition();
|
||||
me->GetMotionMaster()->MovePoint(POINT_GROUND, VictimPos);
|
||||
DoResetThreat();
|
||||
events.ScheduleEvent(EVENT_LASH, urand(5000, 8000));
|
||||
events.ScheduleEvent(EVENT_TRASH, urand(3000, 6000));
|
||||
events.CancelEvent(EVENT_POISON_STINGER);
|
||||
}
|
||||
else
|
||||
{
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
if (!_enraged && me->GetHealthPct() < 20.0f)
|
||||
{
|
||||
DoCast(me, SPELL_FRENZY);
|
||||
Talk(EMOTE_FRENZY);
|
||||
_enraged = true;
|
||||
}
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_STINGER_SPRAY:
|
||||
DoCast(me, SPELL_STINGER_SPRAY);
|
||||
DoCastSelf(SPELL_STINGER_SPRAY);
|
||||
events.ScheduleEvent(EVENT_STINGER_SPRAY, urand(15000, 20000));
|
||||
break;
|
||||
case EVENT_POISON_STINGER:
|
||||
@@ -198,30 +192,32 @@ public:
|
||||
break;
|
||||
case EVENT_SWARMER_ATTACK:
|
||||
for (ObjectGuid const& guid : _swarmers)
|
||||
{
|
||||
if (Creature* swarmer = me->GetMap()->GetCreature(guid))
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
|
||||
{
|
||||
swarmer->AI()->AttackStart(target);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
_swarmers.clear();
|
||||
events.ScheduleEvent(EVENT_SWARMER_ATTACK, 60000);
|
||||
break;
|
||||
case EVENT_SUMMON_SWARMER:
|
||||
{
|
||||
Position Pos = me->GetRandomPoint(SwarmerPos, 80.0f);
|
||||
me->SummonCreature(NPC_SWARMER, Pos);
|
||||
me->SummonCreature(NPC_SWARMER, Pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 5000);
|
||||
events.ScheduleEvent(EVENT_SUMMON_SWARMER, 5000);
|
||||
break;
|
||||
}
|
||||
case EVENT_TRASH:
|
||||
DoCastVictim(SPELL_TRASH);
|
||||
events.ScheduleEvent(EVENT_TRASH, urand(5000, 7000));
|
||||
break;
|
||||
case EVENT_LASH:
|
||||
DoCastVictim(SPELL_LASH);
|
||||
events.ScheduleEvent(EVENT_LASH, urand(8000, 15000));
|
||||
break;
|
||||
}
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
private:
|
||||
GuidList _swarmers;
|
||||
@@ -250,9 +246,15 @@ public:
|
||||
void MovementInform(uint32 type, uint32 id) override
|
||||
{
|
||||
if (type == POINT_MOTION_TYPE)
|
||||
{
|
||||
if (id == POINT_PARALYZE)
|
||||
{
|
||||
if (Player* target = ObjectAccessor::GetPlayer(*me, _instance->GetGuidData(DATA_PARALYZED)))
|
||||
DoCast(target, SPELL_FEED); // Omnomnom
|
||||
{
|
||||
DoCast(target, SPELL_FEED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "ruins_of_ahnqiraj.h"
|
||||
|
||||
enum Emotes
|
||||
@@ -36,7 +37,8 @@ enum Spells
|
||||
SPELL_SUMMON_HATCHLING = 1881,
|
||||
SPELL_EXPLODE = 19593,
|
||||
SPELL_EXPLODE_2 = 5255,
|
||||
SPELL_BURU_EGG_TRIGGER = 26646
|
||||
SPELL_BURU_EGG_TRIGGER = 26646,
|
||||
SPELL_CREATURE_SPECIAL = 7155, // from sniffs
|
||||
};
|
||||
|
||||
enum Events
|
||||
@@ -45,7 +47,7 @@ enum Events
|
||||
EVENT_GATHERING_SPEED = 2,
|
||||
EVENT_FULL_SPEED = 3,
|
||||
EVENT_CREEPING_PLAGUE = 4,
|
||||
EVENT_RESPAWN_EGG = 5
|
||||
EVENT_RESPAWN_EGG = 5,
|
||||
};
|
||||
|
||||
enum Phases
|
||||
@@ -54,228 +56,229 @@ enum Phases
|
||||
PHASE_TRANSFORM = 1
|
||||
};
|
||||
|
||||
enum Actions
|
||||
struct boss_buru : public BossAI
|
||||
{
|
||||
ACTION_EXPLODE = 0
|
||||
};
|
||||
boss_buru(Creature* creature) : BossAI(creature, DATA_BURU) {}
|
||||
|
||||
class boss_buru : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_buru() : CreatureScript("boss_buru") { }
|
||||
|
||||
struct boss_buruAI : public BossAI
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
boss_buruAI(Creature* creature) : BossAI(creature, DATA_BURU)
|
||||
BossAI::EnterEvadeMode(why);
|
||||
|
||||
DoCastSelf(SPELL_FULL_SPEED, true);
|
||||
|
||||
ManipulateEggs(true);
|
||||
_eggs.clear();
|
||||
}
|
||||
|
||||
void ManipulateEggs(bool respawn)
|
||||
{
|
||||
std::list<Creature*> eggs;
|
||||
me->GetCreaturesWithEntryInRange(eggs, 150.0f, NPC_BURU_EGG);
|
||||
for (Creature* egg : eggs)
|
||||
respawn ? egg->Respawn() : Unit::Kill(me, egg);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
_EnterCombat();
|
||||
Talk(EMOTE_TARGET, who);
|
||||
DoCastSelf(SPELL_THORNS);
|
||||
ManipulateEggs(true);
|
||||
me->RemoveAurasDueToSpell(SPELL_FULL_SPEED);
|
||||
events.ScheduleEvent(EVENT_DISMEMBER, 5s);
|
||||
events.ScheduleEvent(EVENT_GATHERING_SPEED, 9s);
|
||||
events.ScheduleEvent(EVENT_FULL_SPEED, 60s);
|
||||
_phase = PHASE_EGG;
|
||||
}
|
||||
|
||||
void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override
|
||||
{
|
||||
if (spell->Id == SPELL_CREATURE_SPECIAL)
|
||||
ChaseNewVictim();
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (InstanceScript* pInstance = me->GetInstanceScript())
|
||||
{
|
||||
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_CREEPING_PLAGUE);
|
||||
}
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (victim->GetTypeId() == TYPEID_PLAYER)
|
||||
ChaseNewVictim();
|
||||
}
|
||||
|
||||
void ChaseNewVictim()
|
||||
{
|
||||
if (_phase != PHASE_EGG)
|
||||
return;
|
||||
|
||||
me->RemoveAurasDueToSpell(SPELL_FULL_SPEED);
|
||||
me->RemoveAurasDueToSpell(SPELL_GATHERING_SPEED);
|
||||
events.ScheduleEvent(EVENT_GATHERING_SPEED, 9s);
|
||||
events.ScheduleEvent(EVENT_FULL_SPEED, 60s);
|
||||
if (Unit* victim = SelectTarget(SelectTargetMethod::Random, 0, 0.f, true))
|
||||
{
|
||||
DoResetThreat();
|
||||
AttackStart(victim);
|
||||
me->AddThreat(victim, 1000000.f);
|
||||
Talk(EMOTE_TARGET, victim);
|
||||
}
|
||||
}
|
||||
|
||||
void SetGUID(ObjectGuid const guid, int32 /*type*/) override
|
||||
{
|
||||
_eggs.push_back(guid);
|
||||
events.ScheduleEvent(EVENT_RESPAWN_EGG, 120s);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (attacker->GetEntry() != NPC_BURU_EGG && _phase == PHASE_EGG)
|
||||
{
|
||||
damage = damage * 0.01f; // 99% dmg resist
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
if (me->HealthBelowPctDamaged(20, damage) && _phase == PHASE_EGG)
|
||||
{
|
||||
BossAI::EnterEvadeMode(why);
|
||||
|
||||
for (ObjectGuid const& guid : Eggs)
|
||||
if (Creature* egg = me->GetMap()->GetCreature(guid))
|
||||
egg->Respawn();
|
||||
|
||||
Eggs.clear();
|
||||
DoCastSelf(SPELL_FULL_SPEED, true);
|
||||
ManipulateEggs(false);
|
||||
me->RemoveAurasDueToSpell(SPELL_THORNS);
|
||||
events.Reset();
|
||||
_phase = PHASE_TRANSFORM;
|
||||
DoResetThreat();
|
||||
events.ScheduleEvent(EVENT_CREEPING_PLAGUE, 2s);
|
||||
DoCastSelf(SPELL_BURU_TRANSFORM);
|
||||
}
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
_EnterCombat();
|
||||
Talk(EMOTE_TARGET, who);
|
||||
DoCast(me, SPELL_THORNS);
|
||||
|
||||
events.ScheduleEvent(EVENT_DISMEMBER, 5000);
|
||||
events.ScheduleEvent(EVENT_GATHERING_SPEED, 9000);
|
||||
events.ScheduleEvent(EVENT_FULL_SPEED, 60000);
|
||||
|
||||
_phase = PHASE_EGG;
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
{
|
||||
if (action == ACTION_EXPLODE)
|
||||
if (_phase == PHASE_EGG)
|
||||
Unit::DealDamage(me, me, 45000);
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* victim) override
|
||||
{
|
||||
if (victim->GetTypeId() == TYPEID_PLAYER)
|
||||
ChaseNewVictim();
|
||||
}
|
||||
|
||||
void ChaseNewVictim()
|
||||
{
|
||||
if (_phase != PHASE_EGG)
|
||||
return;
|
||||
|
||||
me->RemoveAurasDueToSpell(SPELL_FULL_SPEED);
|
||||
me->RemoveAurasDueToSpell(SPELL_GATHERING_SPEED);
|
||||
events.ScheduleEvent(EVENT_GATHERING_SPEED, 9000);
|
||||
events.ScheduleEvent(EVENT_FULL_SPEED, 60000);
|
||||
|
||||
if (Unit* victim = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
|
||||
switch (eventId)
|
||||
{
|
||||
DoResetThreat();
|
||||
AttackStart(victim);
|
||||
Talk(EMOTE_TARGET, victim);
|
||||
case EVENT_DISMEMBER:
|
||||
DoCastVictim(SPELL_DISMEMBER);
|
||||
events.ScheduleEvent(EVENT_DISMEMBER, 5s);
|
||||
break;
|
||||
case EVENT_GATHERING_SPEED:
|
||||
DoCastSelf(SPELL_GATHERING_SPEED);
|
||||
events.ScheduleEvent(EVENT_GATHERING_SPEED, 9s);
|
||||
break;
|
||||
case EVENT_FULL_SPEED:
|
||||
DoCastSelf(SPELL_FULL_SPEED);
|
||||
break;
|
||||
case EVENT_CREEPING_PLAGUE:
|
||||
DoCastAOE(SPELL_CREEPING_PLAGUE);
|
||||
events.ScheduleEvent(EVENT_CREEPING_PLAGUE, 6s);
|
||||
break;
|
||||
case EVENT_RESPAWN_EGG:
|
||||
if (Creature* egg = me->GetMap()->GetCreature(*_eggs.begin()))
|
||||
egg->Respawn();
|
||||
|
||||
_eggs.pop_front();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ManageRespawn(ObjectGuid EggGUID)
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
private:
|
||||
uint8 _phase;
|
||||
GuidList _eggs;
|
||||
};
|
||||
|
||||
struct npc_buru_egg : public ScriptedAI
|
||||
{
|
||||
npc_buru_egg(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
_instance = me->GetInstanceScript();
|
||||
SetCombatMovement(false);
|
||||
me->SetReactState(REACT_PASSIVE);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* attacker) override
|
||||
{
|
||||
if (Creature* buru = _instance->GetCreature(DATA_BURU))
|
||||
{
|
||||
ChaseNewVictim();
|
||||
Eggs.push_back(EggGUID);
|
||||
events.ScheduleEvent(EVENT_RESPAWN_EGG, 100000);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
if (!buru->IsInCombat())
|
||||
{
|
||||
switch (eventId)
|
||||
buru->AI()->AttackStart(attacker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* who) override
|
||||
{
|
||||
if (who->GetEntry() == NPC_HATCHLING)
|
||||
{
|
||||
if (Creature* buru = _instance->GetCreature(DATA_BURU))
|
||||
{
|
||||
if (Unit* target = buru->AI()->SelectTarget(SelectTargetMethod::Random))
|
||||
{
|
||||
case EVENT_DISMEMBER:
|
||||
DoCastVictim(SPELL_DISMEMBER);
|
||||
events.ScheduleEvent(EVENT_DISMEMBER, 5000);
|
||||
break;
|
||||
case EVENT_GATHERING_SPEED:
|
||||
DoCast(me, SPELL_GATHERING_SPEED);
|
||||
events.ScheduleEvent(EVENT_GATHERING_SPEED, 9000);
|
||||
break;
|
||||
case EVENT_FULL_SPEED:
|
||||
DoCast(me, SPELL_FULL_SPEED);
|
||||
break;
|
||||
case EVENT_CREEPING_PLAGUE:
|
||||
DoCast(me, SPELL_CREEPING_PLAGUE);
|
||||
events.ScheduleEvent(EVENT_CREEPING_PLAGUE, 6000);
|
||||
break;
|
||||
case EVENT_RESPAWN_EGG:
|
||||
if (Creature* egg = me->GetMap()->GetCreature(*Eggs.begin()))
|
||||
{
|
||||
egg->Respawn();
|
||||
Eggs.pop_front();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
who->AI()->AttackStart(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (me->GetHealthPct() < 20.0f && _phase == PHASE_EGG)
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
DoCastSelf(SPELL_SUMMON_HATCHLING, true);
|
||||
if (killer->GetEntry() != NPC_BURU)
|
||||
{
|
||||
if (Creature* buru = _instance->GetCreature(DATA_BURU))
|
||||
{
|
||||
DoCast(me, SPELL_BURU_TRANSFORM); // Enrage
|
||||
DoCast(me, SPELL_FULL_SPEED, true);
|
||||
me->RemoveAurasDueToSpell(SPELL_THORNS);
|
||||
_phase = PHASE_TRANSFORM;
|
||||
DoCastSelf(SPELL_EXPLODE);
|
||||
DoCastSelf(SPELL_BURU_EGG_TRIGGER, true);
|
||||
buru->CastSpell(buru, SPELL_CREATURE_SPECIAL, true);
|
||||
if (buru->GetAI())
|
||||
buru->AI()->SetGUID(me->GetGUID());
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
private:
|
||||
uint8 _phase;
|
||||
GuidList Eggs;
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetRuinsOfAhnQirajAI<boss_buruAI>(creature);
|
||||
}
|
||||
private:
|
||||
InstanceScript* _instance;
|
||||
};
|
||||
|
||||
class npc_buru_egg : public CreatureScript
|
||||
class spell_egg_explosion : public SpellScript
|
||||
{
|
||||
public:
|
||||
npc_buru_egg() : CreatureScript("npc_buru_egg") { }
|
||||
PrepareSpellScript(spell_egg_explosion);
|
||||
|
||||
struct npc_buru_eggAI : public ScriptedAI
|
||||
void HandleDummyHitTarget(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
npc_buru_eggAI(Creature* creature) : ScriptedAI(creature)
|
||||
if (Unit* target = GetHitUnit())
|
||||
{
|
||||
_instance = me->GetInstanceScript();
|
||||
SetCombatMovement(false);
|
||||
int32 damage = 0;
|
||||
if (target->IsPlayer())
|
||||
damage = -16 * GetCaster()->GetDistance(target) + 500;
|
||||
else if (target->GetEntry() == NPC_BURU && target->HasAura(SPELL_THORNS))
|
||||
damage = target->GetMaxHealth() * 7.f / 100;
|
||||
|
||||
GetCaster()->CastCustomSpell(target, SPELL_EXPLODE_2, &damage, nullptr, nullptr, true);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* attacker) override
|
||||
{
|
||||
if (Creature* buru = me->GetMap()->GetCreature(_instance->GetGuidData(DATA_BURU)))
|
||||
if (!buru->IsInCombat())
|
||||
buru->AI()->AttackStart(attacker);
|
||||
}
|
||||
|
||||
void JustSummoned(Creature* who) override
|
||||
{
|
||||
if (who->GetEntry() == NPC_HATCHLING)
|
||||
if (Creature* buru = me->GetMap()->GetCreature(_instance->GetGuidData(DATA_BURU)))
|
||||
if (Unit* target = buru->AI()->SelectTarget(SelectTargetMethod::Random))
|
||||
who->AI()->AttackStart(target);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
DoCastAOE(SPELL_EXPLODE, true);
|
||||
DoCastAOE(SPELL_EXPLODE_2, true); // Unknown purpose
|
||||
DoCast(me, SPELL_SUMMON_HATCHLING, true);
|
||||
|
||||
if (Creature* buru = me->GetMap()->GetCreature(_instance->GetGuidData(DATA_BURU)))
|
||||
if (boss_buru::boss_buruAI* buruAI = dynamic_cast<boss_buru::boss_buruAI*>(buru->AI()))
|
||||
buruAI->ManageRespawn(me->GetGUID());
|
||||
}
|
||||
private:
|
||||
InstanceScript* _instance;
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetRuinsOfAhnQirajAI<npc_buru_eggAI>(creature);
|
||||
}
|
||||
};
|
||||
|
||||
class spell_egg_explosion : public SpellScriptLoader
|
||||
{
|
||||
public:
|
||||
spell_egg_explosion() : SpellScriptLoader("spell_egg_explosion") { }
|
||||
|
||||
class spell_egg_explosion_SpellScript : public SpellScript
|
||||
void Register() override
|
||||
{
|
||||
PrepareSpellScript(spell_egg_explosion_SpellScript);
|
||||
|
||||
void HandleAfterCast()
|
||||
{
|
||||
if (Creature* buru = GetCaster()->FindNearestCreature(NPC_BURU, 5.f))
|
||||
buru->AI()->DoAction(ACTION_EXPLODE);
|
||||
}
|
||||
|
||||
void HandleDummyHitTarget(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
if (Unit* target = GetHitUnit())
|
||||
Unit::DealDamage(GetCaster(), target, -16 * GetCaster()->GetDistance(target) + 500);
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterCast += SpellCastFn(spell_egg_explosion_SpellScript::HandleAfterCast);
|
||||
OnEffectHitTarget += SpellEffectFn(spell_egg_explosion_SpellScript::HandleDummyHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY);
|
||||
}
|
||||
};
|
||||
|
||||
SpellScript* GetSpellScript() const override
|
||||
{
|
||||
return new spell_egg_explosion_SpellScript();
|
||||
OnEffectHitTarget += SpellEffectFn(spell_egg_explosion::HandleDummyHitTarget, EFFECT_0, SPELL_EFFECT_DUMMY);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_buru()
|
||||
{
|
||||
new boss_buru();
|
||||
new npc_buru_egg();
|
||||
new spell_egg_explosion();
|
||||
RegisterRuinsOfAhnQirajCreatureAI(boss_buru);
|
||||
RegisterRuinsOfAhnQirajCreatureAI(npc_buru_egg);
|
||||
RegisterSpellScript(spell_egg_explosion);
|
||||
}
|
||||
|
||||
@@ -16,121 +16,134 @@
|
||||
*/
|
||||
|
||||
#include "CreatureTextMgr.h"
|
||||
#include "GameObjectAI.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "ruins_of_ahnqiraj.h"
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_MORTALWOUND = 25646,
|
||||
SPELL_SANDTRAP = 25648,
|
||||
SPELL_MORTAL_WOUND = 25646,
|
||||
SPELL_SAND_TRAP = 25648,
|
||||
SPELL_ENRAGE = 26527,
|
||||
SPELL_SUMMON_PLAYER = 26446,
|
||||
SPELL_TRASH = 3391, // Should perhaps be triggered by an aura? Couldn't find any though
|
||||
SPELL_WIDE_SLASH = 25814
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
EVENT_MORTAL_WOUND = 1,
|
||||
EVENT_SANDTRAP = 2,
|
||||
EVENT_TRASH = 3,
|
||||
EVENT_WIDE_SLASH = 4
|
||||
EVENT_SAND_TRAP = 2,
|
||||
EVENT_WIDE_SLASH = 3
|
||||
};
|
||||
|
||||
enum Texts
|
||||
{
|
||||
SAY_KURINAXX_DEATH = 5, // Yelled by Ossirian the Unscarred
|
||||
SAY_KURINNAXX_DEATH = 5 // Yell by 'Ossirian the Unscarred'
|
||||
};
|
||||
|
||||
class boss_kurinnaxx : public CreatureScript
|
||||
struct boss_kurinnaxx : public BossAI
|
||||
{
|
||||
public:
|
||||
boss_kurinnaxx() : CreatureScript("boss_kurinnaxx") { }
|
||||
boss_kurinnaxx(Creature* creature) : BossAI(creature, DATA_KURINNAXX) {}
|
||||
|
||||
struct boss_kurinnaxxAI : public BossAI
|
||||
void Reset() override
|
||||
{
|
||||
boss_kurinnaxxAI(Creature* creature) : BossAI(creature, DATA_KURINNAXX)
|
||||
{
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
_enraged = false;
|
||||
events.ScheduleEvent(EVENT_MORTAL_WOUND, 8000);
|
||||
events.ScheduleEvent(EVENT_SANDTRAP, urand(5000, 15000));
|
||||
events.ScheduleEvent(EVENT_TRASH, 1000);
|
||||
events.ScheduleEvent(EVENT_WIDE_SLASH, 11000);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (!_enraged && HealthBelowPct(30))
|
||||
{
|
||||
DoCast(me, SPELL_ENRAGE);
|
||||
_enraged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
_JustDied();
|
||||
if (Creature* Ossirian = me->GetMap()->GetCreature(instance->GetGuidData(DATA_OSSIRIAN)))
|
||||
sCreatureTextMgr->SendChat(Ossirian, SAY_KURINAXX_DEATH, nullptr, CHAT_MSG_ADDON, LANG_ADDON, TEXT_RANGE_ZONE);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_MORTAL_WOUND:
|
||||
DoCastVictim(SPELL_MORTALWOUND);
|
||||
events.ScheduleEvent(EVENT_MORTAL_WOUND, 8000);
|
||||
break;
|
||||
case EVENT_SANDTRAP:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true))
|
||||
target->CastSpell(target, SPELL_SANDTRAP, true);
|
||||
else if (Unit* victim = me->GetVictim())
|
||||
victim->CastSpell(victim, SPELL_SANDTRAP, true);
|
||||
events.ScheduleEvent(EVENT_SANDTRAP, urand(5000, 15000));
|
||||
break;
|
||||
case EVENT_WIDE_SLASH:
|
||||
DoCast(me, SPELL_WIDE_SLASH);
|
||||
events.ScheduleEvent(EVENT_WIDE_SLASH, 11000);
|
||||
break;
|
||||
case EVENT_TRASH:
|
||||
DoCast(me, SPELL_TRASH);
|
||||
events.ScheduleEvent(EVENT_WIDE_SLASH, 16000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
private:
|
||||
bool _enraged;
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetRuinsOfAhnQirajAI<boss_kurinnaxxAI>(creature);
|
||||
BossAI::Reset();
|
||||
_enraged = false;
|
||||
events.ScheduleEvent(EVENT_MORTAL_WOUND, 8s, 10s);
|
||||
events.ScheduleEvent(EVENT_SAND_TRAP, 5s, 15s);
|
||||
events.ScheduleEvent(EVENT_WIDE_SLASH, 10s, 15s);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
|
||||
{
|
||||
if (!_enraged && HealthBelowPct(30))
|
||||
{
|
||||
DoCastSelf(SPELL_ENRAGE);
|
||||
_enraged = true;
|
||||
}
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
if (killer)
|
||||
killer->GetMap()->LoadGrid(-9502.80f, 2042.65f); // Ossirian grid
|
||||
|
||||
if (Creature* ossirian = instance->GetCreature(DATA_OSSIRIAN))
|
||||
{
|
||||
ossirian->setActive(true);
|
||||
if (ossirian->GetAI())
|
||||
ossirian->AI()->Talk(SAY_KURINNAXX_DEATH);
|
||||
}
|
||||
BossAI::JustDied(killer);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
return;
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_MORTAL_WOUND:
|
||||
DoCastVictim(SPELL_MORTAL_WOUND);
|
||||
events.ScheduleEvent(EVENT_MORTAL_WOUND, 8s, 10s);
|
||||
break;
|
||||
case EVENT_SAND_TRAP:
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100.f, true))
|
||||
{
|
||||
target->CastSpell(target, SPELL_SAND_TRAP, true, nullptr, nullptr, me->GetGUID());
|
||||
}
|
||||
events.ScheduleEvent(EVENT_SAND_TRAP, 5s, 15s);
|
||||
break;
|
||||
case EVENT_WIDE_SLASH:
|
||||
DoCastSelf(SPELL_WIDE_SLASH);
|
||||
events.ScheduleEvent(EVENT_WIDE_SLASH, 12s, 15s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
private:
|
||||
bool _enraged;
|
||||
};
|
||||
|
||||
struct go_sand_trap : public GameObjectAI
|
||||
{
|
||||
go_sand_trap(GameObject* go) : GameObjectAI(go) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_scheduler.Schedule(5s, [this](TaskContext /*context*/)
|
||||
{
|
||||
if (InstanceScript* instance = me->GetInstanceScript())
|
||||
if (Creature* kurinnaxx = instance->GetCreature(DATA_KURINNAXX))
|
||||
me->Use(kurinnaxx);
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 const diff) override
|
||||
{
|
||||
_scheduler.Update(diff);
|
||||
}
|
||||
|
||||
protected:
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
void AddSC_boss_kurinnaxx()
|
||||
{
|
||||
new boss_kurinnaxx();
|
||||
RegisterRuinsOfAhnQirajCreatureAI(boss_kurinnaxx);
|
||||
RegisterGameObjectAI(go_sand_trap);
|
||||
}
|
||||
|
||||
@@ -15,173 +15,180 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "ruins_of_ahnqiraj.h"
|
||||
|
||||
enum Texts
|
||||
{
|
||||
EMOTE_AGGRO = 0,
|
||||
EMOTE_MANA_FULL = 1
|
||||
EMOTE_AGGRO = 0,
|
||||
EMOTE_MANA_FULL = 1,
|
||||
EMOTE_STONE_PHASE = 2
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_TRAMPLE = 15550,
|
||||
SPELL_DRAIN_MANA_SERVERSIDE = 25676,
|
||||
SPELL_DRAIN_MANA = 25671,
|
||||
SPELL_ARCANE_ERUPTION = 25672,
|
||||
SPELL_SUMMON_MANA_FIENDS = 25684,
|
||||
SPELL_SUMMON_MANA_FIEND_1 = 25681, // TARGET_DEST_CASTER_FRONT
|
||||
SPELL_SUMMON_MANA_FIEND_2 = 25682, // TARGET_DEST_CASTER_LEFT
|
||||
SPELL_SUMMON_MANA_FIEND_3 = 25683, // TARGET_DEST_CASTER_RIGHT
|
||||
SPELL_ENERGIZE = 25685
|
||||
SPELL_ENERGIZE = 25685,
|
||||
|
||||
SPELL_LARGE_OBSIDIAN_CHUNK = 27630 // Server-side
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
EVENT_TRAMPLE = 1,
|
||||
EVENT_DRAIN_MANA = 2,
|
||||
EVENT_STONE_PHASE = 3,
|
||||
EVENT_STONE_PHASE_END = 4,
|
||||
EVENT_WIDE_SLASH = 5,
|
||||
EVENT_SPELL_TRAMPLE = 1,
|
||||
EVENT_SPELL_DRAIN_MANA = 2,
|
||||
EVENT_STONE_PHASE = 3,
|
||||
EVENT_STONE_PHASE_END = 4
|
||||
};
|
||||
|
||||
enum Actions
|
||||
struct boss_moam : public BossAI
|
||||
{
|
||||
ACTION_STONE_PHASE_START = 1,
|
||||
ACTION_STONE_PHASE_END = 2,
|
||||
};
|
||||
boss_moam(Creature* creature) : BossAI(creature, DATA_MOAM) {}
|
||||
|
||||
class boss_moam : public CreatureScript
|
||||
{
|
||||
public:
|
||||
boss_moam() : CreatureScript("boss_moam") { }
|
||||
|
||||
struct boss_moamAI : public BossAI
|
||||
void Reset() override
|
||||
{
|
||||
boss_moamAI(Creature* creature) : BossAI(creature, DATA_MOAM)
|
||||
{
|
||||
}
|
||||
_Reset();
|
||||
me->SetPower(POWER_MANA, 0);
|
||||
me->SetRegeneratingPower(false);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
me->SetPower(POWER_MANA, 0);
|
||||
_isStonePhase = false;
|
||||
events.ScheduleEvent(EVENT_STONE_PHASE, 90000);
|
||||
//events.ScheduleEvent(EVENT_WIDE_SLASH, 11000);
|
||||
}
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
BossAI::EnterCombat(who);
|
||||
Talk(EMOTE_AGGRO);
|
||||
events.ScheduleEvent(EVENT_STONE_PHASE, 90000);
|
||||
events.ScheduleEvent(EVENT_SPELL_TRAMPLE, 9000);
|
||||
events.ScheduleEvent(EVENT_SPELL_DRAIN_MANA, 3000);
|
||||
}
|
||||
|
||||
void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
_JustDied();
|
||||
DoCastAOE(SPELL_LARGE_OBSIDIAN_CHUNK, true);
|
||||
}
|
||||
|
||||
void SummonedCreatureDies(Creature* /*creature*/, Unit* /*killer*/) override
|
||||
{
|
||||
if (!summons.IsAnyCreatureAlive() && me->HasAura(SPELL_ENERGIZE))
|
||||
{
|
||||
if (!_isStonePhase && HealthBelowPct(45))
|
||||
events.RescheduleEvent(EVENT_STONE_PHASE_END, 1s);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
if (me->GetPower(POWER_MANA) == me->GetMaxPower(POWER_MANA))
|
||||
{
|
||||
if (me->HasAura(SPELL_ENERGIZE))
|
||||
{
|
||||
_isStonePhase = true;
|
||||
DoAction(ACTION_STONE_PHASE_START);
|
||||
me->RemoveAurasDueToSpell(SPELL_ENERGIZE);
|
||||
events.RescheduleEvent(EVENT_STONE_PHASE_END, 1s);
|
||||
}
|
||||
|
||||
Talk(EMOTE_MANA_FULL);
|
||||
DoCastAOE(SPELL_ARCANE_ERUPTION);
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
switch (action)
|
||||
switch (eventId)
|
||||
{
|
||||
case ACTION_STONE_PHASE_END:
|
||||
{
|
||||
me->RemoveAurasDueToSpell(SPELL_ENERGIZE);
|
||||
events.ScheduleEvent(EVENT_STONE_PHASE, 90000);
|
||||
_isStonePhase = false;
|
||||
break;
|
||||
}
|
||||
case ACTION_STONE_PHASE_START:
|
||||
{
|
||||
DoCast(me, SPELL_SUMMON_MANA_FIEND_1);
|
||||
DoCast(me, SPELL_SUMMON_MANA_FIEND_2);
|
||||
DoCast(me, SPELL_SUMMON_MANA_FIEND_3);
|
||||
DoCast(me, SPELL_ENERGIZE);
|
||||
events.ScheduleEvent(EVENT_STONE_PHASE_END, 90000);
|
||||
break;
|
||||
}
|
||||
case EVENT_STONE_PHASE:
|
||||
Talk(EMOTE_STONE_PHASE);
|
||||
DoCastAOE(SPELL_SUMMON_MANA_FIENDS);
|
||||
DoCastSelf(SPELL_ENERGIZE);
|
||||
events.CancelEvent(EVENT_SPELL_DRAIN_MANA);
|
||||
events.ScheduleEvent(EVENT_STONE_PHASE_END, 90000);
|
||||
break;
|
||||
case EVENT_STONE_PHASE_END:
|
||||
me->RemoveAurasDueToSpell(SPELL_ENERGIZE);
|
||||
events.ScheduleEvent(EVENT_SPELL_DRAIN_MANA, urand(2000, 6000));
|
||||
events.ScheduleEvent(EVENT_STONE_PHASE, 90000);
|
||||
break;
|
||||
case EVENT_SPELL_DRAIN_MANA:
|
||||
DoCastAOE(SPELL_DRAIN_MANA_SERVERSIDE);
|
||||
events.ScheduleEvent(EVENT_SPELL_DRAIN_MANA, urand(2000, 6000));
|
||||
break;
|
||||
case EVENT_SPELL_TRAMPLE:
|
||||
DoCastAOE(SPELL_TRAMPLE);
|
||||
events.ScheduleEvent(EVENT_SPELL_TRAMPLE, 15000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
events.Update(diff);
|
||||
// 25676 - Drain Mana (server-side)
|
||||
class spell_moam_mana_drain_filter : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_moam_mana_drain_filter);
|
||||
|
||||
if (me->GetPower(POWER_MANA) == me->GetMaxPower(POWER_MANA))
|
||||
{
|
||||
if (_isStonePhase)
|
||||
DoAction(ACTION_STONE_PHASE_END);
|
||||
DoCastAOE(SPELL_ARCANE_ERUPTION);
|
||||
me->SetPower(POWER_MANA, 0);
|
||||
}
|
||||
|
||||
if (_isStonePhase)
|
||||
{
|
||||
if (events.ExecuteEvent() == EVENT_STONE_PHASE_END)
|
||||
DoAction(ACTION_STONE_PHASE_END);
|
||||
return;
|
||||
}
|
||||
|
||||
// Messing up mana-drain channel
|
||||
//if (me->HasUnitState(UNIT_STATE_CASTING))
|
||||
// return;
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_STONE_PHASE:
|
||||
DoAction(ACTION_STONE_PHASE_START);
|
||||
break;
|
||||
case EVENT_DRAIN_MANA:
|
||||
{
|
||||
std::list<Unit*> targetList;
|
||||
{
|
||||
const std::list<HostileReference*>& threatlist = me->GetThreatMgr().getThreatList();
|
||||
for (std::list<HostileReference*>::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
|
||||
if ((*itr)->getTarget()->GetTypeId() == TYPEID_PLAYER && (*itr)->getTarget()->getPowerType() == POWER_MANA)
|
||||
targetList.push_back((*itr)->getTarget());
|
||||
}
|
||||
|
||||
Acore::Containers::RandomResize(targetList, 5);
|
||||
|
||||
for (std::list<Unit*>::iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
|
||||
DoCast(*itr, SPELL_DRAIN_MANA);
|
||||
|
||||
events.ScheduleEvent(EVENT_DRAIN_MANA, urand(5000, 15000));
|
||||
break;
|
||||
}/*
|
||||
case EVENT_WIDE_SLASH:
|
||||
DoCast(me, SPELL_WIDE_SLASH);
|
||||
events.ScheduleEvent(EVENT_WIDE_SLASH, 11000);
|
||||
break;
|
||||
case EVENT_TRASH:
|
||||
DoCast(me, SPELL_TRASH);
|
||||
events.ScheduleEvent(EVENT_WIDE_SLASH, 16000);
|
||||
break;*/
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
private:
|
||||
bool _isStonePhase;
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
void FilterTargets(std::list<WorldObject*>& targets)
|
||||
{
|
||||
return GetRuinsOfAhnQirajAI<boss_moamAI>(creature);
|
||||
targets.remove_if([&](WorldObject* target) -> bool
|
||||
{
|
||||
return !target->IsPlayer() || target->ToPlayer()->getPowerType() != POWER_MANA;
|
||||
});
|
||||
}
|
||||
|
||||
void HandleScript(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
caster->CastSpell(GetHitUnit(), SPELL_DRAIN_MANA, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_moam_mana_drain_filter::FilterTargets, EFFECT_ALL, TARGET_UNIT_DEST_AREA_ENEMY);
|
||||
OnEffectHitTarget += SpellEffectFn(spell_moam_mana_drain_filter::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
||||
}
|
||||
};
|
||||
|
||||
// 25684 - Summon Mana Fiends (server-side)
|
||||
class spell_moam_summon_mana_fiends : public SpellScript
|
||||
{
|
||||
PrepareSpellScript(spell_moam_summon_mana_fiends);
|
||||
|
||||
void HandleScript(SpellEffIndex /*effIndex*/)
|
||||
{
|
||||
const uint32 spellIds[3] = { SPELL_SUMMON_MANA_FIEND_1, SPELL_SUMMON_MANA_FIEND_2, SPELL_SUMMON_MANA_FIEND_3 };
|
||||
|
||||
for (uint32 spellId : spellIds)
|
||||
{
|
||||
GetCaster()->CastSpell((Unit*)nullptr, spellId, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectHitTarget += SpellEffectFn(spell_moam_summon_mana_fiends::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_moam()
|
||||
{
|
||||
new boss_moam();
|
||||
RegisterRuinsOfAhnQirajCreatureAI(boss_moam);
|
||||
RegisterSpellScript(spell_moam_mana_drain_filter);
|
||||
RegisterSpellScript(spell_moam_summon_mana_fiends);
|
||||
}
|
||||
|
||||
@@ -22,255 +22,248 @@
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellInfo.h"
|
||||
#include "ruins_of_ahnqiraj.h"
|
||||
#include "TaskScheduler.h"
|
||||
|
||||
enum Texts
|
||||
{
|
||||
SAY_SUPREME = 0,
|
||||
SAY_INTRO = 1,
|
||||
SAY_AGGRO = 2,
|
||||
SAY_SLAY = 3,
|
||||
SAY_DEATH = 4
|
||||
SAY_SUPREME = 0,
|
||||
SAY_INTRO = 1,
|
||||
SAY_AGGRO = 2,
|
||||
SAY_SLAY = 3,
|
||||
SAY_DEATH = 4
|
||||
};
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_SILENCE = 25195,
|
||||
SPELL_CYCLONE = 25189,
|
||||
SPELL_STOMP = 25188,
|
||||
SPELL_SUPREME = 25176,
|
||||
SPELL_SUMMON = 20477,
|
||||
SPELL_SAND_STORM = 25160,
|
||||
SPELL_SUMMON_CRYSTAL = 25192
|
||||
SPELL_CURSE_OF_TONGUES = 25195,
|
||||
SPELL_ENVELOPING_WINDS = 25189,
|
||||
SPELL_WAR_STOMP = 25188,
|
||||
SPELL_STRENGHT_OF_OSSIRIAN = 25176,
|
||||
SPELL_SAND_STORM = 25160,
|
||||
SPELL_SUMMON_CRYSTAL = 25192
|
||||
};
|
||||
|
||||
enum Actions
|
||||
{
|
||||
ACTION_TRIGGER_WEAKNESS = 1
|
||||
ACTION_TRIGGER_WEAKNESS = 1
|
||||
};
|
||||
|
||||
enum Events
|
||||
{
|
||||
EVENT_SILENCE = 1,
|
||||
EVENT_CYCLONE = 2,
|
||||
EVENT_STOMP = 3
|
||||
EVENT_SILENCE = 1,
|
||||
EVENT_CYCLONE = 2,
|
||||
EVENT_STOMP = 3
|
||||
};
|
||||
|
||||
uint8 const NUM_CRYSTALS = 9;
|
||||
|
||||
// You spin me right round, baby
|
||||
// right round like a record, baby
|
||||
// right round round round
|
||||
Position CrystalCoordinates[NUM_CRYSTALS] =
|
||||
{
|
||||
{ -9394.230469f, 1951.808594f, 85.97733f, 0.0f },
|
||||
{ -9394.230469f, 1951.808594f, 85.97733f, 0.0f },
|
||||
{ -9357.931641f, 1930.596802f, 85.556198f, 0.0f },
|
||||
{ -9383.113281f, 2011.042725f, 85.556389f, 0.0f },
|
||||
{ -9243.36f, 1979.04f, 85.556f, 0.0f },
|
||||
{ -9281.68f, 1886.66f, 85.5558f, 0.0f },
|
||||
{ -9241.8f, 1806.39f, 85.5557f, 0.0f },
|
||||
{ -9366.78f, 1781.76f, 85.5561f, 0.0f },
|
||||
{ -9430.37f, 1786.86f, 85.557f, 0.0f },
|
||||
{ -9406.73f, 1863.13f, 85.5558f, 0.0f }
|
||||
{ -9243.36f, 1979.04f, 85.556f, 0.0f },
|
||||
{ -9281.68f, 1886.66f, 85.5558f, 0.0f },
|
||||
{ -9241.8f, 1806.39f, 85.5557f, 0.0f },
|
||||
{ -9366.78f, 1781.76f, 85.5561f, 0.0f },
|
||||
{ -9430.37f, 1786.86f, 85.557f, 0.0f },
|
||||
{ -9406.73f, 1863.13f, 85.5558f, 0.0f }
|
||||
};
|
||||
|
||||
float RoomRadius = 165.0f;
|
||||
uint8 const NUM_TORNADOS = 5; /// @todo This number is completly random!
|
||||
float roomRadius = 165.0f;
|
||||
uint8 const NUM_TORNADOS = 2;
|
||||
uint8 const NUM_WEAKNESS = 5;
|
||||
uint32 const SpellWeakness[NUM_WEAKNESS] = { 25177, 25178, 25180, 25181, 25183 };
|
||||
uint32 const spellWeakness[NUM_WEAKNESS] = { 25177, 25178, 25180, 25181, 25183 };
|
||||
Position const RoomCenter = { -9343.041992f, 1923.278198f, 85.555984f, 0.0 };
|
||||
|
||||
class boss_ossirian : public CreatureScript
|
||||
struct boss_ossirian : public BossAI
|
||||
{
|
||||
public:
|
||||
boss_ossirian() : CreatureScript("boss_ossirian") { }
|
||||
|
||||
struct boss_ossirianAI : public BossAI
|
||||
boss_ossirian(Creature* creature) : BossAI(creature, DATA_OSSIRIAN)
|
||||
{
|
||||
boss_ossirianAI(Creature* creature) : BossAI(creature, DATA_OSSIRIAN)
|
||||
{
|
||||
SaidIntro = false;
|
||||
}
|
||||
|
||||
ObjectGuid TriggerGUID;
|
||||
ObjectGuid CrystalGUID;
|
||||
uint8 CrystalIterator;
|
||||
bool SaidIntro;
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
CrystalIterator = 0;
|
||||
TriggerGUID.Clear();
|
||||
CrystalGUID.Clear();
|
||||
}
|
||||
|
||||
void SpellHit(Unit* caster, SpellInfo const* spell) override
|
||||
{
|
||||
for (uint8 i = 0; i < NUM_WEAKNESS; ++i)
|
||||
{
|
||||
if (spell->Id == SpellWeakness[i])
|
||||
{
|
||||
me->RemoveAurasDueToSpell(SPELL_SUPREME);
|
||||
((TempSummon*)caster)->UnSummon();
|
||||
SpawnNextCrystal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
{
|
||||
if (action == ACTION_TRIGGER_WEAKNESS)
|
||||
if (Creature* Trigger = me->GetMap()->GetCreature(TriggerGUID))
|
||||
if (!Trigger->HasUnitState(UNIT_STATE_CASTING))
|
||||
Trigger->CastSpell(Trigger, SpellWeakness[urand(0, 4)], false);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
_EnterCombat();
|
||||
events.Reset();
|
||||
events.ScheduleEvent(EVENT_SILENCE, 30000);
|
||||
events.ScheduleEvent(EVENT_CYCLONE, 20000);
|
||||
events.ScheduleEvent(EVENT_STOMP, 30000);
|
||||
|
||||
DoCast(me, SPELL_SUPREME);
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
Map* map = me->GetMap();
|
||||
if (!map->IsDungeon())
|
||||
return;
|
||||
|
||||
WorldPackets::Misc::Weather weather(WEATHER_STATE_HEAVY_SANDSTORM, 1.0f);
|
||||
map->SendToPlayers(weather.Write());
|
||||
|
||||
for (uint8 i = 0; i < NUM_TORNADOS; ++i)
|
||||
{
|
||||
Position Point = me->GetRandomPoint(RoomCenter, RoomRadius);
|
||||
if (Creature* Tornado = me->GetMap()->SummonCreature(NPC_SAND_VORTEX, Point))
|
||||
Tornado->CastSpell(Tornado, SPELL_SAND_STORM, true);
|
||||
}
|
||||
|
||||
SpawnNextCrystal();
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
Talk(SAY_SLAY);
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
Cleanup();
|
||||
summons.DespawnAll();
|
||||
BossAI::EnterEvadeMode(why);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
Cleanup();
|
||||
_JustDied();
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
if (GameObject* Crystal = me->GetMap()->GetGameObject(CrystalGUID))
|
||||
Crystal->Use(me);
|
||||
}
|
||||
|
||||
void SpawnNextCrystal()
|
||||
{
|
||||
if (CrystalIterator == NUM_CRYSTALS)
|
||||
CrystalIterator = 0;
|
||||
|
||||
if (Creature* Trigger = me->GetMap()->SummonCreature(NPC_OSSIRIAN_TRIGGER, CrystalCoordinates[CrystalIterator]))
|
||||
{
|
||||
TriggerGUID = Trigger->GetGUID();
|
||||
if (GameObject* Crystal = Trigger->SummonGameObject(GO_OSSIRIAN_CRYSTAL,
|
||||
CrystalCoordinates[CrystalIterator].GetPositionX(),
|
||||
CrystalCoordinates[CrystalIterator].GetPositionY(),
|
||||
CrystalCoordinates[CrystalIterator].GetPositionZ(),
|
||||
0, 0, 0, 0, 0, uint32(-1)))
|
||||
{
|
||||
CrystalGUID = Crystal->GetGUID();
|
||||
++CrystalIterator;
|
||||
Crystal->SetOwnerGUID(ObjectGuid::Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
|
||||
{
|
||||
if (!SaidIntro)
|
||||
{
|
||||
Talk(SAY_INTRO);
|
||||
SaidIntro = true;
|
||||
}
|
||||
BossAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
|
||||
// No kiting!
|
||||
if (me->GetDistance(me->GetVictim()) > 60.00f && me->GetDistance(me->GetVictim()) < 120.00f)
|
||||
DoCastVictim(SPELL_SUMMON);
|
||||
|
||||
bool ApplySupreme = true;
|
||||
|
||||
if (me->HasAura(SPELL_SUPREME))
|
||||
ApplySupreme = false;
|
||||
else
|
||||
{
|
||||
for (uint8 i = 0; i < NUM_WEAKNESS; ++i)
|
||||
{
|
||||
if (me->HasAura(SpellWeakness[i]))
|
||||
{
|
||||
ApplySupreme = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ApplySupreme)
|
||||
{
|
||||
DoCast(me, SPELL_SUPREME);
|
||||
Talk(SAY_SUPREME);
|
||||
}
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_SILENCE:
|
||||
DoCast(me, SPELL_SILENCE);
|
||||
events.ScheduleEvent(EVENT_SILENCE, urand(20000, 30000));
|
||||
break;
|
||||
case EVENT_CYCLONE:
|
||||
DoCastVictim(SPELL_CYCLONE);
|
||||
events.ScheduleEvent(EVENT_CYCLONE, 20000);
|
||||
break;
|
||||
case EVENT_STOMP:
|
||||
DoCast(me, SPELL_STOMP);
|
||||
events.ScheduleEvent(EVENT_STOMP, 30000);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
};
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return GetRuinsOfAhnQirajAI<boss_ossirianAI>(creature);
|
||||
_saidIntro = false;
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
BossAI::Reset();
|
||||
_crystalIterator = 0;
|
||||
_triggerGUID.Clear();
|
||||
_crystalGUID.Clear();
|
||||
}
|
||||
|
||||
void SpellHit(Unit* caster, SpellInfo const* spell) override
|
||||
{
|
||||
for (uint8 weakness : spellWeakness)
|
||||
{
|
||||
if (spell->Id == weakness)
|
||||
{
|
||||
me->RemoveAurasDueToSpell(SPELL_STRENGHT_OF_OSSIRIAN);
|
||||
((TempSummon*)caster)->UnSummon();
|
||||
SpawnNextCrystal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
{
|
||||
if (action == ACTION_TRIGGER_WEAKNESS)
|
||||
{
|
||||
if (Creature* trigger = me->GetMap()->GetCreature(_triggerGUID))
|
||||
{
|
||||
if (!trigger->HasUnitState(UNIT_STATE_CASTING))
|
||||
{
|
||||
trigger->CastSpell(trigger, spellWeakness[urand(0, 4)], false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* who) override
|
||||
{
|
||||
BossAI::EnterCombat(who);
|
||||
events.Reset();
|
||||
events.ScheduleEvent(EVENT_SILENCE, 30s);
|
||||
events.ScheduleEvent(EVENT_CYCLONE, 20s);
|
||||
events.ScheduleEvent(EVENT_STOMP, 30s);
|
||||
DoCastSelf(SPELL_STRENGHT_OF_OSSIRIAN);
|
||||
Talk(SAY_AGGRO);
|
||||
|
||||
Map* map = me->GetMap();
|
||||
if (!map->IsDungeon())
|
||||
return;
|
||||
|
||||
WorldPackets::Misc::Weather weather(WEATHER_STATE_HEAVY_SANDSTORM, 1.0f);
|
||||
map->SendToPlayers(weather.Write());
|
||||
for (uint8 i = 0; i < NUM_TORNADOS; ++i)
|
||||
{
|
||||
Position Point = me->GetRandomPoint(RoomCenter, roomRadius);
|
||||
if (Creature* Tornado = me->GetMap()->SummonCreature(NPC_SAND_VORTEX, Point))
|
||||
{
|
||||
Tornado->CastSpell(Tornado, SPELL_SAND_STORM, true);
|
||||
}
|
||||
}
|
||||
SpawnNextCrystal();
|
||||
}
|
||||
|
||||
void KilledUnit(Unit* /*victim*/) override
|
||||
{
|
||||
Talk(SAY_SLAY);
|
||||
}
|
||||
|
||||
void EnterEvadeMode(EvadeReason why) override
|
||||
{
|
||||
Cleanup();
|
||||
summons.DespawnAll();
|
||||
BossAI::EnterEvadeMode(why);
|
||||
}
|
||||
|
||||
void JustDied(Unit* killer) override
|
||||
{
|
||||
Cleanup();
|
||||
BossAI::JustDied(killer);
|
||||
}
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
if (GameObject* crystal = me->GetMap()->GetGameObject(_crystalGUID))
|
||||
{
|
||||
crystal->Use(me);
|
||||
}
|
||||
|
||||
std::list<Creature*> vortexes;
|
||||
me->GetCreaturesWithEntryInRange(vortexes, 200.f, NPC_SAND_VORTEX);
|
||||
for (Creature* vortex : vortexes)
|
||||
vortex->DespawnOrUnsummon();
|
||||
}
|
||||
|
||||
void SpawnNextCrystal()
|
||||
{
|
||||
if (_crystalIterator == NUM_CRYSTALS)
|
||||
_crystalIterator = 0;
|
||||
|
||||
if (Creature* trigger = me->GetMap()->SummonCreature(NPC_OSSIRIAN_TRIGGER, CrystalCoordinates[_crystalIterator]))
|
||||
{
|
||||
_triggerGUID = trigger->GetGUID();
|
||||
if (GameObject* crystal = trigger->SummonGameObject(GO_OSSIRIAN_CRYSTAL,
|
||||
CrystalCoordinates[_crystalIterator].GetPositionX(),
|
||||
CrystalCoordinates[_crystalIterator].GetPositionY(),
|
||||
CrystalCoordinates[_crystalIterator].GetPositionZ(),
|
||||
0, 0, 0, 0, 0, uint32(-1)))
|
||||
{
|
||||
_crystalGUID = crystal->GetGUID();
|
||||
++_crystalIterator;
|
||||
crystal->SetOwnerGUID(ObjectGuid::Empty);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
|
||||
{
|
||||
if (!_saidIntro)
|
||||
{
|
||||
Talk(SAY_INTRO);
|
||||
_saidIntro = true;
|
||||
}
|
||||
BossAI::MoveInLineOfSight(who);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
events.Update(diff);
|
||||
bool applySupreme = true;
|
||||
if (me->HasAura(SPELL_STRENGHT_OF_OSSIRIAN))
|
||||
{
|
||||
applySupreme = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8 weakness : spellWeakness)
|
||||
{
|
||||
if (me->HasAura(weakness))
|
||||
{
|
||||
applySupreme = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (applySupreme)
|
||||
{
|
||||
DoCastSelf(SPELL_STRENGHT_OF_OSSIRIAN);
|
||||
Talk(SAY_SUPREME);
|
||||
}
|
||||
|
||||
while (uint32 eventId = events.ExecuteEvent())
|
||||
{
|
||||
switch (eventId)
|
||||
{
|
||||
case EVENT_SILENCE:
|
||||
DoCastAOE(SPELL_CURSE_OF_TONGUES);
|
||||
events.ScheduleEvent(EVENT_SILENCE, 20s, 30s);
|
||||
break;
|
||||
case EVENT_CYCLONE:
|
||||
DoCastVictim(SPELL_ENVELOPING_WINDS);
|
||||
events.ScheduleEvent(EVENT_CYCLONE, 20s);
|
||||
break;
|
||||
case EVENT_STOMP:
|
||||
DoCastAOE(SPELL_WAR_STOMP);
|
||||
events.ScheduleEvent(EVENT_STOMP, 30s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
DoMeleeAttackIfReady();
|
||||
}
|
||||
|
||||
protected:
|
||||
ObjectGuid _triggerGUID;
|
||||
ObjectGuid _crystalGUID;
|
||||
uint8 _crystalIterator;
|
||||
bool _saidIntro;
|
||||
};
|
||||
|
||||
class go_ossirian_crystal : public GameObjectScript
|
||||
@@ -280,21 +273,99 @@ public:
|
||||
|
||||
bool OnGossipHello(Player* player, GameObject* /*go*/) override
|
||||
{
|
||||
InstanceScript* Instance = player->GetInstanceScript();
|
||||
if (!Instance)
|
||||
InstanceScript* instance = player->GetInstanceScript();
|
||||
if (!instance)
|
||||
return false;
|
||||
|
||||
Creature* Ossirian = player->FindNearestCreature(NPC_OSSIRIAN, 30.0f);
|
||||
if (!Ossirian || Instance->GetBossState(DATA_OSSIRIAN) != IN_PROGRESS)
|
||||
Creature* ossirian = instance->GetCreature(DATA_OSSIRIAN);
|
||||
if (!ossirian || instance->GetBossState(DATA_OSSIRIAN) != IN_PROGRESS)
|
||||
return false;
|
||||
|
||||
Ossirian->AI()->DoAction(ACTION_TRIGGER_WEAKNESS);
|
||||
ossirian->AI()->DoAction(ACTION_TRIGGER_WEAKNESS);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
enum AnubisathGuardian
|
||||
{
|
||||
SPELL_METEOR = 24340,
|
||||
SPELL_PLAGUE = 22997,
|
||||
SPELL_SHADOW_STORM = 2148,
|
||||
SPELL_THUNDER_CLAP = 2834,
|
||||
SPELL_REFLECT_ARCANE_FIRE = 13022,
|
||||
SPELL_REFLECT_FROST_SHADOW = 19595,
|
||||
SPELL_ENRAGE = 8599,
|
||||
SPELL_EXPLODE = 25698,
|
||||
|
||||
SPELL_SUMMON_ANUB_SWARMGUARD = 17430,
|
||||
SPELL_SUMMON_ANUB_WARRIOR = 17431,
|
||||
};
|
||||
|
||||
struct npc_anubisath_guardian : public ScriptedAI
|
||||
{
|
||||
npc_anubisath_guardian(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
_spells[0] = RAND(SPELL_SHADOW_STORM, SPELL_THUNDER_CLAP);
|
||||
_spells[1] = RAND(SPELL_REFLECT_ARCANE_FIRE, SPELL_REFLECT_FROST_SHADOW);
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_enraged = false;
|
||||
|
||||
_scheduler.SetValidator([this]
|
||||
{
|
||||
return !me->HasUnitState(UNIT_STATE_CASTING);
|
||||
});
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
DoCastSelf(_spells[0]);
|
||||
DoCastSelf(_spells[1]);
|
||||
|
||||
_scheduler.CancelAll();
|
||||
|
||||
uint32 spell = RAND(SPELL_METEOR, SPELL_PLAGUE);
|
||||
_scheduler.Schedule(10s, [this, spell](TaskContext context) {
|
||||
DoCastRandomTarget(spell);
|
||||
context.Repeat(10s, 15s);
|
||||
});
|
||||
|
||||
spell = RAND(SPELL_SUMMON_ANUB_SWARMGUARD, SPELL_SUMMON_ANUB_WARRIOR);
|
||||
_scheduler.Schedule(10s, [this, spell](TaskContext context) {
|
||||
DoCastAOE(spell);
|
||||
context.Repeat(10s, 15s);
|
||||
});
|
||||
}
|
||||
|
||||
void DamageTaken(Unit* /*doneBy*/, uint32& damage, DamageEffectType /* damagetype */, SpellSchoolMask /*damageSchoolMask*/) override
|
||||
{
|
||||
if (!_enraged && me->HealthBelowPctDamaged(10, damage))
|
||||
{
|
||||
_enraged = true;
|
||||
DoCastSelf(RAND(SPELL_ENRAGE, SPELL_EXPLODE), true);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
_scheduler.Update(diff,
|
||||
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
|
||||
}
|
||||
|
||||
private:
|
||||
bool _enraged;
|
||||
uint32 _spells[2];
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
void AddSC_boss_ossirian()
|
||||
{
|
||||
new boss_ossirian();
|
||||
RegisterRuinsOfAhnQirajCreatureAI(boss_ossirian);
|
||||
new go_ossirian_crystal();
|
||||
RegisterCreatureAI(npc_anubisath_guardian);
|
||||
}
|
||||
|
||||
@@ -22,16 +22,6 @@
|
||||
|
||||
enum Yells
|
||||
{
|
||||
// The time of our retribution is at hand! Let darkness reign in the hearts of our enemies! Sound: 8645 Emote: 35
|
||||
SAY_ANDOROV_INTRO = 0, // Before for the first wave
|
||||
SAY_ANDOROV_ATTACK = 1, // Beginning the event
|
||||
|
||||
SAY_WAVE3 = 0,
|
||||
SAY_WAVE4 = 1,
|
||||
SAY_WAVE5 = 2,
|
||||
SAY_WAVE6 = 3,
|
||||
SAY_WAVE7 = 4,
|
||||
SAY_INTRO = 5,
|
||||
SAY_UNK1 = 6,
|
||||
SAY_UNK2 = 7,
|
||||
SAY_UNK3 = 8,
|
||||
@@ -63,27 +53,25 @@ public:
|
||||
|
||||
struct boss_rajaxxAI : public BossAI
|
||||
{
|
||||
boss_rajaxxAI(Creature* creature) : BossAI(creature, DATA_RAJAXX)
|
||||
{
|
||||
}
|
||||
boss_rajaxxAI(Creature* creature) : BossAI(creature, DATA_RAJAXX) { }
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
enraged = false;
|
||||
events.ScheduleEvent(EVENT_DISARM, 10000);
|
||||
events.ScheduleEvent(EVENT_THUNDERCRASH, 12000);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
//SAY_DEATH
|
||||
Talk(SAY_DEATH);
|
||||
_JustDied();
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*victim*/) override
|
||||
{
|
||||
_EnterCombat();
|
||||
events.ScheduleEvent(EVENT_DISARM, 10000);
|
||||
events.ScheduleEvent(EVENT_THUNDERCRASH, 12000);
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
|
||||
@@ -15,10 +15,49 @@
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "CreatureGroups.h"
|
||||
#include "InstanceScript.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "ruins_of_ahnqiraj.h"
|
||||
|
||||
ObjectData const creatureData[] =
|
||||
{
|
||||
{ NPC_BURU, DATA_BURU },
|
||||
{ NPC_KURINNAXX, DATA_KURINNAXX },
|
||||
{ NPC_RAJAXX, DATA_RAJAXX },
|
||||
{ NPC_OSSIRIAN, DATA_OSSIRIAN },
|
||||
{ NPC_QUUEZ, DATA_QUUEZ },
|
||||
{ NPC_TUUBID, DATA_TUUBID },
|
||||
{ NPC_DRENN, DATA_DRENN },
|
||||
{ NPC_XURREM, DATA_XURREM },
|
||||
{ NPC_YEGGETH, DATA_YEGGETH },
|
||||
{ NPC_PAKKON, DATA_PAKKON },
|
||||
{ NPC_ZERRAN, DATA_ZERRAN },
|
||||
};
|
||||
|
||||
enum RajaxxText
|
||||
{
|
||||
SAY_WAVE3 = 0,
|
||||
SAY_WAVE4 = 1,
|
||||
SAY_WAVE5 = 2,
|
||||
SAY_WAVE6 = 3,
|
||||
SAY_WAVE7 = 4,
|
||||
SAY_ENGAGE = 5
|
||||
};
|
||||
|
||||
std::array<uint32, 8> RajaxxWavesData[] =
|
||||
{
|
||||
{ DATA_QUUEZ, 0 },
|
||||
{ DATA_TUUBID, 0 },
|
||||
{ DATA_DRENN, SAY_WAVE3 },
|
||||
{ DATA_XURREM, SAY_WAVE4 },
|
||||
{ DATA_YEGGETH, SAY_WAVE5 },
|
||||
{ DATA_PAKKON, SAY_WAVE6 },
|
||||
{ DATA_ZERRAN, SAY_WAVE7 },
|
||||
{ DATA_RAJAXX, SAY_ENGAGE }
|
||||
};
|
||||
|
||||
class instance_ruins_of_ahnqiraj : public InstanceMapScript
|
||||
{
|
||||
public:
|
||||
@@ -29,14 +68,18 @@ public:
|
||||
instance_ruins_of_ahnqiraj_InstanceMapScript(Map* map) : InstanceScript(map)
|
||||
{
|
||||
SetBossNumber(NUM_ENCOUNTER);
|
||||
LoadObjectData(creatureData, nullptr);
|
||||
_rajaxWaveCounter = 0;
|
||||
}
|
||||
|
||||
void OnCreatureCreate(Creature* creature) override
|
||||
{
|
||||
InstanceScript::OnCreatureCreate(creature);
|
||||
|
||||
switch (creature->GetEntry())
|
||||
{
|
||||
case NPC_KURINAXX:
|
||||
_kurinaxxGUID = creature->GetGUID();
|
||||
case NPC_KURINNAXX:
|
||||
_kurinnaxxGUID = creature->GetGUID();
|
||||
break;
|
||||
case NPC_RAJAXX:
|
||||
_rajaxxGUID = creature->GetGUID();
|
||||
@@ -56,12 +99,73 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
bool SetBossState(uint32 bossId, EncounterState state) override
|
||||
void OnCreatureEvade(Creature* creature) override
|
||||
{
|
||||
if (!InstanceScript::SetBossState(bossId, state))
|
||||
return false;
|
||||
if (CreatureGroup* formation = creature->GetFormation())
|
||||
{
|
||||
if (Creature* leader = formation->GetLeader())
|
||||
{
|
||||
switch (leader->GetEntry())
|
||||
{
|
||||
case NPC_QUUEZ:
|
||||
case NPC_TUUBID:
|
||||
case NPC_DRENN:
|
||||
case NPC_XURREM:
|
||||
case NPC_YEGGETH:
|
||||
case NPC_PAKKON:
|
||||
case NPC_ZERRAN:
|
||||
if (!formation->IsFormationInCombat())
|
||||
{
|
||||
ResetRajaxxWaves();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (creature->GetEntry() == NPC_RAJAXX)
|
||||
{
|
||||
ResetRajaxxWaves();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
void OnUnitDeath(Unit* unit) override
|
||||
{
|
||||
if (Creature* creature = unit->ToCreature())
|
||||
{
|
||||
if (CreatureGroup* formation = creature->GetFormation())
|
||||
{
|
||||
if (Creature* leader = formation->GetLeader())
|
||||
{
|
||||
switch (leader->GetEntry())
|
||||
{
|
||||
case NPC_QUUEZ:
|
||||
case NPC_TUUBID:
|
||||
case NPC_DRENN:
|
||||
case NPC_XURREM:
|
||||
case NPC_YEGGETH:
|
||||
case NPC_PAKKON:
|
||||
case NPC_ZERRAN:
|
||||
_scheduler.CancelAll();
|
||||
_scheduler.Schedule(1s, [this, formation](TaskContext /*context*/) {
|
||||
if (!formation->IsAnyMemberAlive())
|
||||
{
|
||||
CallNextRajaxxLeader();
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Update(uint32 diff) override
|
||||
{
|
||||
_scheduler.Update(diff);
|
||||
}
|
||||
|
||||
void SetGuidData(uint32 type, ObjectGuid data) override
|
||||
@@ -75,7 +179,7 @@ public:
|
||||
switch (type)
|
||||
{
|
||||
case DATA_KURINNAXX:
|
||||
return _kurinaxxGUID;
|
||||
return _kurinnaxxGUID;
|
||||
case DATA_RAJAXX:
|
||||
return _rajaxxGUID;
|
||||
case DATA_MOAM:
|
||||
@@ -136,14 +240,56 @@ public:
|
||||
OUT_LOAD_INST_DATA_COMPLETE;
|
||||
}
|
||||
|
||||
void CallNextRajaxxLeader()
|
||||
{
|
||||
++_rajaxWaveCounter;
|
||||
|
||||
if (Creature* nextLeader = GetCreature(RajaxxWavesData[_rajaxWaveCounter].at(0)))
|
||||
{
|
||||
if (_rajaxWaveCounter >= 2)
|
||||
{
|
||||
if (Creature* rajaxx = GetCreature(DATA_RAJAXX))
|
||||
{
|
||||
rajaxx->AI()->Talk(RajaxxWavesData[_rajaxWaveCounter].at(1));
|
||||
}
|
||||
}
|
||||
|
||||
if (nextLeader->IsAlive())
|
||||
{
|
||||
nextLeader->SetInCombatWithZone();
|
||||
}
|
||||
else
|
||||
{
|
||||
CallNextRajaxxLeader();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResetRajaxxWaves()
|
||||
{
|
||||
_rajaxWaveCounter = 0;
|
||||
for (auto const& data : RajaxxWavesData)
|
||||
{
|
||||
if (Creature* creature = GetCreature(data.at(0)))
|
||||
{
|
||||
if (CreatureGroup* group = creature->GetFormation())
|
||||
{
|
||||
group->RespawnFormation(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ObjectGuid _kurinaxxGUID;
|
||||
ObjectGuid _kurinnaxxGUID;
|
||||
ObjectGuid _rajaxxGUID;
|
||||
ObjectGuid _moamGUID;
|
||||
ObjectGuid _buruGUID;
|
||||
ObjectGuid _ayamissGUID;
|
||||
ObjectGuid _ossirianGUID;
|
||||
ObjectGuid _paralyzedGUID;
|
||||
uint32 _rajaxWaveCounter;
|
||||
TaskScheduler _scheduler;
|
||||
};
|
||||
|
||||
InstanceScript* GetInstanceScript(InstanceMap* map) const override
|
||||
|
||||
@@ -32,12 +32,22 @@ enum DataTypes
|
||||
DATA_OSSIRIAN = 5,
|
||||
NUM_ENCOUNTER = 6,
|
||||
|
||||
DATA_PARALYZED = 7
|
||||
DATA_PARALYZED = 7,
|
||||
|
||||
DATA_QUUEZ = 8,
|
||||
DATA_TUUBID = 9,
|
||||
DATA_DRENN = 10,
|
||||
DATA_XURREM = 11,
|
||||
DATA_YEGGETH = 12,
|
||||
DATA_PAKKON = 13,
|
||||
DATA_ZERRAN = 14,
|
||||
|
||||
DATA_ENGAGED_FORMATION = 1
|
||||
};
|
||||
|
||||
enum Creatures
|
||||
{
|
||||
NPC_KURINAXX = 15348,
|
||||
NPC_KURINNAXX = 15348,
|
||||
NPC_RAJAXX = 15341,
|
||||
NPC_MOAM = 15340,
|
||||
NPC_BURU = 15370,
|
||||
@@ -49,9 +59,19 @@ enum Creatures
|
||||
NPC_SAND_VORTEX = 15428,
|
||||
NPC_OSSIRIAN_TRIGGER = 15590,
|
||||
NPC_HATCHLING = 15521,
|
||||
NPC_BURU_EGG = 15514,
|
||||
NPC_LARVA = 15555,
|
||||
NPC_SWARMER = 15546,
|
||||
NPC_HORNET = 15934
|
||||
NPC_HORNET = 15934,
|
||||
|
||||
// Rajaxx
|
||||
NPC_QUUEZ = 15391,
|
||||
NPC_TUUBID = 15392,
|
||||
NPC_DRENN = 15389,
|
||||
NPC_XURREM = 15390,
|
||||
NPC_YEGGETH = 15386,
|
||||
NPC_PAKKON = 15388,
|
||||
NPC_ZERRAN = 15385
|
||||
};
|
||||
|
||||
enum GameObjects
|
||||
@@ -65,4 +85,6 @@ inline AI* GetRuinsOfAhnQirajAI(T* obj)
|
||||
return GetInstanceAI<AI>(obj, RuinsOfAhnQirajScriptName);
|
||||
}
|
||||
|
||||
#define RegisterRuinsOfAhnQirajCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetRuinsOfAhnQirajAI)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -195,6 +195,18 @@ public:
|
||||
instance->SetData(DATA_CTHUN_PHASE, PHASE_EYE_GREEN_BEAM);
|
||||
}
|
||||
|
||||
void MoveInLineOfSight(Unit* who) override
|
||||
{
|
||||
if (who->GetTypeId() == TYPEID_PLAYER && !me->IsInCombat())
|
||||
{
|
||||
// Z checks are necessary here because AQ maps do funky stuff.
|
||||
if (me->IsWithinLOSInMap(who) && me->IsWithinDist2d(who, 50.0f) && who->GetPositionZ() > 100.0f)
|
||||
{
|
||||
AttackStart(who);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoAction(int32 action) override
|
||||
{
|
||||
if (action == ACTION_SPAWN_EYE_TENTACLES)
|
||||
@@ -367,6 +379,7 @@ public:
|
||||
|
||||
me->InterruptNonMeleeSpells(true);
|
||||
me->RemoveAllAuras();
|
||||
_scheduler.CancelAll();
|
||||
break;
|
||||
|
||||
case PHASE_CTHUN_DONE:
|
||||
@@ -819,13 +832,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void DoAction(int32 param) override
|
||||
void SummonedCreatureDies(Creature* creature, Unit* /*killer*/) override
|
||||
{
|
||||
switch (param)
|
||||
if (creature->GetEntry() == NPC_FLESH_TENTACLE)
|
||||
{
|
||||
case ACTION_FLESH_TENTACLE_KILLED:
|
||||
++FleshTentaclesKilled;
|
||||
break;
|
||||
++FleshTentaclesKilled;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1178,33 +1189,6 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
class npc_giant_flesh_tentacle : public CreatureScript
|
||||
{
|
||||
public:
|
||||
npc_giant_flesh_tentacle() : CreatureScript("npc_giant_flesh_tentacle") { }
|
||||
|
||||
CreatureAI* GetAI(Creature* creature) const override
|
||||
{
|
||||
return new flesh_tentacleAI(creature);
|
||||
}
|
||||
|
||||
struct flesh_tentacleAI : public ScriptedAI
|
||||
{
|
||||
flesh_tentacleAI(Creature* creature) : ScriptedAI(creature)
|
||||
{
|
||||
SetCombatMovement(false);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (TempSummon* summon = me->ToTempSummon())
|
||||
if (Unit* summoner = summon->GetSummonerUnit())
|
||||
if (summoner->IsAIEnabled)
|
||||
summoner->GetAI()->DoAction(ACTION_FLESH_TENTACLE_KILLED);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
//GetAIs
|
||||
|
||||
void AddSC_boss_cthun()
|
||||
@@ -1215,5 +1199,4 @@ void AddSC_boss_cthun()
|
||||
new npc_claw_tentacle();
|
||||
new npc_giant_claw_tentacle();
|
||||
new npc_giant_eye_tentacle();
|
||||
new npc_giant_flesh_tentacle();
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ EndScriptData */
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "TaskScheduler.h"
|
||||
#include "temple_of_ahnqiraj.h"
|
||||
|
||||
#define SOUND_SENTENCE_YOU 8588
|
||||
@@ -34,7 +35,7 @@ EndScriptData */
|
||||
|
||||
enum Spells
|
||||
{
|
||||
SPELL_MORTAL_WOUND = 28467,
|
||||
SPELL_MORTAL_WOUND = 25646,
|
||||
SPELL_ROOT = 28858,
|
||||
|
||||
// Enrage for his spawns
|
||||
@@ -55,28 +56,8 @@ public:
|
||||
{
|
||||
boss_fankrissAI(Creature* creature) : ScriptedAI(creature) { }
|
||||
|
||||
uint32 MortalWound_Timer;
|
||||
uint32 SpawnHatchlings_Timer;
|
||||
uint32 SpawnSpawns_Timer;
|
||||
int Rand;
|
||||
float RandX;
|
||||
float RandY;
|
||||
|
||||
Creature* Hatchling;
|
||||
Creature* Spawn;
|
||||
|
||||
void Reset() override
|
||||
void SummonSpawn()
|
||||
{
|
||||
MortalWound_Timer = urand(10000, 15000);
|
||||
SpawnHatchlings_Timer = urand(6000, 12000);
|
||||
SpawnSpawns_Timer = urand(15000, 45000);
|
||||
}
|
||||
|
||||
void SummonSpawn(Unit* victim)
|
||||
{
|
||||
if (!victim)
|
||||
return;
|
||||
|
||||
Rand = 10 + (rand() % 10);
|
||||
switch (rand() % 2)
|
||||
{
|
||||
@@ -99,13 +80,68 @@ public:
|
||||
break;
|
||||
}
|
||||
Rand = 0;
|
||||
Spawn = DoSpawnCreature(15630, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
|
||||
if (Spawn)
|
||||
Spawn->AI()->AttackStart(victim);
|
||||
DoSpawnCreature(15630, RandX, RandY, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
_scheduler.CancelAll();
|
||||
|
||||
_scheduler.Schedule(4s, 8s, [this](TaskContext context) {
|
||||
DoCastVictim(SPELL_MORTAL_WOUND);
|
||||
context.Repeat();
|
||||
}).Schedule(15s, 45s, [this](TaskContext context) {
|
||||
switch (urand(0, 2))
|
||||
{
|
||||
case 0:
|
||||
SummonSpawn();
|
||||
break;
|
||||
case 1:
|
||||
SummonSpawn();
|
||||
SummonSpawn();
|
||||
break;
|
||||
case 2:
|
||||
SummonSpawn();
|
||||
SummonSpawn();
|
||||
SummonSpawn();
|
||||
break;
|
||||
}
|
||||
context.Repeat(30s, 60s);
|
||||
}).Schedule(15s, 45s, [this](TaskContext context) {
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
|
||||
{
|
||||
DoCast(target, SPELL_ROOT);
|
||||
|
||||
if (DoGetThreat(target))
|
||||
DoModifyThreatPercent(target, -100);
|
||||
|
||||
switch (urand(0, 2))
|
||||
{
|
||||
case 0:
|
||||
DoTeleportPlayer(target, -8106.0142f, 1289.2900f, -74.419533f, 5.112f);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() - 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() + 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() - 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() + 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
break;
|
||||
case 1:
|
||||
DoTeleportPlayer(target, -7990.135354f, 1155.1907f, -78.849319f, 2.608f);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() - 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() + 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() - 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() + 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
break;
|
||||
case 2:
|
||||
DoTeleportPlayer(target, -8159.7753f, 1127.9064f, -76.868660f, 0.675f);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() - 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() + 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() - 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() + 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
break;
|
||||
}
|
||||
}
|
||||
context.Repeat(45s, 60s);
|
||||
});
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
@@ -114,105 +150,15 @@ public:
|
||||
if (!UpdateVictim())
|
||||
return;
|
||||
|
||||
//MortalWound_Timer
|
||||
if (MortalWound_Timer <= diff)
|
||||
{
|
||||
DoCastVictim(SPELL_MORTAL_WOUND);
|
||||
MortalWound_Timer = urand(10000, 20000);
|
||||
}
|
||||
else MortalWound_Timer -= diff;
|
||||
|
||||
//Summon 1-3 Spawns of Fankriss at random time.
|
||||
if (SpawnSpawns_Timer <= diff)
|
||||
{
|
||||
switch (urand(0, 2))
|
||||
{
|
||||
case 0:
|
||||
SummonSpawn(SelectTarget(SelectTargetMethod::Random, 0));
|
||||
break;
|
||||
case 1:
|
||||
SummonSpawn(SelectTarget(SelectTargetMethod::Random, 0));
|
||||
SummonSpawn(SelectTarget(SelectTargetMethod::Random, 0));
|
||||
break;
|
||||
case 2:
|
||||
SummonSpawn(SelectTarget(SelectTargetMethod::Random, 0));
|
||||
SummonSpawn(SelectTarget(SelectTargetMethod::Random, 0));
|
||||
SummonSpawn(SelectTarget(SelectTargetMethod::Random, 0));
|
||||
break;
|
||||
}
|
||||
SpawnSpawns_Timer = urand(30000, 60000);
|
||||
}
|
||||
else SpawnSpawns_Timer -= diff;
|
||||
|
||||
// Teleporting Random Target to one of the three tunnels and spawn 4 hatchlings near the gamer.
|
||||
//We will only telport if fankriss has more than 3% of hp so teleported gamers can always loot.
|
||||
if (HealthAbovePct(3))
|
||||
{
|
||||
if (SpawnHatchlings_Timer <= diff)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 0.0f, true))
|
||||
{
|
||||
DoCast(target, SPELL_ROOT);
|
||||
|
||||
if (DoGetThreat(target))
|
||||
DoModifyThreatPercent(target, -100);
|
||||
|
||||
switch (urand(0, 2))
|
||||
{
|
||||
case 0:
|
||||
DoTeleportPlayer(target, -8106.0142f, 1289.2900f, -74.419533f, 5.112f);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() - 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() + 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() - 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() + 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
break;
|
||||
case 1:
|
||||
DoTeleportPlayer(target, -7990.135354f, 1155.1907f, -78.849319f, 2.608f);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() - 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() + 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() - 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() + 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
break;
|
||||
case 2:
|
||||
DoTeleportPlayer(target, -8159.7753f, 1127.9064f, -76.868660f, 0.675f);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() - 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 3, target->GetPositionY() + 3, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() - 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
Hatchling = me->SummonCreature(15962, target->GetPositionX() - 5, target->GetPositionY() + 5, target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
|
||||
if (Hatchling)
|
||||
Hatchling->AI()->AttackStart(target);
|
||||
break;
|
||||
}
|
||||
}
|
||||
SpawnHatchlings_Timer = urand(45000, 60000);
|
||||
}
|
||||
else SpawnHatchlings_Timer -= diff;
|
||||
}
|
||||
|
||||
DoMeleeAttackIfReady();
|
||||
_scheduler.Update(diff,
|
||||
std::bind(&ScriptedAI::DoMeleeAttackIfReady, this));
|
||||
}
|
||||
|
||||
private:
|
||||
TaskScheduler _scheduler;
|
||||
int Rand;
|
||||
float RandX;
|
||||
float RandY;
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ EndScriptData */
|
||||
|
||||
#include "ScriptMgr.h"
|
||||
#include "ScriptedCreature.h"
|
||||
#include "SpellScript.h"
|
||||
#include "temple_of_ahnqiraj.h"
|
||||
|
||||
enum Huhuran
|
||||
@@ -36,7 +37,8 @@ enum Huhuran
|
||||
SPELL_POISONBOLT = 26052,
|
||||
SPELL_NOXIOUSPOISON = 26053,
|
||||
SPELL_WYVERNSTING = 26180,
|
||||
SPELL_ACIDSPIT = 26050
|
||||
SPELL_ACIDSPIT = 26050,
|
||||
SPELL_WYVERN_STING_DAMAGE = 26233
|
||||
};
|
||||
|
||||
class boss_huhuran : public CreatureScript
|
||||
@@ -76,10 +78,6 @@ public:
|
||||
Berserk = false;
|
||||
}
|
||||
|
||||
void EnterCombat(Unit* /*who*/) override
|
||||
{
|
||||
}
|
||||
|
||||
void UpdateAI(uint32 diff) override
|
||||
{
|
||||
//Return since we have no target
|
||||
@@ -100,8 +98,7 @@ public:
|
||||
// Wyvern Timer
|
||||
if (Wyvern_Timer <= diff)
|
||||
{
|
||||
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0))
|
||||
DoCast(target, SPELL_WYVERNSTING);
|
||||
DoCastAOE(SPELL_WYVERNSTING);
|
||||
Wyvern_Timer = urand(15000, 32000);
|
||||
}
|
||||
else Wyvern_Timer -= diff;
|
||||
@@ -155,7 +152,27 @@ public:
|
||||
};
|
||||
};
|
||||
|
||||
// 26180 - Wyvern Sting
|
||||
class spell_huhuran_wyvern_sting : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_huhuran_wyvern_sting);
|
||||
|
||||
void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
caster->CastCustomSpell(SPELL_WYVERN_STING_DAMAGE, SPELLVALUE_BASE_POINT0, 3000, GetUnitOwner(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_huhuran_wyvern_sting::HandleRemove, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_boss_huhuran()
|
||||
{
|
||||
new boss_huhuran();
|
||||
RegisterSpellScript(spell_huhuran_wyvern_sting);
|
||||
}
|
||||
|
||||
@@ -58,6 +58,7 @@ public:
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
_Reset();
|
||||
_flag = 0;
|
||||
_hpct = 75.0f;
|
||||
me->SetVisible(true);
|
||||
@@ -96,9 +97,6 @@ public:
|
||||
if (_flag & (1 << 7))
|
||||
_flag = 0;
|
||||
|
||||
if (Unit* Target = SelectTarget(SelectTargetMethod::Random))
|
||||
creature->AI()->AttackStart(Target);
|
||||
|
||||
float ImageHealthPct;
|
||||
|
||||
if (me->GetHealthPct() < 25.0f)
|
||||
@@ -110,12 +108,16 @@ public:
|
||||
|
||||
creature->SetMaxHealth(me->GetMaxHealth() * ImageHealthPct);
|
||||
creature->SetHealth(creature->GetMaxHealth() * (me->GetHealthPct() / 100.0f));
|
||||
BossAI::JustSummoned(creature);
|
||||
}
|
||||
|
||||
void JustDied(Unit* /*killer*/) override
|
||||
{
|
||||
if (!me->IsSummon())
|
||||
{
|
||||
_JustDied();
|
||||
Talk(SAY_DEATH);
|
||||
}
|
||||
else
|
||||
me->RemoveCorpse();
|
||||
}
|
||||
@@ -169,7 +171,7 @@ public:
|
||||
|
||||
if (!me->IsSummon() && me->GetHealthPct() < _hpct)
|
||||
{
|
||||
DoCast(me, SPELL_SUMMON_IMAGES);
|
||||
DoCast(me, SPELL_SUMMON_IMAGES, true);
|
||||
Talk(SAY_SPLIT);
|
||||
_hpct -= 25.0f;
|
||||
me->SetVisible(false);
|
||||
|
||||
@@ -4472,6 +4472,104 @@ class spell_gen_remove_impairing_auras : public SpellScript
|
||||
}
|
||||
};
|
||||
|
||||
enum AQSpells
|
||||
{
|
||||
SPELL_CONSUME_LEECH_AQ20 = 25373,
|
||||
SPELL_CONSUME_LEECH_HEAL_AQ20 = 25378,
|
||||
SPELL_CONSUME_SPIT_OUT = 25383,
|
||||
|
||||
SPELL_HIVEZARA_CATALYST = 25187
|
||||
};
|
||||
|
||||
class spell_gen_consume : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_gen_consume);
|
||||
|
||||
public:
|
||||
spell_gen_consume(uint32 spellId1, uint32 spellId2) : AuraScript(), _spellId1(spellId1), _spellId2(spellId2) { }
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ _spellId1, _spellId2 });
|
||||
}
|
||||
|
||||
void HandleProc(AuraEffect* /*aurEff*/)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
if (!caster->IsAlive())
|
||||
{
|
||||
GetUnitOwner()->RemoveAurasDueToSpell(GetSpellInfo()->Id);
|
||||
return;
|
||||
}
|
||||
|
||||
caster->CastSpell(GetUnitOwner(), _spellId1, true);
|
||||
}
|
||||
}
|
||||
|
||||
void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (GetTargetApplication())
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH)
|
||||
{
|
||||
caster->CastSpell(caster, _spellId2, true);
|
||||
}
|
||||
else if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
|
||||
{
|
||||
caster->CastSpell(GetTarget(), SPELL_CONSUME_SPIT_OUT, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_gen_consume::AfterRemove, EFFECT_1, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL);
|
||||
OnEffectUpdatePeriodic += AuraEffectUpdatePeriodicFn(spell_gen_consume::HandleProc, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 _spellId1;
|
||||
uint32 _spellId2;
|
||||
};
|
||||
|
||||
class spell_gen_apply_aura_after_expiration : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_gen_apply_aura_after_expiration);
|
||||
|
||||
public:
|
||||
spell_gen_apply_aura_after_expiration(uint32 spellId, uint32 effect, uint32 aura) : AuraScript(), _spellId(spellId), _effect(effect), _aura(aura) { }
|
||||
|
||||
bool Validate(SpellInfo const* /*spellInfo*/) override
|
||||
{
|
||||
return ValidateSpellInfo({ _spellId });
|
||||
}
|
||||
|
||||
void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (GetTargetApplication() && GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE)
|
||||
{
|
||||
if (Unit* caster = GetCaster())
|
||||
{
|
||||
caster->CastSpell(GetTarget(), _spellId, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
AfterEffectRemove += AuraEffectRemoveFn(spell_gen_apply_aura_after_expiration::AfterRemove, _effect, _aura, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
|
||||
private:
|
||||
uint32 _spellId;
|
||||
uint32 _effect;
|
||||
uint32 _aura;
|
||||
};
|
||||
|
||||
void AddSC_generic_spell_scripts()
|
||||
{
|
||||
RegisterSpellScript(spell_silithyst);
|
||||
@@ -4569,6 +4667,7 @@ void AddSC_generic_spell_scripts()
|
||||
RegisterSpellScript(spell_gen_teleporting);
|
||||
RegisterSpellScript(spell_gen_ds_flush_knockback);
|
||||
RegisterSpellScriptWithArgs(spell_gen_count_pct_from_max_hp, "spell_gen_default_count_pct_from_max_hp");
|
||||
RegisterSpellScriptWithArgs(spell_gen_count_pct_from_max_hp, "spell_gen_10pct_count_pct_from_max_hp", 10);
|
||||
RegisterSpellScriptWithArgs(spell_gen_count_pct_from_max_hp, "spell_gen_50pct_count_pct_from_max_hp", 50);
|
||||
RegisterSpellScriptWithArgs(spell_gen_count_pct_from_max_hp, "spell_gen_100pct_count_pct_from_max_hp", 100);
|
||||
RegisterSpellScript(spell_gen_despawn_self);
|
||||
@@ -4605,4 +4704,6 @@ void AddSC_generic_spell_scripts()
|
||||
RegisterSpellScript(spell_gen_holiday_buff_food);
|
||||
RegisterSpellScript(spell_gen_arcane_charge);
|
||||
RegisterSpellScript(spell_gen_remove_impairing_auras);
|
||||
RegisterSpellScriptWithArgs(spell_gen_consume, "spell_consume_aq20", SPELL_CONSUME_LEECH_AQ20, SPELL_CONSUME_LEECH_HEAL_AQ20);
|
||||
RegisterSpellScriptWithArgs(spell_gen_apply_aura_after_expiration, "spell_itch_aq20", SPELL_HIVEZARA_CATALYST, EFFECT_0, SPELL_AURA_DUMMY);
|
||||
}
|
||||
|
||||
@@ -842,22 +842,17 @@ class spell_pri_vampiric_touch : public AuraScript
|
||||
bool CheckProc(ProcEventInfo& eventInfo)
|
||||
{
|
||||
if (!eventInfo.GetActionTarget() || GetOwner()->GetGUID() != eventInfo.GetActionTarget()->GetGUID())
|
||||
return false;
|
||||
|
||||
if (eventInfo.GetTypeMask() & PROC_FLAG_KILLED)
|
||||
{
|
||||
if (SpellInfo const* spellInfo = eventInfo.GetSpellInfo())
|
||||
{
|
||||
if (spellInfo->SpellFamilyName == SPELLFAMILY_PRIEST && (spellInfo->SpellFamilyFlags[0] & 0x00002000))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return eventInfo.GetActionTarget()->IsAlive();
|
||||
SpellInfo const* spellInfo = eventInfo.GetSpellInfo();
|
||||
if (!spellInfo || spellInfo->SpellFamilyName != SPELLFAMILY_PRIEST || !(spellInfo->SpellFamilyFlags[0] & 0x00002000))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
* Scriptnames of files in this file should be prefixed with "spell_warl_".
|
||||
*/
|
||||
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "ScriptMgr.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
@@ -1229,6 +1230,45 @@ class spell_warl_shadowburn : public AuraScript
|
||||
}
|
||||
};
|
||||
|
||||
class spell_warl_glyph_of_felguard : public AuraScript
|
||||
{
|
||||
PrepareAuraScript(spell_warl_glyph_of_felguard);
|
||||
|
||||
void HandleApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (Player* player = GetCaster()->ToPlayer())
|
||||
{
|
||||
if (Pet* pet = player->GetPet())
|
||||
{
|
||||
if (pet->GetEntry() == NPC_FELGUARD)
|
||||
{
|
||||
pet->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, aurEff->GetAmount(), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HandleRemove(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/)
|
||||
{
|
||||
if (Player* player = GetCaster()->ToPlayer())
|
||||
{
|
||||
if (Pet* pet = player->GetPet())
|
||||
{
|
||||
if (pet->GetEntry() == NPC_FELGUARD)
|
||||
{
|
||||
pet->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, aurEff->GetAmount(), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Register() override
|
||||
{
|
||||
OnEffectApply += AuraEffectApplyFn(spell_warl_glyph_of_felguard::HandleApply, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
OnEffectRemove += AuraEffectRemoveFn(spell_warl_glyph_of_felguard::HandleRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
|
||||
}
|
||||
};
|
||||
|
||||
void AddSC_warlock_spell_scripts()
|
||||
{
|
||||
RegisterSpellScript(spell_warl_eye_of_kilrogg);
|
||||
@@ -1260,4 +1300,5 @@ void AddSC_warlock_spell_scripts()
|
||||
RegisterSpellScript(spell_warl_unstable_affliction);
|
||||
RegisterSpellScript(spell_warl_drain_soul);
|
||||
RegisterSpellScript(spell_warl_shadowburn);
|
||||
RegisterSpellScript(spell_warl_glyph_of_felguard);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user