/* EQEmu: EQEmulator Copyright (C) 2001-2026 EQEmu Development Team 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 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 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 "quest_parser_collection.h" #include "common/features.h" #include "common/file.h" #include "common/misc_functions.h" #include "common/path_manager.h" #include "common/repositories/perl_event_export_settings_repository.h" #include "zone/quest_interface.h" #include "zone/questmgr.h" #include "zone/zone.h" #include // an encounter can register events before the object is loaded // examples // eq.register_npc_event(Event.death_complete, -1, AllDeath); // eq.register_npc_event(Event.say, -1, All_Say); const std::string ENCOUNTER_NO_ENTITY_ID = "-1"; extern Zone* zone; extern void MapOpcodes(); QuestParserCollection::QuestParserCollection() { _player_quest_status = QuestUnloaded; _global_player_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded; _merc_quest_status = QuestUnloaded; _global_merc_quest_status = QuestUnloaded; _zone_quest_status = QuestUnloaded; _global_zone_quest_status = QuestUnloaded; } QuestParserCollection::~QuestParserCollection() { } void QuestParserCollection::RegisterQuestInterface(QuestInterface* qi, std::string ext) { _interfaces[qi->GetIdentifier()] = qi; _extensions[qi->GetIdentifier()] = ext; _load_precedence.push_back(qi); } void QuestParserCollection::ClearInterfaces() { _interfaces.clear(); _extensions.clear(); _load_precedence.clear(); } void QuestParserCollection::AddVar(std::string name, std::string val) { for (const auto& e: _load_precedence) { e->AddVar(name, val); } } void QuestParserCollection::Init() { for (const auto& e: _load_precedence) { e->Init(); } } void QuestParserCollection::ReloadQuests(bool reset_timers) { if (reset_timers) { quest_manager.ClearAllTimers(); zone->StopAllTimers(); } MapOpcodes(); _npc_quest_status.clear(); _player_quest_status = QuestUnloaded; _global_player_quest_status = QuestUnloaded; _global_npc_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded; _merc_quest_status = QuestUnloaded; _global_merc_quest_status = QuestUnloaded; _zone_quest_status = QuestUnloaded; _global_zone_quest_status = QuestUnloaded; _spell_quest_status.clear(); _item_quest_status.clear(); _encounter_quest_status.clear(); for (const auto& e: _load_precedence) { e->ReloadQuests(); } } void QuestParserCollection::RemoveEncounter(const std::string& name) { for (const auto& e: _load_precedence) { e->RemoveEncounter(name); } } bool QuestParserCollection::HasQuestSub(uint32 npc_id, QuestEventID event_id) { return ( HasQuestSubLocal(npc_id, event_id) || HasQuestSubGlobal(event_id) || NPCHasEncounterSub(npc_id, event_id) ); } bool QuestParserCollection::NPCHasEncounterSub(uint32 npc_id, QuestEventID event_id) { return HasEncounterSub(event_id, fmt::format("npc_{}", npc_id)) || HasEncounterSub(event_id, "npc_" + ENCOUNTER_NO_ENTITY_ID); } bool QuestParserCollection::HasQuestSubLocal(uint32 npc_id, QuestEventID event_id) { auto iter = _npc_quest_status.find(npc_id); if (iter != _npc_quest_status.end()) { if (iter->second != QuestFailedToLoad) { //loaded or failed to load auto qiter = _interfaces.find(iter->second); if (qiter->second->HasQuestSub(npc_id, event_id)) { return true; } } } else { std::string filename; auto qi = GetQIByNPCQuest(npc_id, filename); if (qi) { _npc_quest_status[npc_id] = qi->GetIdentifier(); qi->LoadNPCScript(filename, npc_id); if (qi->HasQuestSub(npc_id, event_id)) { return true; } } else { _npc_quest_status[npc_id] = QuestFailedToLoad; } } return false; } bool QuestParserCollection::HasQuestSubGlobal(QuestEventID event_id) { if (_global_npc_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalNPCQuest(filename); if (qi) { qi->LoadGlobalNPCScript(filename); _global_npc_quest_status = qi->GetIdentifier(); if (qi->HasGlobalQuestSub(event_id)) { return true; } } } else { if (_global_npc_quest_status != QuestFailedToLoad) { auto qiter = _interfaces.find(_global_npc_quest_status); if (qiter->second->HasGlobalQuestSub(event_id)) { return true; } } } return false; } bool QuestParserCollection::PlayerHasQuestSub(QuestEventID event_id) { return ( PlayerHasQuestSubLocal(event_id) || PlayerHasQuestSubGlobal(event_id) || PlayerHasEncounterSub(event_id) ); } bool QuestParserCollection::PlayerHasEncounterSub(QuestEventID event_id) { return HasEncounterSub(event_id, "player"); } bool QuestParserCollection::PlayerHasQuestSubLocal(QuestEventID event_id) { if (_player_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByPlayerQuest(filename); if (qi) { _player_quest_status = qi->GetIdentifier(); qi->LoadPlayerScript(filename); return qi->PlayerHasQuestSub(event_id); } } else if (_player_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_player_quest_status); return iter->second->PlayerHasQuestSub(event_id); } return false; } bool QuestParserCollection::PlayerHasQuestSubGlobal(QuestEventID event_id) { if (_global_player_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalPlayerQuest(filename); if (qi) { _global_player_quest_status = qi->GetIdentifier(); qi->LoadGlobalPlayerScript(filename); return qi->GlobalPlayerHasQuestSub(event_id); } } else if (_global_player_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_player_quest_status); return iter->second->GlobalPlayerHasQuestSub(event_id); } return false; } bool QuestParserCollection::SpellHasEncounterSub(uint32 spell_id, QuestEventID event_id) { return HasEncounterSub(event_id, fmt::format("spell_{}", spell_id)) || HasEncounterSub(event_id, "spell_" + ENCOUNTER_NO_ENTITY_ID); } bool QuestParserCollection::SpellHasQuestSub(uint32 spell_id, QuestEventID event_id) { if (SpellHasEncounterSub(spell_id, event_id)) { return true; } auto iter = _spell_quest_status.find(spell_id); if (iter != _spell_quest_status.end()) { //loaded or failed to load if (iter->second != QuestFailedToLoad) { auto qiter = _interfaces.find(iter->second); return qiter->second->SpellHasQuestSub(spell_id, event_id); } } else if (_spell_quest_status[spell_id] != QuestFailedToLoad) { std::string filename; auto qi = GetQIBySpellQuest(spell_id, filename); if (qi) { _spell_quest_status[spell_id] = qi->GetIdentifier(); qi->LoadSpellScript(filename, spell_id); return qi->SpellHasQuestSub(spell_id, event_id); } else { _spell_quest_status[spell_id] = QuestFailedToLoad; } } return false; } bool QuestParserCollection::ItemHasEncounterSub(EQ::ItemInstance *inst, QuestEventID event_id) { if (inst) { return HasEncounterSub(event_id, fmt::format("item_{}", inst->GetID())) || HasEncounterSub(event_id, "item_" + ENCOUNTER_NO_ENTITY_ID); } return false; } bool QuestParserCollection::ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id) { if (!inst) { return false; } if (ItemHasEncounterSub(inst, event_id)) { return true; } std::string item_script; if (inst->GetItem()->ScriptFileID != 0) { item_script = fmt::format( "script_{}", inst->GetItem()->ScriptFileID ); } else if (strlen(inst->GetItem()->CharmFile) > 0) { item_script = inst->GetItem()->CharmFile; } else { item_script = std::to_string(inst->GetID()); } uint32 item_id = inst->GetID(); auto iter = _item_quest_status.find(item_id); if (iter != _item_quest_status.end()) { //loaded or failed to load if (iter->second != QuestFailedToLoad) { auto qiter = _interfaces.find(iter->second); return qiter->second->ItemHasQuestSub(inst, event_id); } } else { std::string filename; auto qi = GetQIByItemQuest(item_script, filename); if (qi) { _item_quest_status[item_id] = qi->GetIdentifier(); qi->LoadItemScript(filename, inst); return qi->ItemHasQuestSub(inst, event_id); } else { _item_quest_status[item_id] = QuestFailedToLoad; } } return false; } bool QuestParserCollection::HasEncounterSub(QuestEventID event_id, const std::string& package_name) { for (auto it = _encounter_quest_status.begin(); it != _encounter_quest_status.end(); ++it) { if (it->second != QuestFailedToLoad) { auto qit = _interfaces.find(it->second); if (qit != _interfaces.end() && qit->second->HasEncounterSub(package_name, event_id)) { return true; } } } return false; } bool QuestParserCollection::BotHasQuestSubLocal(QuestEventID event_id) { if (_bot_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByBotQuest(filename); if (qi) { _bot_quest_status = qi->GetIdentifier(); qi->LoadBotScript(filename); return qi->BotHasQuestSub(event_id); } } else if (_bot_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_bot_quest_status); return iter->second->BotHasQuestSub(event_id); } return false; } bool QuestParserCollection::BotHasQuestSubGlobal(QuestEventID event_id) { if (_global_bot_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalBotQuest(filename); if (qi) { _global_bot_quest_status = qi->GetIdentifier(); qi->LoadGlobalBotScript(filename); return qi->GlobalBotHasQuestSub(event_id); } } else if (_global_bot_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_bot_quest_status); return iter->second->GlobalBotHasQuestSub(event_id); } return false; } bool QuestParserCollection::BotHasQuestSub(QuestEventID event_id) { return BotHasQuestSubLocal(event_id) || BotHasQuestSubGlobal(event_id); } bool QuestParserCollection::MercHasQuestSubLocal(QuestEventID event_id) { if (_merc_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByMercQuest(filename); if (qi) { _merc_quest_status = qi->GetIdentifier(); qi->LoadMercScript(filename); return qi->MercHasQuestSub(event_id); } } else if (_merc_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_merc_quest_status); return iter->second->MercHasQuestSub(event_id); } return false; } bool QuestParserCollection::MercHasQuestSubGlobal(QuestEventID event_id) { if (_global_merc_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalMercQuest(filename); if (qi) { _global_merc_quest_status = qi->GetIdentifier(); qi->LoadGlobalMercScript(filename); return qi->GlobalMercHasQuestSub(event_id); } } else if (_global_merc_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_merc_quest_status); return iter->second->GlobalMercHasQuestSub(event_id); } return false; } bool QuestParserCollection::MercHasQuestSub(QuestEventID event_id) { return MercHasQuestSubLocal(event_id) || MercHasQuestSubGlobal(event_id); } bool QuestParserCollection::ZoneHasQuestSubLocal(QuestEventID event_id) { if (_zone_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByZoneQuest(filename); if (qi) { _zone_quest_status = qi->GetIdentifier(); qi->LoadZoneScript(filename); return qi->ZoneHasQuestSub(event_id); } } else if (_zone_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_zone_quest_status); return iter->second->ZoneHasQuestSub(event_id); } return false; } bool QuestParserCollection::ZoneHasQuestSubGlobal(QuestEventID event_id) { if (_global_zone_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalZoneQuest(filename); if (qi) { _global_zone_quest_status = qi->GetIdentifier(); qi->LoadGlobalZoneScript(filename); return qi->GlobalZoneHasQuestSub(event_id); } } else if (_global_zone_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_zone_quest_status); return iter->second->GlobalZoneHasQuestSub(event_id); } return false; } bool QuestParserCollection::ZoneHasQuestSub(QuestEventID event_id) { return ZoneHasQuestSubLocal(event_id) || ZoneHasQuestSubGlobal(event_id); } int QuestParserCollection::EventNPC( QuestEventID event_id, NPC* npc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (npc->IsResumedFromZoneSuspend() && npc->IsQueuedForCorpse()) { return 0; } const int local_return = EventNPCLocal(event_id, npc, init, data, extra_data, extra_pointers); const int global_return = EventNPCGlobal(event_id, npc, init, data, extra_data, extra_pointers); const int default_return = DispatchEventNPC(event_id, npc, init, data, extra_data, extra_pointers); if (local_return != 0) { return local_return; } else if (global_return != 0) { return global_return; } else if (default_return != 0) { return default_return; } return 0; } int QuestParserCollection::EventNPCLocal( QuestEventID event_id, NPC* npc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { auto iter = _npc_quest_status.find(npc->GetNPCTypeID()); if (iter != _npc_quest_status.end()) { //loaded or failed to load if (iter->second != QuestFailedToLoad) { auto qiter = _interfaces.find(iter->second); return qiter->second->EventNPC(event_id, npc, init, data, extra_data, extra_pointers); } } else if (_npc_quest_status[npc->GetNPCTypeID()] != QuestFailedToLoad) { std::string filename; auto qi = GetQIByNPCQuest(npc->GetNPCTypeID(), filename); if (qi) { _npc_quest_status[npc->GetNPCTypeID()] = qi->GetIdentifier(); qi->LoadNPCScript(filename, npc->GetNPCTypeID()); return qi->EventNPC(event_id, npc, init, data, extra_data, extra_pointers); } else { _npc_quest_status[npc->GetNPCTypeID()] = QuestFailedToLoad; } } return 0; } int QuestParserCollection::EventNPCGlobal( QuestEventID event_id, NPC* npc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_global_npc_quest_status != QuestUnloaded && _global_npc_quest_status != QuestFailedToLoad) { auto qiter = _interfaces.find(_global_npc_quest_status); return qiter->second->EventGlobalNPC(event_id, npc, init, data, extra_data, extra_pointers); } else if (_global_npc_quest_status != QuestFailedToLoad) { std::string filename; auto qi = GetQIByGlobalNPCQuest(filename); if (qi) { _global_npc_quest_status = qi->GetIdentifier(); qi->LoadGlobalNPCScript(filename); return qi->EventGlobalNPC(event_id, npc, init, data, extra_data, extra_pointers); } else { _global_npc_quest_status = QuestFailedToLoad; } } return 0; } int QuestParserCollection::EventPlayer( QuestEventID event_id, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers ) { const int local_return = EventPlayerLocal(event_id, client, data, extra_data, extra_pointers); const int global_return = EventPlayerGlobal(event_id, client, data, extra_data, extra_pointers); const int default_return = DispatchEventPlayer(event_id, client, data, extra_data, extra_pointers); if (local_return != 0) { return local_return; } else if (global_return != 0) { return global_return; } else if (default_return != 0) { return default_return; } return 0; } int QuestParserCollection::EventPlayerLocal( QuestEventID event_id, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_player_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByPlayerQuest(filename); if (qi) { _player_quest_status = qi->GetIdentifier(); qi->LoadPlayerScript(filename); return qi->EventPlayer(event_id, client, data, extra_data, extra_pointers); } } else { if (_player_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_player_quest_status); return iter->second->EventPlayer(event_id, client, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventPlayerGlobal( QuestEventID event_id, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_global_player_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalPlayerQuest(filename); if (qi) { _global_player_quest_status = qi->GetIdentifier(); qi->LoadGlobalPlayerScript(filename); return qi->EventGlobalPlayer(event_id, client, data, extra_data, extra_pointers); } } else { if (_global_player_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_player_quest_status); return iter->second->EventGlobalPlayer(event_id, client, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventItem( QuestEventID event_id, Client* client, EQ::ItemInstance* inst, Mob* mob, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (!inst) { return 0; } std::string item_script; if (inst->GetItem()->ScriptFileID != 0) { item_script = fmt::format( "script_{}", inst->GetItem()->ScriptFileID ); } else if (strlen(inst->GetItem()->CharmFile) > 0) { item_script = inst->GetItem()->CharmFile; } else { item_script = std::to_string(inst->GetID()); } uint32 item_id = inst->GetID(); auto iter = _item_quest_status.find(item_id); if (iter != _item_quest_status.end()) { //loaded or failed to load if (iter->second != QuestFailedToLoad) { auto qiter = _interfaces.find(iter->second); int ret = DispatchEventItem(event_id, client, inst, mob, data, extra_data, extra_pointers); int i = qiter->second->EventItem(event_id, client, inst, mob, data, extra_data, extra_pointers); if (i != 0) { ret = i; } return ret; } return DispatchEventItem(event_id, client, inst, mob, data, extra_data, extra_pointers); } else if (_item_quest_status[item_id] != QuestFailedToLoad) { std::string filename; auto qi = GetQIByItemQuest(item_script, filename); if (qi) { _item_quest_status[item_id] = qi->GetIdentifier(); qi->LoadItemScript(filename, inst); int ret = DispatchEventItem( event_id, client, inst, mob, data, extra_data, extra_pointers ); int i = qi->EventItem(event_id, client, inst, mob, data, extra_data, extra_pointers); if (i != 0) { ret = i; } return ret; } else { _item_quest_status[item_id] = QuestFailedToLoad; return DispatchEventItem(event_id, client, inst, mob, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventSpell( QuestEventID event_id, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector* extra_pointers ) { auto iter = _spell_quest_status.find(spell_id); if (iter != _spell_quest_status.end()) { //loaded or failed to load if (iter->second != QuestFailedToLoad) { auto qi = _interfaces.find(iter->second); int ret = DispatchEventSpell(event_id, mob, client, spell_id, data, extra_data, extra_pointers); int i = qi->second->EventSpell(event_id, mob, client, spell_id, data, extra_data, extra_pointers); if (i != 0) { ret = i; } return ret; } return DispatchEventSpell(event_id, mob, client, spell_id, data, extra_data, extra_pointers); } else if (_spell_quest_status[spell_id] != QuestFailedToLoad) { std::string filename; auto qi = GetQIBySpellQuest(spell_id, filename); if (qi) { _spell_quest_status[spell_id] = qi->GetIdentifier(); qi->LoadSpellScript(filename, spell_id); int ret = DispatchEventSpell( event_id, mob, client, spell_id, data, extra_data, extra_pointers ); int i = qi->EventSpell( event_id, mob, client, spell_id, data, extra_data, extra_pointers ); if (i != 0) { ret = i; } return ret; } else { _spell_quest_status[spell_id] = QuestFailedToLoad; return DispatchEventSpell(event_id, mob, client, spell_id, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventEncounter( QuestEventID event_id, std::string encounter_name, std::string data, uint32 extra_data, std::vector* extra_pointers ) { auto iter = _encounter_quest_status.find(encounter_name); if (iter != _encounter_quest_status.end()) { if (iter->second != QuestFailedToLoad) { // Loaded or failed to load auto qiter = _interfaces.find(iter->second); return qiter->second->EventEncounter(event_id, encounter_name, data, extra_data, extra_pointers); } } else if (_encounter_quest_status[encounter_name] != QuestFailedToLoad) { std::string filename; auto qi = GetQIByEncounterQuest(encounter_name, filename); if (qi) { _encounter_quest_status[encounter_name] = qi->GetIdentifier(); qi->LoadEncounterScript(filename, encounter_name); return qi->EventEncounter(event_id, encounter_name, data, extra_data, extra_pointers); } else { _encounter_quest_status[encounter_name] = QuestFailedToLoad; } } return 0; } int QuestParserCollection::EventBot( QuestEventID event_id, Bot* bot, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { const int local_return = EventBotLocal(event_id, bot, init, data, extra_data, extra_pointers); const int global_return = EventBotGlobal(event_id, bot, init, data, extra_data, extra_pointers); const int default_return = DispatchEventBot(event_id, bot, init, data, extra_data, extra_pointers); if (local_return != 0) { return local_return; } else if (global_return != 0) { return global_return; } else if (default_return != 0) { return default_return; } return 0; } int QuestParserCollection::EventBotLocal( QuestEventID event_id, Bot* bot, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_bot_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByBotQuest(filename); if (qi) { _bot_quest_status = qi->GetIdentifier(); qi->LoadBotScript(filename); return qi->EventBot(event_id, bot, init, data, extra_data, extra_pointers); } } else { if (_bot_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_bot_quest_status); return iter->second->EventBot(event_id, bot, init, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventBotGlobal( QuestEventID event_id, Bot* bot, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_global_bot_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalBotQuest(filename); if (qi) { _global_bot_quest_status = qi->GetIdentifier(); qi->LoadGlobalBotScript(filename); return qi->EventGlobalBot(event_id, bot, init, data, extra_data, extra_pointers); } } else { if (_global_bot_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_bot_quest_status); return iter->second->EventGlobalBot(event_id, bot, init, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventMerc( QuestEventID event_id, Merc* merc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { const int local_return = EventMercLocal(event_id, merc, init, data, extra_data, extra_pointers); const int global_return = EventMercGlobal(event_id, merc, init, data, extra_data, extra_pointers); const int default_return = DispatchEventMerc(event_id, merc, init, data, extra_data, extra_pointers); if (local_return != 0) { return local_return; } else if (global_return != 0) { return global_return; } else if (default_return != 0) { return default_return; } return 0; } int QuestParserCollection::EventMercLocal( QuestEventID event_id, Merc* merc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_merc_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByMercQuest(filename); if (qi) { _merc_quest_status = qi->GetIdentifier(); qi->LoadMercScript(filename); return qi->EventMerc(event_id, merc, init, data, extra_data, extra_pointers); } } else { if (_merc_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_merc_quest_status); return iter->second->EventMerc(event_id, merc, init, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventMercGlobal( QuestEventID event_id, Merc* merc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_global_merc_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalMercQuest(filename); if (qi) { _global_merc_quest_status = qi->GetIdentifier(); qi->LoadGlobalMercScript(filename); return qi->EventGlobalMerc(event_id, merc, init, data, extra_data, extra_pointers); } } else { if (_global_merc_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_merc_quest_status); return iter->second->EventGlobalMerc(event_id, merc, init, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventZone( QuestEventID event_id, Zone* zone, std::string data, uint32 extra_data, std::vector* extra_pointers ) { const int local_return = EventZoneLocal(event_id, zone, data, extra_data, extra_pointers); const int global_return = EventZoneGlobal(event_id, zone, data, extra_data, extra_pointers); const int default_return = DispatchEventZone(event_id, zone, data, extra_data, extra_pointers); if (local_return != 0) { return local_return; } else if (global_return != 0) { return global_return; } else if (default_return != 0) { return default_return; } return 0; } int QuestParserCollection::EventZoneLocal( QuestEventID event_id, Zone* zone, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_zone_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByZoneQuest(filename); if (qi) { _zone_quest_status = qi->GetIdentifier(); qi->LoadZoneScript(filename); return qi->EventZone(event_id, zone, data, extra_data, extra_pointers); } } else { if (_zone_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_zone_quest_status); return iter->second->EventZone(event_id, zone, data, extra_data, extra_pointers); } } return 0; } int QuestParserCollection::EventZoneGlobal( QuestEventID event_id, Zone* zone, std::string data, uint32 extra_data, std::vector* extra_pointers ) { if (_global_zone_quest_status == QuestUnloaded) { std::string filename; auto qi = GetQIByGlobalZoneQuest(filename); if (qi) { _global_zone_quest_status = qi->GetIdentifier(); qi->LoadGlobalZoneScript(filename); return qi->EventGlobalZone(event_id, zone, data, extra_data, extra_pointers); } } else { if (_global_zone_quest_status != QuestFailedToLoad) { auto iter = _interfaces.find(_global_zone_quest_status); return iter->second->EventGlobalZone(event_id, zone, data, extra_data, extra_pointers); } } return 0; } QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename) { if (!zone) { return nullptr; } const auto npc_type = content_db.LoadNPCTypesData(npc_id); if (!npc_type && npc_id != ZONE_CONTROLLER_NPC_ID) { return nullptr; } std::string npc_name = npc_id == ZONE_CONTROLLER_NPC_ID ? "zone_controller" : npc_type->name; Strings::FindReplace(npc_name, "`", "-"); for (auto & dir : PathManager::Instance()->GetQuestPaths()) { const std::string& npc_id_and_name = fmt::format( "{}_{}", npc_name, npc_id ); const std::string& global_path = fmt::format( "{}/{}", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/{}", zone_versioned_path, npc_id), // Local versioned by NPC ID (./quests/zone/v0/10.ext) fmt::format("{}/{}", zone_versioned_path, npc_name), // Local versioned by NPC Name (./quests/zone/v0/name.ext) fmt::format("{}/{}", zone_versioned_path, npc_id_and_name), // Local versioned by NPC ID and NPC Name (./quests/zone/v0/10_name.ext) fmt::format("{}/{}", zone_path, npc_id), // Local by NPC ID fmt::format("{}/{}", zone_path, npc_name), // Local by NPC Name fmt::format("{}/{}", zone_path, npc_id_and_name), // Local by NPC ID and NPC Name fmt::format("{}/{}", global_path, npc_id), // Global by NPC ID fmt::format("{}/{}", global_path, npc_name), // Global by NPC ID fmt::format("{}/{}", global_path, npc_id_and_name), // Global by NPC ID and NPC Name fmt::format("{}/default", zone_versioned_path), // Zone Versioned Default (./quests/zone/v0/default.ext) fmt::format("{}/default", zone_path), // Zone Default fmt::format("{}/default", global_path), // Global Default }; std::string file_name; for (auto & file : file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByPlayerQuest(std::string& filename) { if (!zone || !zone->IsLoaded()) { return nullptr; } for (auto & dir : PathManager::Instance()->GetQuestPaths()) { const std::string& global_path = fmt::format( "{}/{}", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/player", zone_versioned_path), // Local by Instance Version ./quests/zone/v0/player.ext fmt::format("{}/player_v{}", zone_path, zone->GetInstanceVersion()), // Local by Instance Version fmt::format("{}/player", zone_path), // Local fmt::format("{}/player", global_path) // Global }; std::string file_name; for (auto & file : file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByGlobalNPCQuest(std::string& filename) { if (!zone) { return nullptr; } std::string file_name; for (auto & dir : PathManager::Instance()->GetQuestPaths()) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}/{}/global_npc.{}", dir, QUEST_GLOBAL_DIRECTORY, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByGlobalPlayerQuest(std::string& filename) { if (!zone) { return nullptr; } std::string file_name; for (auto & dir : PathManager::Instance()->GetQuestPaths()) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}/{}/global_player.{}", dir, QUEST_GLOBAL_DIRECTORY, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIBySpellQuest(uint32 spell_id, std::string& filename) { if (!zone) { return nullptr; } for (auto & dir : PathManager::Instance()->GetQuestPaths()) { const std::string& global_path = fmt::format( "{}/{}/spells", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}/spells", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}/spells", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/{}", zone_versioned_path, spell_id), // Local versioned by Spell ID ./quests/zone/v0/spells/10.ext fmt::format("{}/{}", zone_path, spell_id), // Local fmt::format("{}/{}", global_path, spell_id), // Global fmt::format("{}/default", zone_path), // Local Default fmt::format("{}/default", global_path) // Global Default }; std::string file_name; for (auto & file : file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByItemQuest(std::string item_script, std::string& filename) { if (!zone) { return nullptr; } for (auto & dir : PathManager::Instance()->GetQuestPaths()) { const std::string& global_path = fmt::format( "{}/{}/items", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}/items", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}/items", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/{}", zone_versioned_path, item_script), // Local versioned by Item Script ./quests/zone/v0/items/10.ext fmt::format("{}/{}", zone_path, item_script), // Local fmt::format("{}/{}", global_path, item_script), // Global fmt::format("{}/default", zone_path), // Local Default fmt::format("{}/default", global_path) // Global Default }; std::string file_name; for (auto & file : file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByEncounterQuest(std::string encounter_name, std::string& filename) { if (!zone) { return nullptr; } for (auto & dir : PathManager::Instance()->GetQuestPaths()) { const std::string& global_path = fmt::format( "{}/{}/encounters", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}/encounters", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}/encounters", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/{}", zone_versioned_path, encounter_name), // Local versioned ./quests/zone/v0/encounters/name.ext fmt::format("{}/{}", zone_path, encounter_name), // Local fmt::format("{}/{}", global_path, encounter_name) // Global }; std::string file_name; for (auto & file : file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByBotQuest(std::string& filename) { if (!zone || !zone->IsLoaded()) { return nullptr; } for (auto & dir : PathManager::Instance()->GetQuestPaths()) { const std::string& global_path = fmt::format( "{}/{}", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/bot", zone_versioned_path), // Local versioned by Instance Version ./quests/zone/v0/bot.ext fmt::format("{}/bot_v{}", zone_path, zone->GetInstanceVersion()), // Local by Instance Version fmt::format("{}/bot", zone_path), // Local fmt::format("{}/bot", global_path) // Global }; std::string file_name; for (auto & file : file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByGlobalBotQuest(std::string& filename) { if (!zone) { return nullptr; } std::string file_name; for (auto & dir : PathManager::Instance()->GetQuestPaths()) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}/{}/global_bot.{}", dir, QUEST_GLOBAL_DIRECTORY, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByMercQuest(std::string& filename) { if (!zone || !zone->IsLoaded()) { return nullptr; } for (auto & dir : PathManager::Instance()->GetQuestPaths()) { const std::string& global_path = fmt::format( "{}/{}", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/merc", zone_versioned_path), // Local versioned by Instance Version ./quests/zone/v0/merc.ext fmt::format("{}/merc_v{}", zone_path, zone->GetInstanceVersion()), // Local by Instance Version fmt::format("{}/merc", zone_path), // Local fmt::format("{}/merc", global_path) // Global }; std::string file_name; for (auto & file : file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByGlobalMercQuest(std::string& filename) { if (!zone) { return nullptr; } std::string file_name; for (auto & dir : PathManager::Instance()->GetQuestPaths()) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}/{}/global_merc.{}", dir, QUEST_GLOBAL_DIRECTORY, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByZoneQuest(std::string& filename) { if (!zone || !zone->IsLoaded()) { return nullptr; } std::string file_name; for (auto& dir: PathManager::Instance()->GetQuestPaths()) { const std::string& global_path = fmt::format( "{}/{}", dir, QUEST_GLOBAL_DIRECTORY ); const std::string& zone_path = fmt::format( "{}/{}", dir, zone->GetShortName() ); const std::string& zone_versioned_path = fmt::format( "{}/{}/v{}", dir, zone->GetShortName(), zone->GetInstanceVersion() ); std::vector file_names = { fmt::format("{}/zone", zone_versioned_path), // Local versioned by Instance Version ./quests/zone/v0/zone.ext fmt::format("{}/zone_v{}", zone_path, zone->GetInstanceVersion()), // Local by Instance Version fmt::format("{}/zone", zone_path), // Local fmt::format("{}/zone", global_path) // Global }; for (auto& file: file_names) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}.{}", file, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } } return nullptr; } QuestInterface* QuestParserCollection::GetQIByGlobalZoneQuest(std::string& filename) { if (!zone) { return nullptr; } std::string file_name; for (auto& dir: PathManager::Instance()->GetQuestPaths()) { for (auto* e: _load_precedence) { file_name = fmt::format( "{}/{}/global_zone.{}", dir, QUEST_GLOBAL_DIRECTORY, _extensions.find(e->GetIdentifier())->second ); if (File::Exists(file_name)) { filename = file_name; return e; } } } return nullptr; } void QuestParserCollection::GetErrors(std::list& quest_errors) { quest_errors.clear(); for (const auto& e: _load_precedence) { e->GetErrors(quest_errors); } } int QuestParserCollection::DispatchEventNPC( QuestEventID event_id, NPC* npc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { int ret = 0; for (const auto& e: _load_precedence) { const int i = e->DispatchEventNPC(event_id, npc, init, data, extra_data, extra_pointers); if (i != 0) { ret = i; } } return ret; } int QuestParserCollection::DispatchEventPlayer( QuestEventID event_id, Client* client, std::string data, uint32 extra_data, std::vector* extra_pointers ) { int ret = 0; for (const auto& e: _load_precedence) { const int i = e->DispatchEventPlayer(event_id, client, data, extra_data, extra_pointers); if (i != 0) { ret = i; } } return ret; } int QuestParserCollection::DispatchEventItem( QuestEventID event_id, Client* client, EQ::ItemInstance* inst, Mob* mob, std::string data, uint32 extra_data, std::vector* extra_pointers ) { int ret = 0; for (const auto& e: _load_precedence) { const int i = e->DispatchEventItem(event_id, client, inst, mob, data, extra_data, extra_pointers); if (i != 0) { ret = i; } } return ret; } int QuestParserCollection::DispatchEventSpell( QuestEventID event_id, Mob* mob, Client* client, uint32 spell_id, std::string data, uint32 extra_data, std::vector* extra_pointers ) { int ret = 0; for (const auto& e: _load_precedence) { const int i = e->DispatchEventSpell(event_id, mob, client, spell_id, data, extra_data, extra_pointers); if (i != 0) { ret = i; } } return ret; } int QuestParserCollection::DispatchEventBot( QuestEventID event_id, Bot* bot, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { int ret = 0; for (const auto& e: _load_precedence) { int i = e->DispatchEventBot(event_id, bot, init, data, extra_data, extra_pointers); if (i != 0) { ret = i; } } return ret; } int QuestParserCollection::DispatchEventMerc( QuestEventID event_id, Merc* merc, Mob* init, std::string data, uint32 extra_data, std::vector* extra_pointers ) { int ret = 0; for (const auto& e: _load_precedence) { int i = e->DispatchEventMerc(event_id, merc, init, data, extra_data, extra_pointers); if (i != 0) { ret = i; } } return ret; } int QuestParserCollection::DispatchEventZone( QuestEventID event_id, Zone* zone, std::string data, uint32 extra_data, std::vector* extra_pointers ) { int ret = 0; for (const auto& e: _load_precedence) { int i = e->DispatchEventZone(event_id, zone, data, extra_data, extra_pointers); if (i != 0) { ret = i; } } return ret; } void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* s) { for (int i = 0; i < _LargestEventID; i++) { s[i].qglobals = 1; s[i].mob = 1; s[i].zone = 1; s[i].item = 1; s[i].event_variables = 1; } const auto& l = PerlEventExportSettingsRepository::All(database); for (const auto& e: l) { s[e.event_id].qglobals = e.export_qglobals; s[e.event_id].mob = e.export_mob; s[e.event_id].zone = e.export_zone; s[e.event_id].item = e.export_item; s[e.event_id].event_variables = e.export_event; } LogInfo("Loaded [{}] Perl Event Export Settings", l.size()); } int QuestParserCollection::EventBotMerc( QuestEventID event_id, Mob* e, Mob* init, std::function lazy_data, uint32 extra_data, std::vector* extra_pointers ) { if (e->IsBot() && BotHasQuestSub(event_id)) { return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers); } else if (e->IsMerc() && MercHasQuestSub(event_id)) { return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers); } return false; // No quest subscription found } int QuestParserCollection::EventMercNPC( QuestEventID event_id, Mob* e, Mob* init, std::function lazy_data, uint32 extra_data, std::vector* extra_pointers ) { if (e->IsMerc() && MercHasQuestSub(event_id)) { return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers); } else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) { return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers); } return false; // No quest subscription found } int QuestParserCollection::EventBotMercNPC( QuestEventID event_id, Mob* e, Mob* init, std::function lazy_data, uint32 extra_data, std::vector* extra_pointers ) { if (e->IsBot() && BotHasQuestSub(event_id)) { return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers); } else if (e->IsMerc() && MercHasQuestSub(event_id)) { return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers); } else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) { return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers); } return false; // No quest subscription found } int QuestParserCollection::EventMob( QuestEventID event_id, Mob* e, Mob* init, std::function lazy_data, uint32 extra_data, std::vector* extra_pointers ) { if (e->IsClient() && PlayerHasQuestSub(event_id)) { return EventPlayer(event_id, e->CastToClient(), lazy_data(), extra_data, extra_pointers); } else if (e->IsBot() && BotHasQuestSub(event_id)) { return EventBot(event_id, e->CastToBot(), init, lazy_data(), extra_data, extra_pointers); } else if (e->IsMerc() && MercHasQuestSub(event_id)) { return EventMerc(event_id, e->CastToMerc(), init, lazy_data(), extra_data, extra_pointers); } else if (e->IsNPC() && HasQuestSub(e->GetNPCTypeID(), event_id)) { return EventNPC(event_id, e->CastToNPC(), init, lazy_data(), extra_data, extra_pointers); } return false; // No quest subscription found }