Merge branch 'azerothcore:master' into Playerbot

This commit is contained in:
ZhengPeiRu21
2022-07-21 09:06:44 -06:00
committed by GitHub
79 changed files with 3887 additions and 3815 deletions
+10 -15
View File
@@ -19,33 +19,29 @@
#define ACORE_FACTORY_HOLDER
#include "Define.h"
#include "Dynamic/TypeList.h"
#include "ObjectRegistry.h"
#include "TypeList.h"
/*
* FactoryHolder holds a factory object of a specific type
/** FactoryHolder holds a factory object of a specific type
*/
template<class T, class Key = std::string>
template<class T, class O, class Key = std::string>
class FactoryHolder
{
public:
typedef ObjectRegistry<FactoryHolder<T, Key >, Key > FactoryHolderRegistry;
typedef ObjectRegistry<FactoryHolder<T, O, Key>, Key> FactoryHolderRegistry;
FactoryHolder(Key k) : i_key(k) { }
explicit FactoryHolder(Key const& k) : _key(k) { }
virtual ~FactoryHolder() { }
inline Key key() const { return i_key; }
void RegisterSelf(void) { FactoryHolderRegistry::instance()->InsertItem(this, i_key); }
void DeregisterSelf(void) { FactoryHolderRegistry::instance()->RemoveItem(this, false); }
void RegisterSelf() { FactoryHolderRegistry::instance()->InsertItem(this, _key); }
/// Abstract Factory create method
virtual T* Create(void* data = nullptr) const = 0;
virtual T* Create(O* object = nullptr) const = 0;
private:
Key i_key;
Key const _key;
};
/*
* Permissible is a classic way of letting the object decide
/** Permissible is a classic way of letting the object decide
* whether how good they handle things. This is not retricted
* to factory selectors.
*/
@@ -54,7 +50,6 @@ class Permissible
{
public:
virtual ~Permissible() { }
virtual int Permit(const T*) const = 0;
virtual int32 Permit(T const*) const = 0;
};
#endif
+22 -51
View File
@@ -23,20 +23,23 @@
#include <string>
#include <unordered_map>
#include <vector>
#include <memory>
/** ObjectRegistry holds all registry item of the same type
*/
template<class T, class Key = std::string>
class ObjectRegistry
class ObjectRegistry final
{
public:
typedef std::map<Key, T*> RegistryMapType;
typedef std::map<Key, std::unique_ptr<T>> RegistryMapType;
/// Returns a registry item
const T* GetRegistryItem(Key key) const
T const* GetRegistryItem(Key const& key) const
{
typename RegistryMapType::const_iterator iter = i_registeredObjects.find(key);
return ( iter == i_registeredObjects.end() ? nullptr : iter->second );
auto itr = _registeredObjects.find(key);
if (itr == _registeredObjects.end())
return nullptr;
return itr->second.get();
}
static ObjectRegistry<T, Key>* instance()
@@ -46,72 +49,40 @@ public:
}
/// Inserts a registry item
bool InsertItem(T* obj, Key key, bool override = false)
bool InsertItem(T* obj, Key const& key, bool force = false)
{
typename RegistryMapType::iterator iter = i_registeredObjects.find(key);
if ( iter != i_registeredObjects.end() )
auto itr = _registeredObjects.find(key);
if (itr != _registeredObjects.end())
{
if ( !override )
if (!force)
{
return false;
}
delete iter->second;
i_registeredObjects.erase(iter);
_registeredObjects.erase(itr);
}
i_registeredObjects[key] = obj;
_registeredObjects.emplace(std::piecewise_construct, std::forward_as_tuple(key), std::forward_as_tuple(obj));
return true;
}
/// Removes a registry item
void RemoveItem(Key key, bool delete_object = true)
{
typename RegistryMapType::iterator iter = i_registeredObjects.find(key);
if ( iter != i_registeredObjects.end() )
{
if ( delete_object )
{
delete iter->second;
}
i_registeredObjects.erase(iter);
}
}
/// Returns true if registry contains an item
bool HasItem(Key key) const
bool HasItem(Key const& key) const
{
return (i_registeredObjects.find(key) != i_registeredObjects.end());
}
/// Inefficiently return a vector of registered items
unsigned int GetRegisteredItems(std::vector<Key>& l) const
{
unsigned int sz = l.size();
l.resize(sz + i_registeredObjects.size());
for (typename RegistryMapType::const_iterator iter = i_registeredObjects.begin(); iter != i_registeredObjects.end(); ++iter)
{
l[sz++] = iter->first;
}
return i_registeredObjects.size();
return (_registeredObjects.count(key) > 0);
}
/// Return the map of registered items
RegistryMapType const& GetRegisteredItems() const
{
return i_registeredObjects;
return _registeredObjects;
}
ObjectRegistry() { }
~ObjectRegistry()
{
for (typename RegistryMapType::iterator iter = i_registeredObjects.begin(); iter != i_registeredObjects.end(); ++iter)
{
delete iter->second;
}
i_registeredObjects.clear();
}
private:
RegistryMapType i_registeredObjects;
RegistryMapType _registeredObjects;
// non instanceable, only static
ObjectRegistry() { }
~ObjectRegistry() { }
};
#endif
+2 -2
View File
@@ -231,7 +231,7 @@ private:
struct TimeTrackerSmall
{
public:
TimeTrackerSmall(uint32 expiry = 0)
TimeTrackerSmall(int32 expiry = 0)
: i_expiryTime(expiry)
{
}
@@ -246,7 +246,7 @@ public:
return i_expiryTime <= 0;
}
void Reset(uint32 interval)
void Reset(int32 interval)
{
i_expiryTime = interval;
}
+10 -2
View File
@@ -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;
}
+5 -5
View File
@@ -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();
+8 -5
View File
@@ -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;
}
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+27
View File
@@ -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;
}
+6 -3
View File
@@ -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
+7 -3
View File
@@ -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;
}
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -222,7 +222,7 @@ private:
bool m_MoveInLineOfSight_locked;
};
enum Permitions
enum Permitions : int32
{
PERMIT_BASE_NO = -1,
PERMIT_BASE_IDLE = 1,
+15 -43
View File
@@ -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
+6 -3
View File
@@ -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();
}
}
+71 -106
View File
@@ -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);
}
}
+4 -3
View File
@@ -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
+51
View File
@@ -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
+9 -24
View File
@@ -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);
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))
{
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);
+2 -2
View File
@@ -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
+21 -31
View File
@@ -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(GetBaseObject(), targets));
}
bool IsSmart(Creature* c = nullptr)
@@ -122,11 +112,11 @@ public:
return smart;
}
ObjectList* GetTargetList(uint32 id)
ObjectVector const* GetStoredTargetVector(uint32 id) 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();
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();
@@ -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,43 @@ 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)
ObjectGuidVector(WorldObject* baseObject, ObjectVector const& objectVector) : _baseObject(baseObject), _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() 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();
return &_objectVector;
}
bool Equals(ObjectList* objectList)
{
return m_objectList == objectList;
}
~ObjectGuidVector() { }
~ObjectGuidList()
private:
WorldObject* const _baseObject;
mutable ObjectVector _objectVector;
GuidVector _guidVector;
//sanitize vector using _guidVector
void UpdateObjects() const
{
delete m_objectList;
delete m_guidList;
_objectVector.clear();
for (ObjectGuid const& guid : _guidVector)
if (WorldObject* obj = ObjectAccessor::GetWorldObject(*_baseObject, guid))
_objectVector.push_back(obj);
}
};
typedef std::unordered_map<uint32, ObjectGuidList*> ObjectListMap;
typedef std::unordered_map<uint32, ObjectGuidVector> ObjectVectorMap;
class SmartWaypointMgr
{
@@ -1020,7 +1020,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();
@@ -2861,7 +2861,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;
}
+1 -1
View File
@@ -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;
@@ -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); }
@@ -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
@@ -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;
}
}
+17
View File
@@ -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);
@@ -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)
{
+9 -13
View File
@@ -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;
}
+17 -8
View File
@@ -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);
}
+3 -3
View File
@@ -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", " ");
+31
View File
@@ -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;
}
@@ -26,7 +26,6 @@ enum Spells
SPELL_SANDTRAP = 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
};
@@ -34,13 +33,12 @@ enum Events
{
EVENT_MORTAL_WOUND = 1,
EVENT_SANDTRAP = 2,
EVENT_TRASH = 3,
EVENT_WIDE_SLASH = 4
EVENT_WIDE_SLASH = 3
};
enum Texts
{
SAY_KURINAXX_DEATH = 5, // Yelled by Ossirian the Unscarred
SAY_KURINAXX_DEATH = 5 // Yell by 'Ossirian the Unscarred'
};
class boss_kurinnaxx : public CreatureScript
@@ -50,25 +48,22 @@ public:
struct boss_kurinnaxxAI : public BossAI
{
boss_kurinnaxxAI(Creature* creature) : BossAI(creature, DATA_KURINNAXX)
{
}
boss_kurinnaxxAI(Creature* creature) : BossAI(creature, DATA_KURINNAXX) {}
void Reset() override
{
_Reset();
_enraged = false;
events.ScheduleEvent(EVENT_MORTAL_WOUND, 8000);
events.ScheduleEvent(EVENT_MORTAL_WOUND, urand(8000, 10000));
events.ScheduleEvent(EVENT_SANDTRAP, urand(5000, 15000));
events.ScheduleEvent(EVENT_TRASH, 1000);
events.ScheduleEvent(EVENT_WIDE_SLASH, 11000);
events.ScheduleEvent(EVENT_WIDE_SLASH, urand(10000, 15000));
}
void DamageTaken(Unit*, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override
{
if (!_enraged && HealthBelowPct(30))
{
DoCast(me, SPELL_ENRAGE);
DoCastSelf(SPELL_ENRAGE);
_enraged = true;
}
}
@@ -77,7 +72,9 @@ public:
{
_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
@@ -96,28 +93,23 @@ public:
{
case EVENT_MORTAL_WOUND:
DoCastVictim(SPELL_MORTALWOUND);
events.ScheduleEvent(EVENT_MORTAL_WOUND, 8000);
events.ScheduleEvent(EVENT_MORTAL_WOUND, urand(8000, 10000));
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);
DoCastSelf(SPELL_WIDE_SLASH);
events.ScheduleEvent(EVENT_WIDE_SLASH, urand(12000, 15000));
break;
default:
break;
}
}
DoMeleeAttackIfReady();
}
private:
@@ -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)
@@ -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,55 @@ class spell_gen_remove_impairing_auras : public SpellScript
}
};
enum AQSpells
{
SPELL_CONSUME_LEECH_AQ20 = 25373,
SPELL_CONSUME_LEECH_HEAL_AQ20 = 25378
};
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())
{
caster->CastSpell(GetUnitOwner(), _spellId1, true);
}
}
void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
// Final heal only on duration end
if (GetTargetApplication() && GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_DEATH)
{
if (Unit* caster = GetCaster())
{
caster->CastSpell(caster, _spellId2, 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;
};
void AddSC_generic_spell_scripts()
{
RegisterSpellScript(spell_silithyst);
@@ -4569,6 +4618,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 +4655,5 @@ 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);
}
+4
View File
@@ -16,10 +16,14 @@
*/
#include "MapBuilder.h"
#include "IntermediateValues.h"
#include "MapDefines.h"
#include "MapTree.h"
#include "ModelInstance.h"
#include "PathCommon.h"
#include "StringFormat.h"
#include "VMapFactory.h"
#include "VMapMgr2.h"
#include <DetourCommon.h>
#include <DetourNavMesh.h>
#include <DetourNavMeshBuilder.h>