From 323a0c0b27ce19586305347d4bf37a3f2d2ae990 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Jul 2025 15:08:08 -0400 Subject: [PATCH] [Feature] Zone Scripting (#4908) * [Feature] Add Zone Scripting Capabilities * Push * Update zone.cpp * Fix crashes * Lua * Add other events, finish Lua * Add EVENT_ENTER_ZONE * Final * Push * Push * [Feature] Add Zone Scripting Capabilities * Push * Update zone.cpp * Fix crashes * Add EVENT_ENTER_ZONE * Remove duplicates * Update embparser.cpp --- zone/attack.cpp | 19 ++ zone/client_packet.cpp | 21 ++ zone/corpse.cpp | 15 + zone/embparser.cpp | 545 ++++++++++++++++++------------- zone/embparser.h | 91 +++--- zone/entity.cpp | 5 + zone/lua_parser.cpp | 263 ++++++++++++++- zone/lua_parser.h | 45 ++- zone/lua_parser_events.cpp | 289 ++++++++++++++++ zone/lua_parser_events.h | 137 ++++++++ zone/lua_zone.cpp | 77 +++++ zone/lua_zone.h | 11 + zone/npc.cpp | 5 + zone/object.cpp | 24 ++ zone/perl_zone.cpp | 66 ++++ zone/quest_interface.h | 45 +++ zone/quest_parser_collection.cpp | 225 +++++++++++++ zone/quest_parser_collection.h | 39 +++ zone/questmgr.cpp | 114 ++++--- zone/questmgr.h | 25 +- zone/zone.cpp | 258 +++++++++++++++ zone/zone.h | 30 ++ 22 files changed, 2009 insertions(+), 340 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index e28eac7cc..9c6581a32 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3039,6 +3039,25 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy DispatchZoneControllerEvent(EVENT_DEATH_ZONE, owner_or_self, export_string, 0, &args); } + if (parse->ZoneHasQuestSub(EVENT_DEATH_ZONE)) { + const auto& export_string = fmt::format( + "{} {} {} {} {} {} {} {} {}", + killer_mob ? killer_mob->GetID() : 0, + damage, + spell, + static_cast(attack_skill), + entity_id, + m_combat_record.GetStartTime(), + m_combat_record.GetEndTime(), + m_combat_record.GetDamageReceived(), + m_combat_record.GetHealingReceived() + ); + + std::vector args = { corpse, this, owner_or_self }; + + parse->EventZone(EVENT_DEATH_ZONE, zone, export_string, 0, &args); + } + return true; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a69a6b96e..a62131b71 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -795,6 +795,11 @@ void Client::CompleteConnect() parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); } + if (parse->ZoneHasQuestSub(EVENT_ENTER_ZONE)) { + std::vector args = { this }; + parse->EventZone(EVENT_ENTER_ZONE, zone, "", 0, &args); + } + DeleteEntityVariable(SEE_BUFFS_FLAG); // the way that the client deals with positions during the initial spawn struct @@ -4709,6 +4714,12 @@ void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) quest_return = parse->EventPlayer(EVENT_CLICK_DOOR, this, std::to_string(cd->doorid), 0, &args); } + if (parse->ZoneHasQuestSub(EVENT_CLICK_DOOR)) { + std::vector args = { currentdoor, this }; + + quest_return = parse->EventZone(EVENT_CLICK_DOOR, zone, std::to_string(cd->doorid), 0, &args); + } + if (quest_return == 0) { currentdoor->HandleClick(this, 0); } @@ -4741,6 +4752,11 @@ void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) parse->EventPlayer(EVENT_CLICK_OBJECT, this, std::to_string(click_object->drop_id), GetID(), &args); } + if (parse->ZoneHasQuestSub(EVENT_CLICK_OBJECT)) { + std::vector args = { object, this }; + parse->EventZone(EVENT_CLICK_OBJECT, zone, std::to_string(click_object->drop_id), GetID(), &args); + } + if (IsDevToolsEnabled()) { SetObjectToolEntityId(entity->GetID()); ObjectManipulation::CommandHeader(this); @@ -12042,6 +12058,11 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) parse->EventPlayer(EVENT_POPUP_RESPONSE, this, std::to_string(popup_response->popupid), 0); } + if (parse->ZoneHasQuestSub(EVENT_POPUP_RESPONSE)) { + std::vector args = { this }; + parse->EventZone(EVENT_POPUP_RESPONSE, zone, std::to_string(popup_response->popupid), 0, &args); + } + auto t = GetTarget(); if (t) { parse->EventBotMercNPC(EVENT_POPUP_RESPONSE, t, this, [&]() { return std::to_string(popup_response->popupid); }); diff --git a/zone/corpse.cpp b/zone/corpse.cpp index f0b04a8f5..9bd13d63b 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1586,6 +1586,21 @@ void Corpse::LootCorpseItem(Client *c, const EQApplicationPacket *app) } } + if (parse->ZoneHasQuestSub(EVENT_LOOT_ZONE)) { + const auto &export_string = fmt::format( + "{} {} {} {}", + inst->GetItem()->ID, + inst->GetCharges(), + EntityList::RemoveNumbers(corpse_name), + GetID() + ); + + std::vector args = {inst, this, c}; + if (parse->EventZone(EVENT_LOOT_ZONE, zone, export_string, 0, &args) != 0) { + prevent_loot = true; + } + } + if (inst && PlayerEventLogs::Instance()->IsEventEnabled(PlayerEvent::LOOT_ITEM) && !IsPlayerCorpse()) { auto e = PlayerEvent::LootItemEvent{ .item_id = inst->GetItem()->ID, diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 16a662482..903a4821f 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -222,6 +222,8 @@ PerlembParser::PerlembParser() : perl(nullptr) global_bot_quest_status_ = questUnloaded; merc_quest_status_ = questUnloaded; global_merc_quest_status_ = questUnloaded; + zone_quest_status_ = questUnloaded; + global_zone_quest_status_ = questUnloaded; } PerlembParser::~PerlembParser() @@ -265,6 +267,8 @@ void PerlembParser::ReloadQuests() global_bot_quest_status_ = questUnloaded; merc_quest_status_ = questUnloaded; global_merc_quest_status_ = questUnloaded; + zone_quest_status_ = questUnloaded; + global_zone_quest_status_ = questUnloaded; item_quest_status_.clear(); spell_quest_status_.clear(); @@ -278,6 +282,7 @@ int PerlembParser::EventCommon( EQ::ItemInstance* inst, const SPDat_Spell_Struct* spell, Mob* mob, + Zone* zone, uint32 extra_data, bool is_global, std::vector* extra_pointers @@ -287,52 +292,22 @@ int PerlembParser::EventCommon( return 0; } - bool is_player_quest = false; - bool is_global_player_quest = false; - bool is_global_npc_quest = false; - bool is_bot_quest = false; - bool is_global_bot_quest = false; - bool is_merc_quest = false; - bool is_global_merc_quest = false; - bool is_item_quest = false; - bool is_spell_quest = false; - - std::string package_name; - - GetQuestTypes( - is_player_quest, - is_global_player_quest, - is_bot_quest, - is_global_bot_quest, - is_merc_quest, - is_global_merc_quest, - is_global_npc_quest, - is_item_quest, - is_spell_quest, + QuestType quest_type = GetQuestTypes( event_id, npc_mob, inst, mob, + zone, is_global ); - GetQuestPackageName( - is_player_quest, - is_global_player_quest, - is_bot_quest, - is_global_bot_quest, - is_merc_quest, - is_global_merc_quest, - is_global_npc_quest, - is_item_quest, - is_spell_quest, - package_name, + std::string package_name = GetQuestPackageName( + quest_type, event_id, object_id, data, npc_mob, - inst, - is_global + inst ); const std::string& sub_name = QuestEventSubroutines[event_id]; @@ -348,15 +323,7 @@ int PerlembParser::EventCommon( /* Check for QGlobal export event enable */ if (parse->perl_event_export_settings[event_id].qglobals) { ExportQGlobals( - is_player_quest, - is_global_player_quest, - is_bot_quest, - is_global_bot_quest, - is_merc_quest, - is_global_merc_quest, - is_global_npc_quest, - is_item_quest, - is_spell_quest, + quest_type, package_name, npc_mob, mob, @@ -367,15 +334,7 @@ int PerlembParser::EventCommon( /* Check for Mob export event enable */ if (parse->perl_event_export_settings[event_id].mob) { ExportMobVariables( - is_player_quest, - is_global_player_quest, - is_bot_quest, - is_global_bot_quest, - is_merc_quest, - is_global_merc_quest, - is_global_npc_quest, - is_item_quest, - is_spell_quest, + quest_type, package_name, mob, npc_mob @@ -397,19 +356,24 @@ int PerlembParser::EventCommon( ExportEventVariables(package_name, event_id, object_id, data, npc_mob, inst, mob, extra_data, extra_pointers); } - if (is_player_quest || is_global_player_quest) { - return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr); - } else if (is_bot_quest || is_global_bot_quest || is_merc_quest || is_global_merc_quest) { - return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr); - } else if (is_item_quest) { - return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr); - } else if (is_spell_quest) { + if (quest_type == QuestType::Player || quest_type == QuestType::PlayerGlobal) { + return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, nullptr, nullptr); + } else if ( + quest_type == QuestType::Bot || + quest_type == QuestType::BotGlobal || + quest_type == QuestType::Merc || + quest_type == QuestType::MercGlobal + ) { + return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, nullptr, nullptr); + } else if (quest_type == QuestType::Item || quest_type == QuestType::ItemGlobal) { + return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, inst, nullptr, nullptr); + } else if (quest_type == QuestType::Spell || quest_type == QuestType::SpellGlobal) { if (mob) { - return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, spell); + return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, mob, mob, nullptr, spell, nullptr); } else { - return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, spell); + return SendCommands(package_name.c_str(), QuestEventSubroutines[event_id], 0, npc_mob, mob, nullptr, spell, nullptr); } - } else { + } else if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) { return SendCommands( package_name.c_str(), QuestEventSubroutines[event_id], @@ -417,8 +381,20 @@ int PerlembParser::EventCommon( npc_mob, mob, nullptr, + nullptr, nullptr ); + } else if (quest_type == QuestType::Zone || quest_type == QuestType::ZoneGlobal) { + return SendCommands( + package_name.c_str(), + QuestEventSubroutines[event_id], + 0, + nullptr, + nullptr, + nullptr, + nullptr, + zone + ); } } @@ -439,6 +415,7 @@ int PerlembParser::EventNPC( nullptr, nullptr, mob, + nullptr, extra_data, false, extra_pointers @@ -462,6 +439,7 @@ int PerlembParser::EventGlobalNPC( nullptr, nullptr, mob, + nullptr, extra_data, true, extra_pointers @@ -484,6 +462,7 @@ int PerlembParser::EventPlayer( nullptr, nullptr, client, + nullptr, extra_data, false, extra_pointers @@ -506,6 +485,7 @@ int PerlembParser::EventGlobalPlayer( nullptr, nullptr, client, + nullptr, extra_data, true, extra_pointers @@ -534,6 +514,7 @@ int PerlembParser::EventItem( inst, nullptr, client, + nullptr, extra_data, false, extra_pointers @@ -558,6 +539,7 @@ int PerlembParser::EventSpell( nullptr, &spells[spell_id], client, + nullptr, extra_data, false, extra_pointers @@ -1006,7 +988,8 @@ int PerlembParser::SendCommands( Mob* other, Mob* mob, EQ::ItemInstance* inst, - const SPDat_Spell_Struct* spell + const SPDat_Spell_Struct* spell, + Zone* zone ) { if (!perl) { @@ -1014,12 +997,20 @@ int PerlembParser::SendCommands( } int ret_value = 0; + RunningQuest q; if (mob && mob->IsClient()) { - quest_manager.StartQuest(other, mob->CastToClient(), inst, spell); - } else { - quest_manager.StartQuest(other); + q.owner = other; + q.initiator = mob->CastToClient(); + q.questitem = inst; + q.questspell = spell; } + if (zone) { + q.zone = zone; + } + + quest_manager.StartQuest(q); + try { perl->eval(fmt::format("package {};", prefix).c_str()); @@ -1033,7 +1024,8 @@ int PerlembParser::SendCommands( "merc", "npc", "questitem", - "spell" + "spell", + "zone" }; for (const auto& suffix : suffixes) { @@ -1058,21 +1050,23 @@ int PerlembParser::SendCommands( sv_setsv(client, _empty_sv); } - if (other->IsBot()) { - Bot* b = quest_manager.GetBot(); - buf = fmt::format("{}::bot", prefix); - SV* bot = get_sv(buf.c_str(), true); - sv_setref_pv(bot, "Bot", b); - } else if (other->IsMerc()) { - Merc* m = quest_manager.GetMerc(); - buf = fmt::format("{}::merc", prefix); - SV* merc = get_sv(buf.c_str(), true); - sv_setref_pv(merc, "Merc", m); - } else if (other->IsNPC()) { - NPC* n = quest_manager.GetNPC(); - buf = fmt::format("{}::npc", prefix); - SV* npc = get_sv(buf.c_str(), true); - sv_setref_pv(npc, "NPC", n); + if (other) { + if (other->IsBot()) { + Bot* b = quest_manager.GetBot(); + buf = fmt::format("{}::bot", prefix); + SV* bot = get_sv(buf.c_str(), true); + sv_setref_pv(bot, "Bot", b); + } else if (other->IsMerc()) { + Merc* m = quest_manager.GetMerc(); + buf = fmt::format("{}::merc", prefix); + SV* merc = get_sv(buf.c_str(), true); + sv_setref_pv(merc, "Merc", m); + } else if (other->IsNPC()) { + NPC* n = quest_manager.GetNPC(); + buf = fmt::format("{}::npc", prefix); + SV* npc = get_sv(buf.c_str(), true); + sv_setref_pv(npc, "NPC", n); + } } //only export QuestItem if it's an inst quest @@ -1110,7 +1104,8 @@ int PerlembParser::SendCommands( "merc", "npc", "questitem", - "spell" + "spell", + "zone" }; for (const auto& suffix : suffixes) { @@ -1192,20 +1187,12 @@ void PerlembParser::MapFunctions() #endif // EMBPERL_XS_CLASSES } -void PerlembParser::GetQuestTypes( - bool& is_player_quest, - bool& is_global_player_quest, - bool& is_bot_quest, - bool& is_global_bot_quest, - bool& is_merc_quest, - bool& is_global_merc_quest, - bool& is_global_npc_quest, - bool& is_item_quest, - bool& is_spell_quest, +QuestType PerlembParser::GetQuestTypes( QuestEventID event_id, Mob* npc_mob, EQ::ItemInstance* inst, Mob* mob, + Zone* zone, bool is_global ) { @@ -1219,100 +1206,74 @@ void PerlembParser::GetQuestTypes( event_id == EVENT_SPELL_FADE || event_id == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE ) { - is_spell_quest = true; + return is_global ? QuestType::SpellGlobal : QuestType::Spell; } else { if (npc_mob) { if (!inst) { - if (is_global) { - if (npc_mob->IsBot()) { - is_global_bot_quest = true; - } else if (npc_mob->IsMerc()) { - is_global_merc_quest = true; - } - } else { - if (npc_mob->IsBot()) { - is_bot_quest = true; - } else if (npc_mob->IsMerc()) { - is_merc_quest = true; - } + if (npc_mob->IsBot()) { + return is_global ? QuestType::BotGlobal : QuestType::Bot; + } else if (npc_mob->IsMerc()) { + return is_global ? QuestType::MercGlobal : QuestType::Merc; + } else if (npc_mob->IsNPC()) { + return is_global ? QuestType::NPCGlobal : QuestType::NPC; } } else { - is_item_quest = true; + return is_global ? QuestType::ItemGlobal : QuestType::Item; } } else if (!npc_mob && mob) { if (!inst) { - if (is_global) { - if (mob->IsClient()) { - is_global_player_quest = true; - } - } else { - if (mob->IsClient()) { - is_player_quest = true; - } + if (mob->IsClient()) { + return is_global ? QuestType::PlayerGlobal : QuestType::Player; } } else { - is_item_quest = true; + return is_global ? QuestType::ItemGlobal : QuestType::Item; } + } else if (zone) { + return is_global ? QuestType::ZoneGlobal : QuestType::Zone; } } } -void PerlembParser::GetQuestPackageName( - bool& is_player_quest, - bool& is_global_player_quest, - bool& is_bot_quest, - bool& is_global_bot_quest, - bool& is_merc_quest, - bool& is_global_merc_quest, - bool& is_global_npc_quest, - bool& is_item_quest, - bool& is_spell_quest, - std::string& package_name, +std::string PerlembParser::GetQuestPackageName( + QuestType quest_type, QuestEventID event_id, uint32 object_id, const char* data, Mob* npc_mob, - EQ::ItemInstance* inst, - bool is_global + EQ::ItemInstance* inst ) { - if ( - !is_player_quest && - !is_global_player_quest && - !is_bot_quest && - !is_global_bot_quest && - !is_merc_quest && - !is_global_merc_quest && - !is_item_quest && - !is_spell_quest - ) { - if (is_global) { - is_global_npc_quest = true; - package_name = "qst_global_npc"; - } else { - package_name = fmt::format("qst_npc_{}", npc_mob->GetNPCTypeID()); - } - } else if (is_item_quest) { + if (quest_type == QuestType::NPC) { + return fmt::format("qst_npc_{}", npc_mob->GetNPCTypeID()); + } else if (quest_type == QuestType::NPCGlobal) { + return "qst_global_npc"; + } else if (quest_type == QuestType::Item || quest_type == QuestType::ItemGlobal) { if (!inst) { - return; + return ""; } - package_name = fmt::format("qst_item_{}", inst->GetID()); - } else if (is_player_quest) { - package_name = "qst_player"; - } else if (is_global_player_quest) { - package_name = "qst_global_player"; - } else if (is_bot_quest) { - package_name = "qst_bot"; - } else if (is_global_bot_quest) { - package_name = "qst_global_bot"; - } else if (is_merc_quest) { - package_name = "qst_merc"; - } else if (is_global_merc_quest) { - package_name = "qst_global_merc"; - } else { - package_name = fmt::format("qst_spell_{}", object_id); + return fmt::format("qst_item_{}", inst->GetID()); + } else if (quest_type == QuestType::Player) { + return "qst_player"; + } else if (quest_type == QuestType::PlayerGlobal) { + return "qst_global_player"; + } else if (quest_type == QuestType::Bot) { + return "qst_bot"; + } else if (quest_type == QuestType::BotGlobal) { + return "qst_global_bot"; + } else if (quest_type == QuestType::Merc) { + return "qst_merc"; + } else if (quest_type == QuestType::MercGlobal) { + return "qst_global_merc"; + } else if (quest_type == QuestType::Spell || quest_type == QuestType::SpellGlobal) { + return fmt::format("qst_spell_{}", object_id); + } else if (quest_type == QuestType::Zone) { + return "qst_zone"; + } else if (quest_type == QuestType::ZoneGlobal) { + return "qst_global_zone"; } + + return ""; } void PerlembParser::ExportCharID(const std::string& package_name, int& char_id, Mob* npc_mob, Mob* mob) @@ -1331,15 +1292,7 @@ void PerlembParser::ExportCharID(const std::string& package_name, int& char_id, } void PerlembParser::ExportQGlobals( - bool is_player_quest, - bool is_global_player_quest, - bool is_bot_quest, - bool is_global_bot_quest, - bool is_merc_quest, - bool is_global_merc_quest, - bool is_global_npc_quest, - bool is_item_quest, - bool is_spell_quest, + QuestType quest_type, std::string& package_name, Mob* npc_mob, Mob* mob, @@ -1347,16 +1300,7 @@ void PerlembParser::ExportQGlobals( ) { //NPC quest - if ( - !is_player_quest && - !is_global_player_quest && - !is_bot_quest && - !is_global_bot_quest && - !is_merc_quest && - !is_global_merc_quest && - !is_item_quest && - !is_spell_quest - ) { + if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) { //only export for npcs that are global enabled. if (npc_mob && npc_mob->GetQglobal()) { std::map globhash; @@ -1485,15 +1429,7 @@ void PerlembParser::ExportQGlobals( } void PerlembParser::ExportMobVariables( - bool is_player_quest, - bool is_global_player_quest, - bool is_bot_quest, - bool is_global_bot_quest, - bool is_merc_quest, - bool is_global_merc_quest, - bool is_global_npc_quest, - bool is_item_quest, - bool is_spell_quest, + QuestType quest_type, std::string& package_name, Mob* mob, Mob* npc_mob @@ -1511,15 +1447,7 @@ void PerlembParser::ExportMobVariables( ExportVar(package_name.c_str(), "bot_owner_char_id", mob->CastToBot()->GetBotOwnerCharacterID()); } - if ( - !is_player_quest && - !is_global_player_quest && - !is_bot_quest && - !is_global_bot_quest && - !is_merc_quest && - !is_global_merc_quest && - !is_item_quest - ) { + if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) { if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) { Client* c = mob->CastToClient(); @@ -1543,16 +1471,7 @@ void PerlembParser::ExportMobVariables( ExportVar(package_name.c_str(), "userid", mob->GetID()); } - if ( - !is_player_quest && - !is_global_player_quest && - !is_bot_quest && - !is_global_bot_quest && - !is_merc_quest && - !is_global_merc_quest && - !is_item_quest && - !is_spell_quest - ) { + if (quest_type == QuestType::NPC || quest_type == QuestType::NPCGlobal) { if (npc_mob->IsNPC()) { ExportVar(package_name.c_str(), "mname", npc_mob->GetName()); ExportVar(package_name.c_str(), "mobid", npc_mob->GetID()); @@ -1758,10 +1677,14 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "doorid", data); ExportVar(package_name.c_str(), "version", zone->GetInstanceVersion()); - if (extra_pointers && extra_pointers->size() == 1) { + if (extra_pointers && extra_pointers->size() >= 1) { ExportVar(package_name.c_str(), "door", "Doors", std::any_cast(extra_pointers->at(0))); } + if (extra_pointers && extra_pointers->size() == 2) { + ExportVar(package_name.c_str(), "player", "Client", std::any_cast(extra_pointers->at(1))); + } + break; } @@ -1782,10 +1705,14 @@ void PerlembParser::ExportEventVariables( ); } - if (extra_pointers && extra_pointers->size() == 2) { + if (extra_pointers && extra_pointers->size() >= 2) { ExportVar(package_name.c_str(), "corpse", "Corpse", std::any_cast(extra_pointers->at(1))); } + if (extra_pointers && extra_pointers->size() == 3) { + ExportVar(package_name.c_str(), "player", "Client", std::any_cast(extra_pointers->at(2))); + } + break; } @@ -1852,7 +1779,7 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "picked_up_id", data); ExportVar(package_name.c_str(), "picked_up_entity_id", extra_data); - if (extra_pointers && extra_pointers->size() == 1) { + if (extra_pointers && extra_pointers->size() >= 1) { ExportVar( package_name.c_str(), "item", @@ -1861,6 +1788,10 @@ void PerlembParser::ExportEventVariables( ); } + if (extra_pointers && extra_pointers->size() == 2) { + ExportVar(package_name.c_str(), "player", "Client", std::any_cast(extra_pointers->at(1))); + } + break; } @@ -1873,6 +1804,11 @@ void PerlembParser::ExportEventVariables( case EVENT_POPUP_RESPONSE: { ExportVar(package_name.c_str(), "popupid", data); + + if (extra_pointers && extra_pointers->size() == 1) { + ExportVar(package_name.c_str(), "player", "Client", std::any_cast(extra_pointers->at(0))); + } + break; } @@ -2036,10 +1972,14 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "objectid", data); ExportVar(package_name.c_str(), "clicker_id", extra_data); - if (extra_pointers && extra_pointers->size() == 1) { + if (extra_pointers && extra_pointers->size() >= 1) { ExportVar(package_name.c_str(), "object", "Object", std::any_cast(extra_pointers->at(0))); } + if (extra_pointers && extra_pointers->size() == 2) { + ExportVar(package_name.c_str(), "player", "Client", std::any_cast(extra_pointers->at(1))); + } + break; } @@ -2115,6 +2055,13 @@ void PerlembParser::ExportEventVariables( ExportVar(package_name.c_str(), "killed_npc_id", !killed->IsMerc() && killed->IsNPC() ? killed->GetNPCTypeID() : 0); } } + + if (extra_pointers && extra_pointers->size() == 3) { + Mob* killer = std::any_cast(extra_pointers->at(2)); + if (killer) { + ExportVar(package_name.c_str(), "killer", "Mob", killer); + } + } break; } @@ -2142,10 +2089,21 @@ void PerlembParser::ExportEventVariables( } case EVENT_SPAWN_ZONE: { - ExportVar(package_name.c_str(), "spawned_entity_id", mob->GetID()); - ExportVar(package_name.c_str(), "spawned_bot_id", mob->IsBot() ? mob->CastToBot()->GetBotID() : 0); - ExportVar(package_name.c_str(), "spawned_npc_id", mob->IsNPC() ? mob->GetNPCTypeID() : 0); - ExportVar(package_name.c_str(), "spawned", "Mob", mob); + if (mob) { + ExportVar(package_name.c_str(), "spawned", "Mob", mob); + ExportVar(package_name.c_str(), "spawned_bot_id", mob->IsBot() ? mob->CastToBot()->GetBotID() : 0); + ExportVar(package_name.c_str(), "spawned_entity_id", mob->GetID()); + ExportVar(package_name.c_str(), "spawned_npc_id", mob->IsNPC() ? mob->GetNPCTypeID() : 0); + } + + if (extra_pointers && extra_pointers->size() == 1) { + NPC* spawn_npc = std::any_cast(extra_pointers->at(0)); + ExportVar(package_name.c_str(), "spawned", "NPC", spawn_npc); + ExportVar(package_name.c_str(), "spawned_bot_id", spawn_npc->IsBot() ? spawn_npc->CastToBot()->GetBotID() : 0); + ExportVar(package_name.c_str(), "spawned_entity_id", spawn_npc->GetID()); + ExportVar(package_name.c_str(), "spawned_npc_id", spawn_npc->IsNPC() ? spawn_npc->GetNPCTypeID() : 0); + } + break; } @@ -2386,6 +2344,7 @@ void PerlembParser::ExportEventVariables( } case EVENT_DESPAWN: { + ExportVar(package_name.c_str(), "despawned", "Mob", npc_mob); ExportVar(package_name.c_str(), "despawned_entity_id", npc_mob->GetID()); ExportVar(package_name.c_str(), "despawned_bot_id", npc_mob->IsBot() ? npc_mob->CastToBot()->GetBotID() : 0); ExportVar(package_name.c_str(), "despawned_merc_id", npc_mob->IsMerc() ? npc_mob->CastToMerc()->GetMercenaryID() : 0); @@ -2394,9 +2353,21 @@ void PerlembParser::ExportEventVariables( } case EVENT_DESPAWN_ZONE: { - ExportVar(package_name.c_str(), "despawned_entity_id", mob->GetID()); - ExportVar(package_name.c_str(), "despawned_bot_id", mob->IsBot() ? mob->CastToBot()->GetBotID() : 0); - ExportVar(package_name.c_str(), "despawned_npc_id", mob->IsNPC() ? mob->GetNPCTypeID() : 0); + if (mob) { + ExportVar(package_name.c_str(), "despawned", "Mob", mob); + ExportVar(package_name.c_str(), "despawned_bot_id", mob->IsBot() ? mob->CastToBot()->GetBotID() : 0); + ExportVar(package_name.c_str(), "despawned_entity_id", mob->GetID()); + ExportVar(package_name.c_str(), "despawned_npc_id", mob->IsNPC() ? mob->GetNPCTypeID() : 0); + } + + if (extra_pointers && extra_pointers->size() == 1) { + NPC* spawn_npc = std::any_cast(extra_pointers->at(0)); + ExportVar(package_name.c_str(), "despawned", "NPC", spawn_npc); + ExportVar(package_name.c_str(), "despawned_bot_id", spawn_npc->IsBot() ? spawn_npc->CastToBot()->GetBotID() : 0); + ExportVar(package_name.c_str(), "despawned_entity_id", spawn_npc->GetID()); + ExportVar(package_name.c_str(), "despawned_npc_id", spawn_npc->IsNPC() ? spawn_npc->GetNPCTypeID() : 0); + } + break; } @@ -2551,6 +2522,14 @@ void PerlembParser::ExportEventVariables( break; } + case EVENT_ENTER_ZONE: { + if (extra_pointers && extra_pointers->size() == 1) { + ExportVar(package_name.c_str(), "player", "Client", std::any_cast(extra_pointers->at(0))); + } + + break; + } + default: { break; } @@ -2648,6 +2627,7 @@ int PerlembParser::EventBot( nullptr, nullptr, mob, + nullptr, extra_data, false, extra_pointers @@ -2671,6 +2651,7 @@ int PerlembParser::EventGlobalBot( nullptr, nullptr, mob, + nullptr, extra_data, true, extra_pointers @@ -2768,6 +2749,7 @@ int PerlembParser::EventMerc( nullptr, nullptr, mob, + nullptr, extra_data, false, extra_pointers @@ -2791,6 +2773,127 @@ int PerlembParser::EventGlobalMerc( nullptr, nullptr, mob, + nullptr, + extra_data, + true, + extra_pointers + ); +} + +void PerlembParser::LoadZoneScript(std::string filename) +{ + if (!perl || zone_quest_status_ != questUnloaded) { + return; + } + + try { + perl->eval_file("qst_zone", filename.c_str()); + } catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Zone Quest File [{}] Error [{}]", + filename, + e + ) + ); + + zone_quest_status_ = questFailedToLoad; + return; + } + + zone_quest_status_ = questLoaded; +} + +void PerlembParser::LoadGlobalZoneScript(std::string filename) +{ + if (!perl || global_zone_quest_status_ != questUnloaded) { + return; + } + + try { + perl->eval_file("qst_global_zone", filename.c_str()); + } catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Global Zone Quest File [{}] Error [{}]", + filename, + e + ) + ); + + global_zone_quest_status_ = questFailedToLoad; + return; + } + + global_zone_quest_status_ = questLoaded; +} + +bool PerlembParser::ZoneHasQuestSub(QuestEventID event_id) +{ + if ( + !perl || + zone_quest_status_ != questLoaded || + event_id >= _LargestEventID + ) { + return false; + } + + return perl->SubExists("qst_zone", QuestEventSubroutines[event_id]); +} + +bool PerlembParser::GlobalZoneHasQuestSub(QuestEventID event_id) +{ + if ( + !perl || + global_zone_quest_status_ != questLoaded || + event_id >= _LargestEventID + ) { + return false; + } + + return perl->SubExists("qst_global_zone", QuestEventSubroutines[event_id]); +} + +int PerlembParser::EventZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers +) +{ + return EventCommon( + event_id, + 0, + data.c_str(), + nullptr, + nullptr, + nullptr, + nullptr, + zone, + extra_data, + false, + extra_pointers + ); +} + +int PerlembParser::EventGlobalZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers +) +{ + return EventCommon( + event_id, + 0, + data.c_str(), + nullptr, + nullptr, + nullptr, + nullptr, + zone, extra_data, true, extra_pointers diff --git a/zone/embparser.h b/zone/embparser.h index dd4ffe8d8..5817ce15b 100644 --- a/zone/embparser.h +++ b/zone/embparser.h @@ -41,6 +41,23 @@ typedef enum { questFailedToLoad } PerlQuestStatus; +enum class QuestType { + Bot, + BotGlobal, + Item, + ItemGlobal, + Merc, + MercGlobal, + NPC, + NPCGlobal, + Player, + PlayerGlobal, + Spell, + SpellGlobal, + Zone, + ZoneGlobal +}; + class PerlembParser : public QuestInterface { public: PerlembParser(); @@ -136,6 +153,22 @@ public: std::vector* extra_pointers ); + virtual int EventZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + + virtual int EventGlobalZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id); virtual bool HasGlobalQuestSub(QuestEventID event_id); virtual bool PlayerHasQuestSub(QuestEventID event_id); @@ -146,6 +179,8 @@ public: virtual bool GlobalBotHasQuestSub(QuestEventID event_id); virtual bool MercHasQuestSub(QuestEventID event_id); virtual bool GlobalMercHasQuestSub(QuestEventID event_id); + virtual bool ZoneHasQuestSub(QuestEventID event_id); + virtual bool GlobalZoneHasQuestSub(QuestEventID event_id); virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadGlobalNPCScript(std::string filename); @@ -157,6 +192,8 @@ public: virtual void LoadGlobalBotScript(std::string filename); virtual void LoadMercScript(std::string filename); virtual void LoadGlobalMercScript(std::string filename); + virtual void LoadZoneScript(std::string filename); + virtual void LoadGlobalZoneScript(std::string filename); virtual void AddVar(std::string name, std::string val); virtual std::string GetVar(std::string name); @@ -182,6 +219,7 @@ private: EQ::ItemInstance* inst, const SPDat_Spell_Struct* spell, Mob* mob, + Zone* zone, uint32 extra_data, bool is_global, std::vector* extra_pointers @@ -194,59 +232,34 @@ private: Mob* other, Mob* mob, EQ::ItemInstance* inst, - const SPDat_Spell_Struct* spell + const SPDat_Spell_Struct* spell, + Zone* zone ); void MapFunctions(); - void GetQuestTypes( - bool& is_player_quest, - bool& is_global_player_quest, - bool& is_bot_quest, - bool& is_global_bot_quest, - bool& is_merc_quest, - bool& is_global_merc_quest, - bool& is_global_npc_quest, - bool& is_item_quest, - bool& is_spell_quest, + QuestType GetQuestTypes( QuestEventID event, Mob* npc_mob, EQ::ItemInstance* inst, Mob* mob, + Zone* zone, bool is_global ); - void GetQuestPackageName( - bool& is_player_quest, - bool& is_global_player_quest, - bool& is_bot_quest, - bool& is_global_bot_quest, - bool& is_merc_quest, - bool& is_global_merc_quest, - bool& is_global_npc_quest, - bool& is_item_quest, - bool& is_spell_quest, - std::string& package_name, + std::string GetQuestPackageName( + QuestType quest_type, QuestEventID event, uint32 object_id, const char* data, Mob* npc_mob, - EQ::ItemInstance* inst, - bool is_global + EQ::ItemInstance* inst ); void ExportCharID(const std::string& package_name, int& char_id, Mob* npc_mob, Mob* mob); void ExportQGlobals( - bool is_player_quest, - bool is_global_player_quest, - bool is_bot_quest, - bool is_global_bot_quest, - bool is_merc_quest, - bool is_global_merc_quest, - bool is_global_npc_quest, - bool is_item_quest, - bool is_spell_quest, + QuestType quest_type, std::string& package_name, Mob* npc_mob, Mob* mob, @@ -254,15 +267,7 @@ private: ); void ExportMobVariables( - bool is_player_quest, - bool is_global_player_quest, - bool is_bot_quest, - bool is_global_bot_quest, - bool is_merc_quest, - bool is_global_merc_quest, - bool is_global_npc_quest, - bool is_item_quest, - bool is_spell_quest, + QuestType quest_type, std::string& package_name, Mob* mob, Mob* npc_mob @@ -295,6 +300,8 @@ private: PerlQuestStatus global_bot_quest_status_; PerlQuestStatus merc_quest_status_; PerlQuestStatus global_merc_quest_status_; + PerlQuestStatus zone_quest_status_; + PerlQuestStatus global_zone_quest_status_; SV* _empty_sv; diff --git a/zone/entity.cpp b/zone/entity.cpp index b87895001..0b94fc7ac 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -743,6 +743,11 @@ void EntityList::AddNPC(NPC *npc, bool send_spawn_packet, bool dont_queue) npc->DispatchZoneControllerEvent(EVENT_SPAWN_ZONE, npc, "", 0, nullptr); } + if (parse->ZoneHasQuestSub(EVENT_SPAWN_ZONE)) { + std::vector args = { npc }; + parse->EventZone(EVENT_SPAWN_ZONE, zone, "", 0, &args); + } + if (zone->HasMap() && zone->HasWaterMap()) { npc->SetSpawnedInWater(false); if (zone->watermap->InLiquid(npc->GetPosition())) { diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index d44fb2bfa..0f8dded50 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -219,6 +219,7 @@ LuaParser::LuaParser() { SpellArgumentDispatch[i] = handle_spell_null; EncounterArgumentDispatch[i] = handle_encounter_null; BotArgumentDispatch[i] = handle_bot_null; + ZoneArgumentDispatch[i] = handle_zone_null; } NPCArgumentDispatch[EVENT_SAY] = handle_npc_event_say; @@ -409,6 +410,23 @@ LuaParser::LuaParser() { BotArgumentDispatch[EVENT_ENTITY_VARIABLE_SET] = handle_bot_entity_variable; BotArgumentDispatch[EVENT_ENTITY_VARIABLE_UPDATE] = handle_bot_entity_variable; BotArgumentDispatch[EVENT_SPELL_BLOCKED] = handle_bot_spell_blocked; + + ZoneArgumentDispatch[EVENT_CLICK_DOOR] = handle_zone_click_door; + ZoneArgumentDispatch[EVENT_CLICK_OBJECT] = handle_zone_click_object; + ZoneArgumentDispatch[EVENT_DEATH_ZONE] = handle_zone_death; + ZoneArgumentDispatch[EVENT_DESPAWN_ZONE] = handle_zone_despawn; + ZoneArgumentDispatch[EVENT_ENTER_ZONE] = handle_zone_enter; + ZoneArgumentDispatch[EVENT_LOOT_ZONE] = handle_zone_loot; + ZoneArgumentDispatch[EVENT_PAYLOAD] = handle_zone_payload; + ZoneArgumentDispatch[EVENT_PLAYER_PICKUP] = handle_zone_pickup; + ZoneArgumentDispatch[EVENT_POPUP_RESPONSE] = handle_zone_popup; + ZoneArgumentDispatch[EVENT_SIGNAL] = handle_zone_signal; + ZoneArgumentDispatch[EVENT_SPAWN_ZONE] = handle_zone_spawn; + ZoneArgumentDispatch[EVENT_TIMER] = handle_zone_timer; + ZoneArgumentDispatch[EVENT_TIMER_PAUSE] = handle_zone_timer_pause_resume_start; + ZoneArgumentDispatch[EVENT_TIMER_RESUME] = handle_zone_timer_pause_resume_start; + ZoneArgumentDispatch[EVENT_TIMER_START] = handle_zone_timer_pause_resume_start; + ZoneArgumentDispatch[EVENT_TIMER_STOP] = handle_zone_timer_stop; #endif L = nullptr; @@ -489,7 +507,13 @@ int LuaParser::_EventNPC(std::string package_name, QuestEventID evt, NPC* npc, M arg_function(this, L, npc, init, data, extra_data, extra_pointers); Client *c = (init && init->IsClient()) ? init->CastToClient() : nullptr; - quest_manager.StartQuest(npc, c); + RunningQuest q; + + q.owner = npc; + q.initiator = c; + + quest_manager.StartQuest(q); + if(lua_pcall(L, 1, 1, start + 1)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -582,7 +606,13 @@ int LuaParser::_EventPlayer(std::string package_name, QuestEventID evt, Client * auto arg_function = PlayerArgumentDispatch[evt]; arg_function(this, L, client, data, extra_data, extra_pointers); - quest_manager.StartQuest(client, client); + RunningQuest q; + + q.owner = client; + q.initiator = client; + + quest_manager.StartQuest(q); + if(lua_pcall(L, 1, 1, start + 1)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -666,7 +696,14 @@ int LuaParser::_EventItem(std::string package_name, QuestEventID evt, Client *cl auto arg_function = ItemArgumentDispatch[evt]; arg_function(this, L, client, item, mob, data, extra_data, extra_pointers); - quest_manager.StartQuest(client, client, item); + RunningQuest q; + + q.owner = client; + q.initiator = client; + q.questitem = item; + + quest_manager.StartQuest(q); + if(lua_pcall(L, 1, 1, start + 1)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -748,7 +785,14 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, Mob* mob, auto arg_function = SpellArgumentDispatch[evt]; arg_function(this, L, mob, client, spell_id, data, extra_data, extra_pointers); - quest_manager.StartQuest(mob, client, nullptr, const_cast(&spells[spell_id])); + RunningQuest q; + + q.owner = client; + q.initiator = client; + q.questspell = const_cast(&spells[spell_id]); + + quest_manager.StartQuest(q); + if(lua_pcall(L, 1, 1, start + 1)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -814,7 +858,13 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: auto arg_function = EncounterArgumentDispatch[evt]; arg_function(this, L, enc, data, extra_data, extra_pointers); - quest_manager.StartQuest(enc, nullptr, nullptr, nullptr, encounter_name); + RunningQuest q; + + q.owner = enc; + q.encounter = encounter_name; + + quest_manager.StartQuest(q); + if(lua_pcall(L, 1, 1, start + 1)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -1757,7 +1807,13 @@ int LuaParser::_EventBot( arg_function(this, L, bot, init, data, extra_data, extra_pointers); auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr; - quest_manager.StartQuest(bot, c); + RunningQuest q; + + q.owner = bot; + q.initiator = c; + + quest_manager.StartQuest(q); + if(lua_pcall(L, 1, 1, start + 1)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -1936,7 +1992,13 @@ int LuaParser::_EventMerc( arg_function(this, L, merc, init, data, extra_data, extra_pointers); auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr; - quest_manager.StartQuest(merc, c); + RunningQuest q; + + q.owner = merc; + q.initiator = c; + + quest_manager.StartQuest(q); + if(lua_pcall(L, 1, 1, start + 1)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -2039,3 +2101,190 @@ void LuaParser::LoadMercScript(std::string filename) { void LuaParser::LoadGlobalMercScript(std::string filename) { LoadScript(filename, "global_merc"); } + +int LuaParser::EventZone( + QuestEventID evt, + Zone *zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return 0; + } + + if (!zone) { + return 0; + } + + if (!ZoneHasQuestSub(evt)) { + return 0; + } + + return _EventZone("zone", evt, zone, data, extra_data, extra_pointers); +} + +int LuaParser::EventGlobalZone( + QuestEventID evt, + Zone *zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return 0; + } + + if (!zone) { + return 0; + } + + if (!GlobalZoneHasQuestSub(evt)) { + return 0; + } + + return _EventZone("global_zone", evt, zone, data, extra_data, extra_pointers); +} + +int LuaParser::_EventZone( + std::string package_name, + QuestEventID evt, + Zone *zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers, + luabind::adl::object *l_func +) { + const char *sub_name = LuaEvents[evt]; + int start = lua_gettop(L); + + try { + int npop = 2; + PushErrorHandler(L); + if(l_func != nullptr) { + l_func->push(L); + } else { + lua_getfield(L, LUA_REGISTRYINDEX, package_name.c_str()); + lua_getfield(L, -1, sub_name); + npop = 3; + } + + lua_createtable(L, 0, 0); + //push self + Lua_Zone l_zone(zone); + luabind::adl::object l_zone_o = luabind::adl::object(L, l_zone); + l_zone_o.push(L); + lua_setfield(L, -2, "self"); + + auto arg_function = ZoneArgumentDispatch[evt]; + arg_function(this, L, zone, data, extra_data, extra_pointers); + + RunningQuest q; + + q.zone = zone; + + quest_manager.StartQuest(q); + + if(lua_pcall(L, 1, 1, start + 1)) { + std::string error = lua_tostring(L, -1); + AddError(error); + quest_manager.EndQuest(); + lua_pop(L, npop); + return 0; + } + quest_manager.EndQuest(); + + if(lua_isnumber(L, -1)) { + int ret = static_cast(lua_tointeger(L, -1)); + lua_pop(L, npop); + return ret; + } + + lua_pop(L, npop); + } catch(std::exception &ex) { + AddError( + fmt::format( + "Lua Exception | [{}] for Zone [{}] in [{}]: {}", + sub_name, + zone->GetShortName(), + package_name, + ex.what() + ) + ); + + //Restore our stack to the best of our ability + int end = lua_gettop(L); + int n = end - start; + if(n > 0) { + lua_pop(L, n); + } + } + + return 0; +} + +int LuaParser::DispatchEventZone( + QuestEventID evt, + Zone *zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return 0; + } + + std::string package_name = "zone"; + + auto iter = lua_encounter_events_registered.find(package_name); + if (iter == lua_encounter_events_registered.end()) { + return 0; + } + + int ret = 0; + auto riter = iter->second.begin(); + while (riter != iter->second.end()) { + if (riter->event_id == evt) { + package_name = fmt::format("encounter_{}", riter->encounter_name); + int i = _EventZone(package_name, evt, zone, data, extra_data, extra_pointers, &riter->lua_reference); + if (i != 0) { + ret = i; + } + } + + ++riter; + } + + return ret; +} + +bool LuaParser::ZoneHasQuestSub(QuestEventID evt) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return false; + } + + const char *subname = LuaEvents[evt]; + return HasFunction(subname, "zone"); +} + +bool LuaParser::GlobalZoneHasQuestSub(QuestEventID evt) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return false; + } + + const char *subname = LuaEvents[evt]; + return HasFunction(subname, "global_zone"); +} + +void LuaParser::LoadZoneScript(std::string filename) { + LoadScript(filename, "zone"); +} + +void LuaParser::LoadGlobalZoneScript(std::string filename) { + LoadScript(filename, "global_zone"); +} diff --git a/zone/lua_parser.h b/zone/lua_parser.h index fbd50a984..c1bad7088 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -125,6 +125,20 @@ public: uint32 extra_data, std::vector* extra_pointers ); + virtual int EventZone( + QuestEventID evt, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + virtual int EventGlobalZone( + QuestEventID evt, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt); @@ -138,6 +152,8 @@ public: virtual bool GlobalBotHasQuestSub(QuestEventID evt); virtual bool MercHasQuestSub(QuestEventID evt); virtual bool GlobalMercHasQuestSub(QuestEventID evt); + virtual bool ZoneHasQuestSub(QuestEventID evt); + virtual bool GlobalZoneHasQuestSub(QuestEventID evt); virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadGlobalNPCScript(std::string filename); @@ -150,6 +166,8 @@ public: virtual void LoadGlobalBotScript(std::string filename); virtual void LoadMercScript(std::string filename); virtual void LoadGlobalMercScript(std::string filename); + virtual void LoadZoneScript(std::string filename); + virtual void LoadGlobalZoneScript(std::string filename); virtual void AddVar(std::string name, std::string val); virtual std::string GetVar(std::string name); @@ -207,6 +225,13 @@ public: uint32 extra_data, std::vector* extra_pointers ); + virtual int DispatchEventZone( + QuestEventID evt, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); static LuaParser* Instance() { static LuaParser inst; @@ -307,6 +332,15 @@ private: std::vector* extra_pointers, luabind::adl::object* l_func = nullptr ); + int _EventZone( + std::string package_name, + QuestEventID evt, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers, + luabind::adl::object* l_func = nullptr + ); void LoadScript(std::string filename, std::string package_name); void MapFunctions(lua_State *L); @@ -317,12 +351,13 @@ private: std::vector mods_; lua_State *L; - NPCArgumentHandler NPCArgumentDispatch[_LargestEventID]; - PlayerArgumentHandler PlayerArgumentDispatch[_LargestEventID]; - ItemArgumentHandler ItemArgumentDispatch[_LargestEventID]; - SpellArgumentHandler SpellArgumentDispatch[_LargestEventID]; + NPCArgumentHandler NPCArgumentDispatch[_LargestEventID]; + PlayerArgumentHandler PlayerArgumentDispatch[_LargestEventID]; + ItemArgumentHandler ItemArgumentDispatch[_LargestEventID]; + SpellArgumentHandler SpellArgumentDispatch[_LargestEventID]; EncounterArgumentHandler EncounterArgumentDispatch[_LargestEventID]; - BotArgumentHandler BotArgumentDispatch[_LargestEventID]; + BotArgumentHandler BotArgumentDispatch[_LargestEventID]; + ZoneArgumentHandler ZoneArgumentDispatch[_LargestEventID]; }; #endif diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 8d2517fe3..153775e78 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -2873,4 +2873,293 @@ void handle_bot_spell_blocked( lua_setfield(L, -2, "cast_spell"); } +// Zone + +void handle_zone_null( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { +} + +void handle_zone_click_door( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Lua_Door l_door(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_door_o = luabind::adl::object(L, l_door); + l_door_o.push(L); + lua_setfield(L, -2, "door"); + + Lua_Client l_client(std::any_cast(extra_pointers->at(1))); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); +} + +void handle_zone_click_object( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Lua_Object l_object(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_object_o = luabind::adl::object(L, l_object); + l_object_o.push(L); + lua_setfield(L, -2, "object"); + + Lua_Client l_client(std::any_cast(extra_pointers->at(1))); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); +} + +void handle_zone_death( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Seperator sep(data.c_str()); + + Lua_Mob l_mob(std::any_cast(extra_pointers->at(2))); + luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); + l_mob_o.push(L); + lua_setfield(L, -2, "other"); + + lua_pushinteger(L, Strings::ToInt(sep.arg[0])); + lua_setfield(L, -2, "killer_id"); + + lua_pushinteger(L, Strings::ToInt(sep.arg[1])); + lua_setfield(L, -2, "damage"); + + const uint32 spell_id = Strings::ToUnsignedInt(sep.arg[2]); + if (IsValidSpell(spell_id)) { + Lua_Spell l_spell(&spells[spell_id]); + luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell); + l_spell_o.push(L); + lua_setfield(L, -2, "spell"); + } else { + Lua_Spell l_spell(nullptr); + luabind::adl::object l_spell_o = luabind::adl::object(L, l_spell); + l_spell_o.push(L); + lua_setfield(L, -2, "spell"); + } + + lua_pushinteger(L, Strings::ToInt(sep.arg[3])); + lua_setfield(L, -2, "skill_id"); + + lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[4])); + lua_setfield(L, -2, "killed_entity_id"); + + lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[5])); + lua_setfield(L, -2, "combat_start_time"); + + lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[6])); + lua_setfield(L, -2, "combat_end_time"); + + lua_pushinteger(L, Strings::ToBigInt(sep.arg[7])); + lua_setfield(L, -2, "damage_received"); + + lua_pushinteger(L, Strings::ToBigInt(sep.arg[8])); + lua_setfield(L, -2, "healing_received"); + + if (extra_pointers && extra_pointers->size() >= 1) { + Lua_Corpse l_corpse(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse); + l_corpse_o.push(L); + lua_setfield(L, -2, "corpse"); + } + + if (extra_pointers && extra_pointers->size() >= 2) { + Lua_NPC l_npc(std::any_cast(extra_pointers->at(1))); + luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); + l_npc_o.push(L); + lua_setfield(L, -2, "killed"); + } +} + +void handle_zone_despawn( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Lua_NPC l_npc(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); + l_npc_o.push(L); + lua_setfield(L, -2, "other"); +} + +void handle_zone_enter( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Lua_Client l_client(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); +} + +void handle_zone_loot( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Lua_Client l_client(std::any_cast(extra_pointers->at(2))); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); + + Lua_ItemInst l_item(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_item_o = luabind::adl::object(L, l_item); + l_item_o.push(L); + lua_setfield(L, -2, "item"); + + Lua_Corpse l_corpse(std::any_cast(extra_pointers->at(1))); + luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse); + l_corpse_o.push(L); + lua_setfield(L, -2, "corpse"); +} + +void handle_zone_payload( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Seperator sep(data.c_str()); + + lua_pushinteger(L, Strings::ToInt(sep.arg[0])); + lua_setfield(L, -2, "payload_id"); + + lua_pushstring(L, sep.argplus[1]); + lua_setfield(L, -2, "payload_value"); +} + +void handle_zone_pickup( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Lua_ItemInst l_item(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_item_o = luabind::adl::object(L, l_item); + l_item_o.push(L); + lua_setfield(L, -2, "item"); + + Lua_Client l_client(std::any_cast(extra_pointers->at(1))); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); +} + +void handle_zone_popup( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + lua_pushinteger(L, Strings::ToInt(data)); + lua_setfield(L, -2, "popup_id"); + + Lua_Client l_client(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_client_o = luabind::adl::object(L, l_client); + l_client_o.push(L); + lua_setfield(L, -2, "other"); +} + +void handle_zone_signal( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + lua_pushinteger(L, Strings::ToInt(data)); + lua_setfield(L, -2, "signal"); +} + +void handle_zone_spawn( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Lua_NPC l_npc(std::any_cast(extra_pointers->at(0))); + luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); + l_npc_o.push(L); + lua_setfield(L, -2, "other"); +} + +void handle_zone_timer( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + lua_pushstring(L, data.c_str()); + lua_setfield(L, -2, "timer"); +} + +void handle_zone_timer_pause_resume_start( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + Seperator sep(data.c_str()); + + lua_pushstring(L, sep.arg[0]); + lua_setfield(L, -2, "timer"); + + lua_pushinteger(L, Strings::ToUnsignedInt(sep.arg[1])); + lua_setfield(L, -2, "duration"); +} + +void handle_zone_timer_stop( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + lua_pushstring(L, data.c_str()); + lua_setfield(L, -2, "timer"); +} + #endif diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 3cb428c58..0adb7ba99 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -9,6 +9,7 @@ typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, typedef void(*EncounterArgumentHandler)(QuestInterface*, lua_State*, Encounter* encounter, std::string, uint32, std::vector*); typedef void(*BotArgumentHandler)(QuestInterface*, lua_State*, Bot*, Mob*, std::string, uint32, std::vector*); typedef void(*MercArgumentHandler)(QuestInterface*, lua_State*, Merc*, Mob*, std::string, uint32, std::vector*); +typedef void(*ZoneArgumentHandler)(QuestInterface*, lua_State*, Zone*, std::string, uint32, std::vector*); // NPC void handle_npc_event_say( @@ -1278,5 +1279,141 @@ void handle_bot_spell_blocked( std::vector *extra_pointers ); +// Zone +void handle_zone_null( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_click_door( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_click_object( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_death( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_despawn( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_enter( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_loot( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_payload( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_pickup( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_popup( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_signal( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_spawn( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_timer( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_timer_pause_resume_start( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + +void handle_zone_timer_stop( + QuestInterface *parse, + lua_State* L, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +); + #endif #endif diff --git a/zone/lua_zone.cpp b/zone/lua_zone.cpp index 8250296ff..3907a3ed9 100644 --- a/zone/lua_zone.cpp +++ b/zone/lua_zone.cpp @@ -772,6 +772,72 @@ bool Lua_Zone::VariableExists(const std::string& variable_name) return self->VariableExists(variable_name); } +uint32 Lua_Zone::GetTimerDuration(std::string name) +{ + Lua_Safe_Call_Int(); + return self->GetTimerDuration(name); +} + +uint32 Lua_Zone::GetTimerRemainingTime(std::string name) +{ + Lua_Safe_Call_Int(); + return self->GetTimerRemainingTime(name); +} + +bool Lua_Zone::HasTimer(std::string name) +{ + Lua_Safe_Call_Bool(); + return self->HasTimer(name); +} + +bool Lua_Zone::IsPausedTimer(std::string name) +{ + Lua_Safe_Call_Bool(); + return self->IsPausedTimer(name); +} + +void Lua_Zone::PauseTimer(std::string name) +{ + Lua_Safe_Call_Void(); + self->PauseTimer(name); +} + +void Lua_Zone::ResumeTimer(std::string name) +{ + Lua_Safe_Call_Void(); + self->ResumeTimer(name); +} + +void Lua_Zone::SetTimer(std::string name, uint32 duration) +{ + Lua_Safe_Call_Void(); + self->SetTimer(name, duration); +} + +void Lua_Zone::StopTimer(std::string name) +{ + Lua_Safe_Call_Void(); + self->StopTimer(name); +} + +void Lua_Zone::StopAllTimers() +{ + Lua_Safe_Call_Void(); + self->StopAllTimers(); +} + +void Lua_Zone::SendPayload(int payload_id, std::string payload_value) +{ + Lua_Safe_Call_Void(); + self->SendPayload(payload_id, payload_value); +} + +void Lua_Zone::Signal(int signal_id) +{ + Lua_Safe_Call_Void(); + self->Signal(signal_id); +} + luabind::scope lua_register_zone() { return luabind::class_("Zones") .def(luabind::constructor<>()) @@ -861,6 +927,8 @@ luabind::scope lua_register_zone() { .def("GetSnowDuration", (int(Lua_Zone::*)(uint8))&Lua_Zone::GetSnowDuration) .def("GetTimeType", &Lua_Zone::GetTimeType) .def("GetTimeZone", &Lua_Zone::GetTimeZone) + .def("GetTimerDuration", &Lua_Zone::GetTimerDuration) + .def("GetTimerRemainingTime", &Lua_Zone::GetTimerRemainingTime) .def("GetZoneDescription", &Lua_Zone::GetZoneDescription) .def("GetZoneID", &Lua_Zone::GetZoneID) .def("GetZoneType", &Lua_Zone::GetZoneType) @@ -875,10 +943,12 @@ luabind::scope lua_register_zone() { .def("HasMap", &Lua_Zone::HasMap) .def("HasWaterMap", &Lua_Zone::HasWaterMap) .def("HasWeather", &Lua_Zone::HasWeather) + .def("HasTimer", &Lua_Zone::HasTimer) .def("IsCity", &Lua_Zone::IsCity) .def("IsHotzone", &Lua_Zone::IsHotzone) .def("IsInstancePersistent", &Lua_Zone::IsInstancePersistent) .def("IsIdleWhenEmpty", &Lua_Zone::IsIdleWhenEmpty) + .def("IsPausedTimer", &Lua_Zone::IsPausedTimer) .def("IsPVPZone", &Lua_Zone::IsPVPZone) .def("IsRaining", &Lua_Zone::IsRaining) .def("IsSnowing", &Lua_Zone::IsSnowing) @@ -887,8 +957,11 @@ luabind::scope lua_register_zone() { .def("IsStaticZone", &Lua_Zone::IsStaticZone) .def("IsUCSServerAvailable", &Lua_Zone::IsUCSServerAvailable) .def("IsWaterZone", &Lua_Zone::IsWaterZone) + .def("PauseTimer", &Lua_Zone::PauseTimer) .def("Repop", (void(Lua_Zone::*)(void))&Lua_Zone::Repop) .def("Repop", (void(Lua_Zone::*)(bool))&Lua_Zone::Repop) + .def("ResumeTimer", &Lua_Zone::ResumeTimer) + .def("SendPayload", &Lua_Zone::SendPayload) .def("SetAAEXPModifier", &Lua_Zone::SetAAEXPModifier) .def("SetAAEXPModifierByCharacterID", &Lua_Zone::SetAAEXPModifierByCharacterID) .def("SetBucket", (void(Lua_Zone::*)(const std::string&,const std::string&))&Lua_Zone::SetBucket) @@ -898,8 +971,12 @@ luabind::scope lua_register_zone() { .def("SetInstanceTimer", &Lua_Zone::SetInstanceTimer) .def("SetInstanceTimeRemaining", &Lua_Zone::SetInstanceTimeRemaining) .def("SetIsHotzone", &Lua_Zone::SetIsHotzone) + .def("SetTimer", &Lua_Zone::SetTimer) .def("SetVariable", &Lua_Zone::SetVariable) .def("ShowZoneGlobalLoot", &Lua_Zone::ShowZoneGlobalLoot) + .def("Signal", &Lua_Zone::Signal) + .def("StopTimer", &Lua_Zone::StopTimer) + .def("StopAllTimers", &Lua_Zone::StopAllTimers) .def("VariableExists", &Lua_Zone::VariableExists); } diff --git a/zone/lua_zone.h b/zone/lua_zone.h index 48d297bb6..b1a5390c2 100644 --- a/zone/lua_zone.h +++ b/zone/lua_zone.h @@ -147,6 +147,17 @@ public: luabind::object GetVariables(lua_State* L); void SetVariable(const std::string& variable_name, const std::string& variable_value); bool VariableExists(const std::string& variable_name); + uint32 GetTimerDuration(std::string name); + uint32 GetTimerRemainingTime(std::string name); + bool HasTimer(std::string name); + bool IsPausedTimer(std::string name); + void PauseTimer(std::string name); + void ResumeTimer(std::string name); + void SetTimer(std::string name, uint32 duration); + void StopTimer(std::string name); + void StopAllTimers(); + void Signal(int signal_id); + void SendPayload(int payload_id, std::string payload_value); // data buckets void SetBucket(const std::string& bucket_name, const std::string& bucket_value); diff --git a/zone/npc.cpp b/zone/npc.cpp index ade7c8e93..1c70da2c4 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -864,6 +864,11 @@ void NPC::Depop(bool start_spawn_timer) { DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr); } + if (parse->ZoneHasQuestSub(EVENT_DESPAWN_ZONE)) { + std::vector args = { this }; + parse->EventZone(EVENT_DESPAWN_ZONE, zone, "", 0, &args); + } + p_depop = true; if (respawn2) { if (start_spawn_timer) { diff --git a/zone/object.cpp b/zone/object.cpp index eb6daad49..6da171b23 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -657,6 +657,30 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) } } + if (parse->ZoneHasQuestSub(EVENT_PLAYER_PICKUP)) { + std::vector args = { m_inst, sender }; + + if (parse->EventZone(EVENT_PLAYER_PICKUP, zone, std::to_string(item->ID), GetID(), &args)) { + auto outapp = new EQApplicationPacket(OP_ClickObject, sizeof(ClickObject_Struct)); + + memcpy(outapp->pBuffer, click_object, sizeof(ClickObject_Struct)); + + auto co = (ClickObject_Struct*) outapp->pBuffer; + + co->drop_id = 0; + + entity_list.QueueClients(nullptr, outapp, false); + + safe_delete(outapp); + + sender->SetTradeskillObject(nullptr); + + user = nullptr; + + return true; + } + } + // Transfer item to client sender->PutItemInInventory(EQ::invslot::slotCursor, *m_inst, false); sender->SendItemPacket(EQ::invslot::slotCursor, m_inst, ItemPacketTrade); diff --git a/zone/perl_zone.cpp b/zone/perl_zone.cpp index 6cb49f557..7783c6020 100644 --- a/zone/perl_zone.cpp +++ b/zone/perl_zone.cpp @@ -598,6 +598,61 @@ bool Perl_Zone_VariableExists(Zone* self, const std::string variable_name) return self->VariableExists(variable_name); } +uint32 Perl_Zone_GetTimerDuration(Zone* self, std::string name) +{ + return self->GetTimerDuration(name); +} + +uint32 Perl_Zone_GetTimerRemainingTime(Zone* self, std::string name) +{ + return self->GetTimerRemainingTime(name); +} + +bool Perl_Zone_HasTimer(Zone* self, std::string name) +{ + return self->HasTimer(name); +} + +bool Perl_Zone_IsPausedTimer(Zone* self, std::string name) +{ + return self->IsPausedTimer(name); +} + +void Perl_Zone_PauseTimer(Zone* self, std::string name) +{ + self->PauseTimer(name); +} + +void Perl_Zone_ResumeTimer(Zone* self, std::string name) +{ + self->ResumeTimer(name); +} + +void Perl_Zone_SetTimer(Zone* self, std::string name, uint32 duration) +{ + self->SetTimer(name, duration); +} + +void Perl_Zone_StopTimer(Zone* self, std::string name) +{ + self->StopTimer(name); +} + +void Perl_Zone_StopAllTimers(Zone* self) +{ + self->StopAllTimers(); +} + +void Perl_Zone_SendPayload(Zone* self, int payload_id, std::string payload_value) +{ + self->SendPayload(payload_id, payload_value); +} + +void Perl_Zone_Signal(Zone* self, int signal_id) +{ + self->Signal(signal_id); +} + void perl_register_zone() { perl::interpreter perl(PERL_GET_THX); @@ -689,6 +744,8 @@ void perl_register_zone() package.add("GetSnowDuration", (int(*)(Zone*, uint8))&Perl_Zone_GetSnowDuration); package.add("GetTimeType", &Perl_Zone_GetTimeType); package.add("GetTimeZone", &Perl_Zone_GetTimeZone); + package.add("GetTimerDuration", &Perl_Zone_GetTimerDuration); + package.add("GetTimerRemainingTime", &Perl_Zone_GetTimerRemainingTime); package.add("GetZoneDescription", &Perl_Zone_GetZoneDescription); package.add("GetZoneID", &Perl_Zone_GetZoneID); package.add("GetZoneType", &Perl_Zone_GetZoneType); @@ -701,12 +758,14 @@ void perl_register_zone() package.add("GetZoneTotalBlockedSpells", &Perl_Zone_GetZoneTotalBlockedSpells); package.add("HasGraveyard", &Perl_Zone_HasGraveyard); package.add("HasMap", &Perl_Zone_HasMap); + package.add("HasTimer", &Perl_Zone_HasTimer); package.add("HasWaterMap", &Perl_Zone_HasWaterMap); package.add("HasWeather", &Perl_Zone_HasWeather); package.add("IsCity", &Perl_Zone_IsCity); package.add("IsHotzone", &Perl_Zone_IsHotzone); package.add("IsInstancePersistent", &Perl_Zone_IsInstancePersistent); package.add("IsIdleWhenEmpty", &Perl_Zone_IsIdleWhenEmpty); + package.add("IsPausedTimer", &Perl_Zone_IsPausedTimer); package.add("IsPVPZone", &Perl_Zone_IsPVPZone); package.add("IsRaining", &Perl_Zone_IsRaining); package.add("IsSnowing", &Perl_Zone_IsSnowing); @@ -715,8 +774,11 @@ void perl_register_zone() package.add("IsStaticZone", &Perl_Zone_IsStaticZone); package.add("IsUCSServerAvailable", &Perl_Zone_IsUCSServerAvailable); package.add("IsWaterZone", &Perl_Zone_IsWaterZone); + package.add("PauseTimer", &Perl_Zone_PauseTimer); package.add("Repop", (void(*)(Zone*))&Perl_Zone_Repop); package.add("Repop", (void(*)(Zone*, bool))&Perl_Zone_Repop); + package.add("ResumeTimer", &Perl_Zone_ResumeTimer); + package.add("SendPayload", &Perl_Zone_SendPayload); package.add("SetAAEXPModifier", &Perl_Zone_SetAAEXPModifier); package.add("SetAAEXPModifierByCharacterID", &Perl_Zone_SetAAEXPModifierByCharacterID); package.add("SetBucket", (void(*)(Zone*, const std::string, const std::string))&Perl_Zone_SetBucket); @@ -726,7 +788,11 @@ void perl_register_zone() package.add("SetInstanceTimer", &Perl_Zone_SetInstanceTimer); package.add("SetInstanceTimeRemaining", &Perl_Zone_SetInstanceTimeRemaining); package.add("SetIsHotzone", &Perl_Zone_SetIsHotzone); + package.add("SetTimer", &Perl_Zone_SetTimer); package.add("SetVariable", &Perl_Zone_SetVariable); + package.add("Signal", &Perl_Zone_Signal); + package.add("StopTimer", &Perl_Zone_StopTimer); + package.add("StopAllTimers", &Perl_Zone_StopAllTimers); package.add("ShowZoneGlobalLoot", &Perl_Zone_ShowZoneGlobalLoot); package.add("VariableExists", &Perl_Zone_VariableExists); } diff --git a/zone/quest_interface.h b/zone/quest_interface.h index 687a90f58..f18e1dbec 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -163,6 +163,28 @@ public: return 0; } + virtual int EventZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ) + { + return 0; + } + + virtual int EventGlobalZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ) + { + return 0; + } + virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id) { return false; @@ -223,6 +245,16 @@ public: return false; } + virtual bool ZoneHasQuestSub(QuestEventID event_id) + { + return false; + } + + virtual bool GlobalZoneHasQuestSub(QuestEventID event_id) + { + return false; + } + virtual void LoadNPCScript(std::string filename, int npc_id) { } virtual void LoadGlobalNPCScript(std::string filename) { } virtual void LoadPlayerScript(std::string filename) { } @@ -234,6 +266,8 @@ public: virtual void LoadGlobalBotScript(std::string filename) { } virtual void LoadMercScript(std::string filename) { } virtual void LoadGlobalMercScript(std::string filename) { } + virtual void LoadZoneScript(std::string filename) { } + virtual void LoadGlobalZoneScript(std::string filename) { } virtual int DispatchEventNPC( QuestEventID event_id, @@ -308,6 +342,17 @@ public: return 0; } + virtual int DispatchEventZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ) + { + return 0; + } + virtual void AddVar(std::string name, std::string val) { } virtual std::string GetVar(std::string name) { diff --git a/zone/quest_parser_collection.cpp b/zone/quest_parser_collection.cpp index 2ab537c64..9535cb89a 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -49,6 +49,8 @@ QuestParserCollection::QuestParserCollection() _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() { } @@ -85,6 +87,7 @@ void QuestParserCollection::ReloadQuests(bool reset_timers) { if (reset_timers) { quest_manager.ClearAllTimers(); + zone->StopAllTimers(); } MapOpcodes(); @@ -98,6 +101,8 @@ void QuestParserCollection::ReloadQuests(bool reset_timers) _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(); @@ -426,6 +431,49 @@ 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, @@ -924,6 +972,83 @@ int QuestParserCollection::EventMercGlobal( 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) { @@ -1425,6 +1550,86 @@ QuestInterface* QuestParserCollection::GetQIByGlobalMercQuest(std::string& filen 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(); @@ -1561,6 +1766,26 @@ int QuestParserCollection::DispatchEventMerc( 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++) { diff --git a/zone/quest_parser_collection.h b/zone/quest_parser_collection.h index cb0a5e8d1..5d7d1d3c0 100644 --- a/zone/quest_parser_collection.h +++ b/zone/quest_parser_collection.h @@ -72,6 +72,7 @@ public: bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); bool BotHasQuestSub(QuestEventID event_id); bool MercHasQuestSub(QuestEventID event_id); + bool ZoneHasQuestSub(QuestEventID event_id); int EventNPC( QuestEventID event_id, @@ -172,6 +173,14 @@ public: std::vector* extra_pointers = nullptr ); + int EventZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data = 0, + std::vector* extra_pointers = nullptr + ); + void GetErrors(std::list &quest_errors); /* @@ -209,6 +218,8 @@ private: bool BotHasQuestSubGlobal(QuestEventID event_id); bool MercHasQuestSubLocal(QuestEventID event_id); bool MercHasQuestSubGlobal(QuestEventID event_id); + bool ZoneHasQuestSubLocal(QuestEventID event_id); + bool ZoneHasQuestSubGlobal(QuestEventID event_id); int EventNPCLocal( QuestEventID event_id, @@ -280,6 +291,22 @@ private: std::vector* extra_pointers ); + int EventZoneLocal( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + + int EventZoneGlobal( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + QuestInterface* GetQIByNPCQuest(uint32 npc_id, std::string& filename); QuestInterface* GetQIByGlobalNPCQuest(std::string& filename); QuestInterface* GetQIByPlayerQuest(std::string& filename); @@ -291,6 +318,8 @@ private: QuestInterface* GetQIByGlobalBotQuest(std::string& filename); QuestInterface* GetQIByMercQuest(std::string& filename); QuestInterface* GetQIByGlobalMercQuest(std::string& filename); + QuestInterface* GetQIByZoneQuest(std::string& filename); + QuestInterface* GetQIByGlobalZoneQuest(std::string& filename); int DispatchEventNPC( QuestEventID event_id, @@ -347,6 +376,14 @@ private: std::vector* extra_pointers ); + int DispatchEventZone( + QuestEventID event_id, + Zone* zone, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + std::map _interfaces; std::map _extensions; std::list _load_precedence; @@ -359,6 +396,8 @@ private: uint32 _global_bot_quest_status; uint32 _merc_quest_status; uint32 _global_merc_quest_status; + uint32 _zone_quest_status; + uint32 _global_zone_quest_status; std::map _spell_quest_status; std::map _item_quest_status; std::map _encounter_quest_status; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index ac17e68d3..288b87ab8 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -64,10 +64,10 @@ QuestManager quest_manager; EQ::ItemInstance* questitem = nullptr; \ const SPDat_Spell_Struct* questspell = nullptr; \ bool depop_npc = false; \ - std::string encounter; \ + std::string encounter = ""; \ do { \ - if(!quests_running_.empty()) { \ - running_quest e = quests_running_.top(); \ + if(!m_running_quests.empty()) { \ + RunningQuest e = m_running_quests.top(); \ owner = e.owner; \ initiator = e.initiator; \ questitem = e.questitem; \ @@ -124,33 +124,30 @@ void QuestManager::Process() { } } -void QuestManager::StartQuest(Mob *_owner, Client *_initiator, EQ::ItemInstance* _questitem, const SPDat_Spell_Struct* _questspell, std::string encounter) { - running_quest run; - run.owner = _owner; - run.initiator = _initiator; - run.questitem = _questitem; - run.questspell = _questspell; - run.depop_npc = false; - run.encounter = encounter; - quests_running_.push(run); +void QuestManager::StartQuest(const RunningQuest& q) +{ + m_running_quests.push(q); } void QuestManager::EndQuest() { - running_quest run = quests_running_.top(); - if(run.depop_npc && run.owner->IsNPC()) { + RunningQuest run = m_running_quests.top(); + + if (run.depop_npc && run.owner->IsNPC()) { //clear out any timers for them... std::list::iterator cur = QTimerList.begin(), end; end = QTimerList.end(); while (cur != end) { - if (cur->mob == run.owner) + if (cur->mob == run.owner) { cur = QTimerList.erase(cur); - else + } else { ++cur; + } } run.owner->Depop(); } - quests_running_.pop(); + + m_running_quests.pop(); } void QuestManager::ClearAllTimers() { @@ -1098,18 +1095,18 @@ void QuestManager::depop(int npc_type) { tmp->CastToNPC()->Depop(); } else { - running_quest e = quests_running_.top(); + RunningQuest e = m_running_quests.top(); e.depop_npc = true; - quests_running_.pop(); - quests_running_.push(e); + m_running_quests.pop(); + m_running_quests.push(e); } } } else { //depop self - running_quest e = quests_running_.top(); + RunningQuest e = m_running_quests.top(); e.depop_npc = true; - quests_running_.pop(); - quests_running_.push(e); + m_running_quests.pop(); + m_running_quests.push(e); } } } @@ -1606,7 +1603,7 @@ void QuestManager::save() { void QuestManager::faction(int faction_id, int faction_value, int temp) { QuestManagerCurrentQuestVars(); - running_quest run = quests_running_.top(); + RunningQuest run = m_running_quests.top(); if(run.owner->IsCharmed() == false && initiator) { if(faction_id != 0 && faction_value != 0) { initiator->SetFactionLevel2( @@ -2077,10 +2074,10 @@ void QuestManager::respawn(int npcTypeID, int grid) { if (!owner || !owner->IsNPC()) return; - running_quest e = quests_running_.top(); + RunningQuest e = m_running_quests.top(); e.depop_npc = true; - quests_running_.pop(); - quests_running_.push(e); + m_running_quests.pop(); + m_running_quests.push(e); const NPCType* npcType = nullptr; if ((npcType = content_db.LoadNPCTypesData(npcTypeID))) @@ -3980,81 +3977,90 @@ void QuestManager::ReloadZoneStaticData() } } -Client *QuestManager::GetInitiator() const { - if(!quests_running_.empty()) { - running_quest e = quests_running_.top(); +Client* QuestManager::GetInitiator() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return e.initiator; } return nullptr; } -NPC *QuestManager::GetNPC() const { - if(!quests_running_.empty()) { - running_quest e = quests_running_.top(); +NPC* QuestManager::GetNPC() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return (e.owner && e.owner->IsNPC()) ? e.owner->CastToNPC() : nullptr; } return nullptr; } -Bot *QuestManager::GetBot() const { - if (!quests_running_.empty()) { - running_quest e = quests_running_.top(); +Bot* QuestManager::GetBot() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return (e.owner && e.owner->IsBot()) ? e.owner->CastToBot() : nullptr; } return nullptr; } -Merc *QuestManager::GetMerc() const { - if (!quests_running_.empty()) { - running_quest e = quests_running_.top(); +Merc* QuestManager::GetMerc() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return (e.owner && e.owner->IsMerc()) ? e.owner->CastToMerc() : nullptr; } return nullptr; } -Mob *QuestManager::GetOwner() const { - if(!quests_running_.empty()) { - running_quest e = quests_running_.top(); +Mob* QuestManager::GetOwner() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return e.owner; } return nullptr; } -EQ::InventoryProfile *QuestManager::GetInventory() const { - if(!quests_running_.empty()) { - running_quest e = quests_running_.top(); +EQ::InventoryProfile* QuestManager::GetInventory() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return &e.initiator->GetInv(); } return nullptr; } -EQ::ItemInstance *QuestManager::GetQuestItem() const { - if(!quests_running_.empty()) { - running_quest e = quests_running_.top(); +EQ::ItemInstance* QuestManager::GetQuestItem() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return e.questitem; } return nullptr; } -const SPDat_Spell_Struct *QuestManager::GetQuestSpell() { - if(!quests_running_.empty()) { - running_quest e = quests_running_.top(); +const SPDat_Spell_Struct* QuestManager::GetQuestSpell() +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return e.questspell; } return nullptr; } -std::string QuestManager::GetEncounter() const { - if(!quests_running_.empty()) { - running_quest e = quests_running_.top(); +std::string QuestManager::GetEncounter() const +{ + if (!m_running_quests.empty()) { + RunningQuest e = m_running_quests.top(); return e.encounter; } diff --git a/zone/questmgr.h b/zone/questmgr.h index 0e3cd4a4f..b85e6e330 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -33,15 +33,17 @@ namespace EQ class ItemInstance; } +struct RunningQuest { + Mob* owner = nullptr; + Client* initiator = nullptr; + EQ::ItemInstance* questitem = nullptr; + const SPDat_Spell_Struct* questspell = nullptr; + bool depop_npc = false; + std::string encounter = ""; + Zone* zone = nullptr; +}; + class QuestManager { - struct running_quest { - Mob *owner; - Client *initiator; - EQ::ItemInstance* questitem; - const SPDat_Spell_Struct* questspell; - bool depop_npc; - std::string encounter; - }; struct PausedTimer { Mob* owner; @@ -49,12 +51,13 @@ class QuestManager { uint32 time; }; public: + QuestManager(); virtual ~QuestManager(); - void StartQuest(Mob *_owner, Client *_initiator = nullptr, EQ::ItemInstance* _questitem = nullptr, const SPDat_Spell_Struct* _questspell = nullptr, std::string encounter = ""); + void StartQuest(const RunningQuest& q); void EndQuest(); - bool QuestsRunning() { return !quests_running_.empty(); } + bool QuestsRunning() { return !m_running_quests.empty(); } void Process(); @@ -381,7 +384,7 @@ public: bool handin(std::map required); private: - std::stack quests_running_; + std::stack m_running_quests; bool HaveProximitySays; diff --git a/zone/zone.cpp b/zone/zone.cpp index 37216b046..29ee85867 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -1693,6 +1693,25 @@ bool Zone::Process() { } } + const bool has_timer_event = parse->ZoneHasQuestSub(EVENT_TIMER); + + for (auto e : zone_timers) { + if (e.timer_.Enabled() && e.timer_.Check()) { + if (has_timer_event) { + parse->EventZone(EVENT_TIMER, this, e.name); + } + } + } + + if (!m_zone_signals.empty()) { + int signal_id = m_zone_signals.front(); + m_zone_signals.pop_front(); + + if (parse->ZoneHasQuestSub(EVENT_SIGNAL)) { + parse->EventZone(EVENT_SIGNAL, this, std::to_string(signal_id), 0); + } + } + mMovementManager->Process(); return true; @@ -1935,6 +1954,8 @@ void Zone::Repop(bool is_forced) quest_manager.ClearAllTimers(); + StopAllTimers(); + LogInfo("Loading spawn groups"); if (!content_db.LoadSpawnGroups(short_name, GetInstanceVersion(), &spawn_group_list)) { LogError("Loading spawn groups failed"); @@ -3301,4 +3322,241 @@ void Zone::ReloadMaps() pathing = IPathfinder::Load(map_name); } +uint32 Zone::GetTimerDuration(std::string name) +{ + if (!IsLoaded() || zone_timers.empty()) { + return 0; + } + + const auto& e = std::find_if( + zone_timers.begin(), + zone_timers.end(), + [&name](ZoneTimer e) { + return e.name == name; + } + ); + + return e != zone_timers.end() ? e->timer_.GetDuration() : 0; +} + +uint32 Zone::GetTimerRemainingTime(std::string name) +{ + if (!IsLoaded() || zone_timers.empty()) { + return 0; + } + + const auto& e = std::find_if( + zone_timers.begin(), + zone_timers.end(), + [&name](ZoneTimer e) { + return e.name == name; + } + ); + + return e != zone_timers.end() ? e->timer_.GetRemainingTime() : 0; +} + +bool Zone::HasTimer(std::string name) +{ + if (!IsLoaded() || zone_timers.empty()) { + return false; + } + + const auto& e = std::find_if( + zone_timers.begin(), + zone_timers.end(), + [&name](ZoneTimer e) { + return e.name == name; + } + ); + + return e != zone_timers.end(); +} + +bool Zone::IsPausedTimer(std::string name) +{ + if (!IsLoaded() || paused_zone_timers.empty()) { + return false; + } + + const auto& e = std::find_if( + paused_zone_timers.begin(), + paused_zone_timers.end(), + [&name](PausedZoneTimer e) { + return e.name == name; + } + ); + + return e != paused_zone_timers.end(); +} + +void Zone::PauseTimer(std::string name) +{ + if ( + !IsLoaded() || + zone_timers.empty() || + !HasTimer(name) || + IsPausedTimer(name) + ) { + return; + } + + uint32 remaining_time = 0; + + const bool has_pause_event = parse->ZoneHasQuestSub(EVENT_TIMER_PAUSE); + + if (!zone_timers.empty()) { + for (auto e = zone_timers.begin(); e != zone_timers.end(); e++) { + if (e->name == name) { + remaining_time = e->timer_.GetRemainingTime(); + + zone_timers.erase(e); + + const std::string& export_string = fmt::format( + "{} {}", + name, + remaining_time + ); + + LogQuests( + "Pausing timer [{}] with [{}] ms remaining", + name, + remaining_time + ); + + paused_zone_timers.emplace_back( + PausedZoneTimer{ + .name = name, + .remaining_time = remaining_time + } + ); + + if (has_pause_event) { + parse->EventZone(EVENT_TIMER_PAUSE, this, export_string); + } + + break; + } + } + } +} + +void Zone::ResumeTimer(std::string name) +{ + if ( + !IsLoaded() || + paused_zone_timers.empty() || + !IsPausedTimer(name) + ) { + return; + } + + uint32 remaining_time = 0; + + if (!paused_zone_timers.empty()) { + for (auto e = paused_zone_timers.begin(); e != paused_zone_timers.end(); e++) { + if (e->name == name) { + remaining_time = e->remaining_time; + + paused_zone_timers.erase(e); + + if (!remaining_time) { + LogQuests("Paused timer [{}] not found or has expired.", name); + return; + } + + const std::string& export_string = fmt::format( + "{} {}", + name, + remaining_time + ); + + LogQuests( + "Creating a new timer and resuming [{}] with [{}] ms remaining", + name, + remaining_time + ); + + zone_timers.emplace_back(ZoneTimer(name, remaining_time)); + + if (parse->ZoneHasQuestSub(EVENT_TIMER_RESUME)) { + parse->EventZone(EVENT_TIMER_RESUME, this, export_string); + } + + break; + } + } + } +} + +void Zone::SetTimer(std::string name, uint32 duration) +{ + if (!IsLoaded() || HasTimer(name)) { + return; + } + + zone_timers.emplace_back(ZoneTimer(name, duration)); + + if (parse->ZoneHasQuestSub(EVENT_TIMER_START)) { + const std::string& export_string = fmt::format("{} {}", name, duration); + parse->EventZone(EVENT_TIMER_START, this, export_string); + } +} + +void Zone::StopTimer(std::string name) +{ + if ( + !IsLoaded() || + zone_timers.empty() || + !HasTimer(name) || + IsPausedTimer(name) + ) { + return; + } + + const bool has_stop_event = parse->ZoneHasQuestSub(EVENT_TIMER_STOP); + + for (auto e = zone_timers.begin(); e != zone_timers.end(); e++) { + if (e->name == name) { + if (has_stop_event) { + parse->EventZone(EVENT_TIMER_STOP, this, name); + } + + zone_timers.erase(e); + break; + } + } +} + +void Zone::StopAllTimers() +{ + if (!IsLoaded() || zone_timers.empty()) { + return; + } + + const bool has_stop_event = parse->ZoneHasQuestSub(EVENT_TIMER_STOP); + + for (auto e = zone_timers.begin(); e != zone_timers.end();) { + if (has_stop_event) { + parse->EventZone(EVENT_TIMER_STOP, this, e->name); + } + + e = zone_timers.erase(e); + } +} + +void Zone::Signal(int signal_id) +{ + m_zone_signals.push_back(signal_id); +} + +void Zone::SendPayload(int payload_id, std::string payload_value) +{ + if (parse->ZoneHasQuestSub(EVENT_PAYLOAD)) { + const auto& export_string = fmt::format("{} {}", payload_id, payload_value); + + parse->EventZone(EVENT_PAYLOAD, this, export_string, 0); + } +} + #include "zone_loot.cpp" diff --git a/zone/zone.h b/zone/zone.h index 237c7fdb0..60c291abf 100755 --- a/zone/zone.h +++ b/zone/zone.h @@ -486,6 +486,24 @@ public: static void ClearZoneState(uint32 zone_id, uint32 instance_id); void ReloadMaps(); + void Signal(int signal_id); + void SendPayload(int payload_id, std::string payload_value); + + struct PausedZoneTimer { + std::string name; + uint32 remaining_time; + }; + + uint32 GetTimerDuration(std::string name); + uint32 GetTimerRemainingTime(std::string name); + bool HasTimer(std::string name); + bool IsPausedTimer(std::string name); + void PauseTimer(std::string name); + void ResumeTimer(std::string name); + void SetTimer(std::string name, uint32 duration); + void StopTimer(std::string name); + void StopAllTimers(); + private: bool allow_mercs; bool can_bind; @@ -552,6 +570,18 @@ private: std::vector m_base_data = { }; uint32_t m_zone_server_id = 0; + + class ZoneTimer { + public: + inline ZoneTimer(std::string _name, uint32 duration) + : name(_name), timer_(duration) { timer_.Start(duration, false); } + std::string name; + Timer timer_; + }; + + std::vector zone_timers; + std::vector paused_zone_timers; + std::deque m_zone_signals; }; #endif