/* * 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(); } }