/*
* This file is part of the TrinityCore 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 General Public License as published by the
* Free Software Foundation; either version 2 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 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 .
*/
#include "AIException.h"
#include "AreaTrigger.h"
#include "Creature.h"
#include "CreatureAISelector.h"
#include "CreatureAIFactory.h"
#include "Log.h"
#include "MovementGenerator.h"
#include "GameObject.h"
#include "GameObjectAIFactory.h"
#include "AreaTriggerAI.h"
#include "Conversation.h"
#include "ConversationAI.h"
#include "ScriptMgr.h"
namespace FactorySelector
{
template
inline int32 GetPermitFor(T const* obj, Value const& value)
{
Permissible const* const p = ASSERT_NOTNULL(dynamic_cast const*>(value.second.get()));
return p->Permit(obj);
}
template
struct PermissibleOrderPred
{
public:
PermissibleOrderPred(T const* obj) : _obj(obj) { }
template
bool operator()(Value const& left, Value const& right) const
{
return GetPermitFor(_obj, left) < GetPermitFor(_obj, right);
}
private:
T const* const _obj;
};
template
inline FactoryHolder const* SelectFactory(T const* obj)
{
static_assert(std::is_same::value || std::is_same::value, "Invalid template parameter");
static_assert(std::is_same::value == std::is_same::value, "Incompatible AI for type");
static_assert(std::is_same::value == std::is_same::value, "Incompatible AI for type");
using AIRegistry = typename FactoryHolder::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(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
try
{
if (CreatureAI* scriptedAI = sScriptMgr->GetCreatureAI(creature))
return scriptedAI;
}
catch (InvalidAIException const& e)
{
TC_LOG_ERROR("entities.unit", "Exception trying to assign script '{}' to Creature (Entry: {}), this Creature will have a default AI. Exception message: {}",
creature->GetScriptName(), creature->GetEntry(), e.what());
}
return SelectFactory(creature)->Create(creature);
}
uint32 GetSelectedAIId(Creature const* creature)
{
if (creature->IsPet())
{
auto const* registry = ASSERT_NOTNULL(sCreatureAIRegistry->GetRegistryItem("PetAI"));
auto const* factory = dynamic_cast const*>(registry);
ASSERT(factory);
return factory->GetScriptId();
}
if (uint32 id = creature->GetScriptId())
{
if (sScriptMgr->CanCreateCreatureAI(id))
{
return id;
}
}
auto const* factory = dynamic_cast const*>(SelectFactory(creature));
ASSERT(factory);
return factory->GetScriptId();
}
MovementGenerator* SelectMovementGenerator(Unit* unit)
{
MovementGeneratorType type = unit->GetDefaultMovementType();
if (Creature* creature = unit->ToCreature())
if (!creature->GetPlayerMovingMe())
type = creature->GetDefaultMovementType();
MovementGeneratorCreator const* mv_factory = sMovementGeneratorRegistry->GetRegistryItem(type);
return ASSERT_NOTNULL(mv_factory)->Create(unit);
}
GameObjectAI* SelectGameObjectAI(GameObject* go)
{
// scriptname in db
if (GameObjectAI* scriptedAI = sScriptMgr->GetGameObjectAI(go))
return scriptedAI;
return SelectFactory(go)->Create(go);
}
uint32 GetSelectedAIId(GameObject const* go)
{
if (uint32 id = go->GetScriptId())
{
if (sScriptMgr->CanCreateGameObjectAI(id))
{
return id;
}
}
auto const* factory = dynamic_cast const*>(SelectFactory(go));
ASSERT(factory);
return factory->GetScriptId();
}
static uint32 GetNullAreaTriggerAIScriptId()
{
return sObjectMgr->GetScriptId("NullAreaTriggerAI", false);
}
AreaTriggerAI* SelectAreaTriggerAI(AreaTrigger* at)
{
if (AreaTriggerAI* ai = sScriptMgr->GetAreaTriggerAI(at))
return ai;
else
return new NullAreaTriggerAI(at, GetNullAreaTriggerAIScriptId());
}
uint32 GetSelectedAIId(AreaTrigger const* at)
{
if (uint32 id = at->GetScriptId())
{
if (sScriptMgr->CanCreateAreaTriggerAI(id))
{
return id;
}
}
return GetNullAreaTriggerAIScriptId();
}
static uint32 GetNullConversationAIScriptId()
{
return sObjectMgr->GetScriptId("NullConversationAI", false);
}
ConversationAI* SelectConversationAI(Conversation* conversation)
{
if (ConversationAI* ai = sScriptMgr->GetConversationAI(conversation))
return ai;
return new NullConversationAI(conversation, GetNullConversationAIScriptId());
}
uint32 GetSelectedAIId(Conversation const* conversation)
{
if (uint32 id = conversation->GetScriptId())
{
if (sScriptMgr->CanCreateConversationAI(id))
return id;
}
return GetNullConversationAIScriptId();
}
}