From 448a33a60cd01f923a1e6a92ab061ab6eda3ae69 Mon Sep 17 00:00:00 2001 From: Alex King <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 10 Oct 2024 21:29:29 -0400 Subject: [PATCH] [Quest API] Add Scripting Support to Mercenaries (#4500) * [Quest API] Add Scripting Support to Mercenaries * Cleanup * Cleanup * Update lua_merc.h * Update mob.cpp * XYZH * Final * Update attack.cpp * Update attack.cpp * Simplify event invocation * Inline example * Nullptr init example * EVENT_TIMER simplify add EventPlayerNpcBotMerc * EVENT_TIMER_START * Remove has_start_event * EVENT_TIMER_START with settimerMS * EVENT_POPUP_RESPONSE * Consolidation * Update attack.cpp * Push * Update quest_parser_collection.h * Comments * Cleanup per comments --------- Co-authored-by: Akkadius --- zone/CMakeLists.txt | 3 + zone/attack.cpp | 257 +++++++++---------------- zone/client.cpp | 49 ++--- zone/client_packet.cpp | 10 +- zone/embparser.cpp | 263 +++++++++++++++++++------ zone/embparser.h | 32 ++++ zone/entity.cpp | 4 + zone/lua_entity.cpp | 8 + zone/lua_entity.h | 2 + zone/lua_entity_list.cpp | 1 + zone/lua_entity_list.h | 1 + zone/lua_merc.cpp | 229 ++++++++++++++++++++++ zone/lua_merc.h | 63 ++++++ zone/lua_mob.h | 1 + zone/lua_parser.cpp | 189 ++++++++++++++++++ zone/lua_parser.h | 38 ++++ zone/lua_parser_events.cpp | 12 ++ zone/lua_parser_events.h | 1 + zone/merc.cpp | 27 ++- zone/merc.h | 3 + zone/mob.cpp | 95 ++------- zone/mob_ai.cpp | 32 +--- zone/npc.cpp | 18 +- zone/perl_client.cpp | 6 + zone/perl_merc.cpp | 195 +++++++++++++++++++ zone/quest_interface.h | 48 +++++ zone/quest_parser_collection.cpp | 301 +++++++++++++++++++++++++++++ zone/quest_parser_collection.h | 79 ++++++++ zone/questmgr.cpp | 319 +++++++++---------------------- zone/questmgr.h | 1 + zone/spells.cpp | 186 +++++------------- 31 files changed, 1715 insertions(+), 758 deletions(-) create mode 100644 zone/lua_merc.cpp create mode 100644 zone/lua_merc.h create mode 100644 zone/perl_merc.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index ddfa57d15..238dcf9f0 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -65,6 +65,7 @@ SET(zone_sources lua_inventory.cpp lua_item.cpp lua_iteminst.cpp + lua_merc.cpp lua_mob.cpp lua_mod.cpp lua_npc.cpp @@ -115,6 +116,7 @@ SET(zone_sources perl_groups.cpp perl_hateentry.cpp perl_inventory.cpp + perl_merc.cpp perl_mob.cpp perl_npc.cpp perl_object.cpp @@ -224,6 +226,7 @@ SET(zone_headers lua_inventory.h lua_item.h lua_iteminst.h + lua_merc.h lua_mob.h lua_mod.h lua_npc.h diff --git a/zone/attack.cpp b/zone/attack.cpp index ab1a971fe..6ffe44099 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1550,17 +1550,18 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo hit.damage_done = 0; } - if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_USE_SKILL)) { - const auto& export_string = fmt::format( + parse->EventBotMerc( + EVENT_USE_SKILL, + this, + nullptr, + [&]() { + return fmt::format( "{} {}", hit.skill, GetSkill(hit.skill) ); - - parse->EventBot(EVENT_USE_SKILL, CastToBot(), nullptr, export_string, 0); } - } + ); } } @@ -1922,11 +1923,9 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil } if (killer_mob) { - if (killer_mob->IsNPC()) { - if (parse->HasQuestSub(killer_mob->GetNPCTypeID(), EVENT_SLAY)) { - parse->EventNPC(EVENT_SLAY, killer_mob->CastToNPC(), this, "", 0); - } + parse->EventBotMercNPC(EVENT_SLAY, killer_mob, this); + if (killer_mob->IsNPC()) { killed_by = KilledByTypes::Killed_NPC; auto emote_id = killer_mob->GetEmoteID(); @@ -1934,12 +1933,6 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil killer_mob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this); } - killer_mob->TrySpellOnKill(killed_level, spell); - } else if (killer_mob->IsBot()) { - if (parse->BotHasQuestSub(EVENT_SLAY)) { - parse->EventBot(EVENT_SLAY, killer_mob->CastToBot(), this, "", 0); - } - killer_mob->TrySpellOnKill(killed_level, spell); } @@ -2458,14 +2451,10 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp spell_id = SPELL_UNKNOWN; //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds - if (attacked_timer.Check()) - { - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_ATTACK)) { - LogCombat("Triggering EVENT_ATTACK due to attack by [{}]", other ? other->GetName() : "nullptr"); - - parse->EventNPC(EVENT_ATTACK, this, other, "", 0); - } + if (attacked_timer.Check()) { + parse->EventMercNPC(EVENT_ATTACK, this, other); } + attacked_timer.Start(CombatEventTimer_expire); if (!IsEngaged()) @@ -2506,41 +2495,22 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy Mob* owner_or_self = killer_mob ? killer_mob->GetOwnerOrSelf() : nullptr; - if (IsNPC()) { - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH)) { - const auto& export_string = fmt::format( - "{} {} {} {}", - killer_mob ? killer_mob->GetID() : 0, - damage, - spell, - static_cast(attack_skill) - ); + auto exports = [&]() { + return fmt::format( + "{} {} {} {}", + killer_mob ? killer_mob->GetID() : 0, + damage, + spell, + static_cast(attack_skill) + ); + }; - if (parse->EventNPC(EVENT_DEATH, this, owner_or_self, export_string, 0) != 0) { - if (GetHP() < 0) { - SetHP(0); - } - - return false; - } + if (parse->EventBotMercNPC(EVENT_DEATH, this, owner_or_self, exports) != 0) { + if (GetHP() < 0) { + SetHP(0); } - } else if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_DEATH)) { - const auto& export_string = fmt::format( - "{} {} {} {}", - killer_mob ? killer_mob->GetID() : 0, - damage, - spell, - static_cast(attack_skill) - ); - if (parse->EventBot(EVENT_DEATH, CastToBot(), owner_or_self, export_string, 0) != 0) { - if (GetHP() < 0) { - SetHP(0); - } - return false; - } - } + return false; } if (killer_mob && killer_mob->IsOfClientBot() && IsValidSpell(spell) && damage > 0) { @@ -3077,10 +3047,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy } } - if (killer_mob && killer_mob->IsBot()) { - if (parse->BotHasQuestSub(EVENT_NPC_SLAY)) { - parse->EventBot(EVENT_NPC_SLAY, killer_mob->CastToBot(), this, "", 0); - } + if (killer_mob) { + parse->EventBotMerc(EVENT_NPC_SLAY, killer_mob, this); killer_mob->TrySpellOnKill(killed_level, spell); } @@ -3108,24 +3076,29 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy } } - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) { - 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 }; - std::vector args = { corpse }; - - parse->EventNPC(EVENT_DEATH_COMPLETE, this, owner_or_self, export_string, 0, &args); - } + parse->EventMercNPC( + EVENT_DEATH_COMPLETE, + this, + owner_or_self, + [&]() { + return 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() + ); + }, + 0, + &args + ); // Zone controller process EVENT_DEATH_ZONE (Death events) if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) { @@ -4285,100 +4258,60 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons //final damage has been determined. int old_hp_ratio = (int)GetHPRatio(); - const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN); - - const auto has_bot_taken_event = parse->BotHasQuestSub(EVENT_DAMAGE_TAKEN); - - const auto has_npc_given_event = ( - ( - IsNPC() && - parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN) - ) || - ( - attacker && - attacker->IsNPC() && - parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_GIVEN) - ) - ); - - const auto has_npc_taken_event = ( - ( - IsNPC() && - parse->HasQuestSub(CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN) - ) || - ( - attacker && - attacker->IsNPC() && - parse->HasQuestSub(attacker->CastToNPC()->GetNPCTypeID(), EVENT_DAMAGE_TAKEN) - ) - ); - - const auto has_player_given_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_GIVEN); - - const auto has_player_taken_event = parse->PlayerHasQuestSub(EVENT_DAMAGE_TAKEN); - - const auto has_given_event = ( - has_bot_given_event || - has_npc_given_event || - has_player_given_event - ); - - const auto has_taken_event = ( - has_bot_taken_event || - has_npc_taken_event || - has_player_taken_event - ); std::vector args; + int64 damage_override = 0; - if (has_given_event && attacker) { - const auto export_string = fmt::format( - "{} {} {} {} {} {} {} {} {}", - GetID(), - damage, - spell_id, - static_cast(skill_used), - FromDamageShield ? 1 : 0, - avoidable ? 1 : 0, - buffslot, - iBuffTic ? 1 : 0, - static_cast(special) - ); + if (attacker) { + args = { this }; - if (attacker->IsBot() && has_bot_given_event) { - parse->EventBot(EVENT_DAMAGE_GIVEN, attacker->CastToBot(), this, export_string, 0); - } else if (attacker->IsClient() && has_player_given_event) { - args.push_back(this); - parse->EventPlayer(EVENT_DAMAGE_GIVEN, attacker->CastToClient(), export_string, 0, &args); - } else if (attacker->IsNPC() && has_npc_given_event) { - parse->EventNPC(EVENT_DAMAGE_GIVEN, attacker->CastToNPC(), this, export_string, 0); - } + parse->EventMob( + EVENT_DAMAGE_GIVEN, + attacker, + this, + [&]() { + return fmt::format( + "{} {} {} {} {} {} {} {} {}", + GetID(), + damage, + spell_id, + static_cast(skill_used), + FromDamageShield ? 1 : 0, + avoidable ? 1 : 0, + buffslot, + iBuffTic ? 1 : 0, + static_cast(special) + ); + }, + 0, + &args + ); } - if (has_taken_event) { - const auto export_string = fmt::format( - "{} {} {} {} {} {} {} {} {}", - attacker ? attacker->GetID() : 0, - damage, - spell_id, - static_cast(skill_used), - FromDamageShield ? 1 : 0, - avoidable ? 1 : 0, - buffslot, - iBuffTic ? 1 : 0, - static_cast(special) - ); + args = { attacker }; - if (IsBot() && has_bot_taken_event) { - damage_override = parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0); - } else if (IsClient() && has_player_taken_event) { - args.push_back(attacker ? attacker : nullptr); - damage_override = parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args); - } else if (IsNPC() && has_npc_taken_event) { - damage_override = parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0); - } - } + damage_override = parse->EventMob( + EVENT_DAMAGE_TAKEN, + this, + attacker, + [&]() { + return fmt::format( + "{} {} {} {} {} {} {} {} {}", + attacker ? attacker->GetID() : 0, + damage, + spell_id, + static_cast(skill_used), + FromDamageShield ? 1 : 0, + avoidable ? 1 : 0, + buffslot, + iBuffTic ? 1 : 0, + static_cast(special) + ); + }, + 0, + &args + ); if (damage_override > 0) { damage = damage_override; diff --git a/zone/client.cpp b/zone/client.cpp index b8a60580c..2da8b4296 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -1243,45 +1243,28 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s entity_list.ProcessProximitySay(message, this, language); } + Mob* t = GetTarget(); + if ( - GetTarget() && - GetTarget()->IsNPC() && - !IsInvisible(GetTarget()) + t && + !IsInvisible(t) && + DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say) ) { - auto* t = GetTarget()->CastToNPC(); - if (!t->IsEngaged()) { - CheckLDoNHail(t); - CheckEmoteHail(t, message); + const bool is_engaged = t->IsEngaged(); - if (DistanceNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) { - if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_SAY)) { - parse->EventNPC(EVENT_SAY, t, this, message, language); - } - - if (RuleB(TaskSystem, EnableTaskSystem)) { - if (UpdateTasksOnSpeakWith(t)) { - t->DoQuestPause(this); - } - } - } + if (is_engaged) { + parse->EventBotMercNPC(EVENT_AGGRO_SAY, t, this, [&]() { return message; }, language); } else { - if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_AGGRO_SAY)) { - if (DistanceSquaredNoZ(m_Position, t->GetPosition()) <= RuleI(Range, Say)) { - parse->EventNPC(EVENT_AGGRO_SAY, t, this, message, language); - } - } + parse->EventBotMercNPC(EVENT_SAY, t, this, [&]() { return message; }, language); } - } - else if (GetTarget() && GetTarget()->IsBot() && !IsInvisible(GetTarget())) { - if (DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) { - if (GetTarget()->IsEngaged()) { - if (parse->BotHasQuestSub(EVENT_AGGRO_SAY)) { - parse->EventBot(EVENT_AGGRO_SAY, GetTarget()->CastToBot(), this, message, language); - } - } else { - if (parse->BotHasQuestSub(EVENT_SAY)) { - parse->EventBot(EVENT_SAY, GetTarget()->CastToBot(), this, message, language); + if (t->IsNPC() && !is_engaged) { + CheckLDoNHail(t->CastToNPC()); + CheckEmoteHail(t->CastToNPC(), message); + + if (RuleB(TaskSystem, EnableTaskSystem)) { + if (UpdateTasksOnSpeakWith(t->CastToNPC())) { + t->CastToNPC()->DoQuestPause(this); } } } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 76f72ab31..c880e310f 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11984,15 +11984,7 @@ void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) auto t = GetTarget(); if (t) { - if (t->IsNPC()) { - if (parse->HasQuestSub(t->GetNPCTypeID(), EVENT_POPUP_RESPONSE)) { - parse->EventNPC(EVENT_POPUP_RESPONSE, t->CastToNPC(), this, std::to_string(popup_response->popupid), 0); - } - } else if (t->IsBot()) { - if (parse->BotHasQuestSub(EVENT_POPUP_RESPONSE)) { - parse->EventBot(EVENT_POPUP_RESPONSE, t->CastToBot(), this, std::to_string(popup_response->popupid), 0); - } - } + parse->EventBotMercNPC(EVENT_POPUP_RESPONSE, t, this, [&]() { return std::to_string(popup_response->popupid); }); } } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index 7b89674b0..8d4145c67 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -57,6 +57,7 @@ void perl_register_expedition(); void perl_register_expedition_lock_messages(); void perl_register_bot(); void perl_register_buff(); +void perl_register_merc(); #endif // EMBPERL_XS_CLASSES #endif // EMBPERL_XS @@ -217,6 +218,8 @@ PerlembParser::PerlembParser() : perl(nullptr) global_player_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded; + merc_quest_status_ = questUnloaded; + global_merc_quest_status_ = questUnloaded; } PerlembParser::~PerlembParser() @@ -258,6 +261,8 @@ void PerlembParser::ReloadQuests() global_player_quest_status_ = questUnloaded; bot_quest_status_ = questUnloaded; global_bot_quest_status_ = questUnloaded; + merc_quest_status_ = questUnloaded; + global_merc_quest_status_ = questUnloaded; item_quest_status_.clear(); spell_quest_status_.clear(); @@ -285,6 +290,8 @@ int PerlembParser::EventCommon( 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; @@ -295,6 +302,8 @@ int PerlembParser::EventCommon( 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, @@ -310,6 +319,8 @@ int PerlembParser::EventCommon( 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, @@ -339,6 +350,8 @@ int PerlembParser::EventCommon( 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, @@ -356,6 +369,8 @@ int PerlembParser::EventCommon( 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, @@ -382,7 +397,7 @@ int PerlembParser::EventCommon( 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) { + } 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); @@ -1009,41 +1024,22 @@ int PerlembParser::SendCommands( #ifdef EMBPERL_XS_CLASSES dTHX; { - std::string cl = fmt::format("${}::client", prefix); - std::string np = fmt::format("${}::npc", prefix); - std::string qi = fmt::format("${}::questitem", prefix); - std::string sp = fmt::format("${}::spell", prefix); - std::string enl = fmt::format("${}::entity_list", prefix); - std::string bot = fmt::format("${}::bot", prefix); + const std::vector& suffixes = { + "bot", + "client", + "entity_list", + "merc", + "npc", + "questitem", + "spell" + }; - if (clear_vars_.find(cl) != clear_vars_.end()) { - auto e = fmt::format("{} = undef;", cl); - perl->eval(e.c_str()); - } - - if (clear_vars_.find(np) != clear_vars_.end()) { - auto e = fmt::format("{} = undef;", np); - perl->eval(e.c_str()); - } - - if (clear_vars_.find(qi) != clear_vars_.end()) { - auto e = fmt::format("{} = undef;", qi); - perl->eval(e.c_str()); - } - - if (clear_vars_.find(sp) != clear_vars_.end()) { - auto e = fmt::format("{} = undef;", sp); - perl->eval(e.c_str()); - } - - if (clear_vars_.find(enl) != clear_vars_.end()) { - auto e = fmt::format("{} = undef;", enl); - perl->eval(e.c_str()); - } - - if (clear_vars_.find(bot) != clear_vars_.end()) { - auto e = fmt::format("{} = undef;", bot); - perl->eval(e.c_str()); + for (const auto& suffix : suffixes) { + const std::string& key = fmt::format("${}::{}", prefix, suffix); + if (clear_vars_.find(suffix) != clear_vars_.end()) { + auto e = fmt::format("{} = undef;", key); + perl->eval(e.c_str()); + } } } @@ -1060,19 +1056,21 @@ int PerlembParser::SendCommands( sv_setsv(client, _empty_sv); } - //only export NPC if it's a npc quest - if (!other->IsClient() && 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->IsClient() && other->IsBot()) { + 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 @@ -1098,23 +1096,25 @@ int PerlembParser::SendCommands( #endif //now call the requested sub - ret_value = perl->dosub(std::string(prefix).append("::").append(event_id).c_str()); + const std::string& sub_key = fmt::format("{}::{}", prefix, event_id); + ret_value = perl->dosub(sub_key.c_str()); #ifdef EMBPERL_XS_CLASSES { - std::string cl = fmt::format("${}::client", prefix); - std::string np = fmt::format("${}::npc", prefix); - std::string qi = fmt::format("${}::questitem", prefix); - std::string sp = fmt::format("${}::spell", prefix); - std::string enl = fmt::format("${}::entity_list", prefix); - std::string bot = fmt::format("${}::bot", prefix); + const std::vector& suffixes = { + "bot", + "client", + "entity_list", + "merc", + "npc", + "questitem", + "spell" + }; - clear_vars_[cl] = 1; - clear_vars_[np] = 1; - clear_vars_[qi] = 1; - clear_vars_[sp] = 1; - clear_vars_[enl] = 1; - clear_vars_[bot] = 1; + for (const auto& suffix : suffixes) { + const std::string& key = fmt::format("${}::{}", prefix, suffix); + clear_vars_[key] = 1; + } } #endif @@ -1184,6 +1184,7 @@ void PerlembParser::MapFunctions() perl_register_expedition_lock_messages(); perl_register_bot(); perl_register_buff(); + perl_register_merc(); #endif // EMBPERL_XS_CLASSES } @@ -1192,6 +1193,8 @@ void PerlembParser::GetQuestTypes( 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, @@ -1219,10 +1222,14 @@ void PerlembParser::GetQuestTypes( 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; } } } else { @@ -1251,6 +1258,8 @@ void PerlembParser::GetQuestPackageName( 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, @@ -1268,6 +1277,8 @@ void PerlembParser::GetQuestPackageName( !is_global_player_quest && !is_bot_quest && !is_global_bot_quest && + !is_merc_quest && + !is_global_merc_quest && !is_item_quest && !is_spell_quest ) { @@ -1291,6 +1302,10 @@ void PerlembParser::GetQuestPackageName( 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); } @@ -1316,6 +1331,8 @@ void PerlembParser::ExportQGlobals( 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, @@ -1331,6 +1348,8 @@ void PerlembParser::ExportQGlobals( !is_global_player_quest && !is_bot_quest && !is_global_bot_quest && + !is_merc_quest && + !is_global_merc_quest && !is_item_quest && !is_spell_quest ) { @@ -1466,6 +1485,8 @@ void PerlembParser::ExportMobVariables( 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, @@ -1491,6 +1512,8 @@ void PerlembParser::ExportMobVariables( !is_global_player_quest && !is_bot_quest && !is_global_bot_quest && + !is_merc_quest && + !is_global_merc_quest && !is_item_quest ) { if (mob && mob->IsClient() && npc_mob && npc_mob->IsNPC()) { @@ -1521,6 +1544,8 @@ void PerlembParser::ExportMobVariables( !is_global_player_quest && !is_bot_quest && !is_global_bot_quest && + !is_merc_quest && + !is_global_merc_quest && !is_item_quest && !is_spell_quest ) { @@ -2081,7 +2106,8 @@ void PerlembParser::ExportEventVariables( "killed_bot_id", killed->IsBot() ? killed->CastToBot()->GetBotID() : 0 ); - ExportVar(package_name.c_str(), "killed_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0); + ExportVar(package_name.c_str(), "killed_merc_id", killed->IsMerc() ? killed->CastToMerc()->GetMercenaryID() : 0); + ExportVar(package_name.c_str(), "killed_npc_id", !killed->IsMerc() && killed->IsNPC() ? killed->GetNPCTypeID() : 0); } } break; @@ -2357,6 +2383,7 @@ void PerlembParser::ExportEventVariables( case EVENT_DESPAWN: { 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); ExportVar(package_name.c_str(), "despawned_npc_id", npc_mob->IsNPC() ? npc_mob->GetNPCTypeID() : 0); break; } @@ -2637,4 +2664,124 @@ int PerlembParser::EventGlobalBot( ); } +void PerlembParser::LoadMercScript(std::string filename) +{ + if (!perl || merc_quest_status_ != questUnloaded) { + return; + } + + try { + perl->eval_file("qst_merc", filename.c_str()); + } catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Merc Quest File [{}] Error [{}]", + filename, + e + ) + ); + + merc_quest_status_ = questFailedToLoad; + return; + } + + merc_quest_status_ = questLoaded; +} + +void PerlembParser::LoadGlobalMercScript(std::string filename) +{ + if (!perl || global_merc_quest_status_ != questUnloaded) { + return; + } + + try { + perl->eval_file("qst_global_merc", filename.c_str()); + } catch (std::string e) { + AddError( + fmt::format( + "Error Compiling Global Merc Quest File [{}] Error [{}]", + filename, + e + ) + ); + + global_merc_quest_status_ = questFailedToLoad; + return; + } + + global_merc_quest_status_ = questLoaded; +} + +bool PerlembParser::MercHasQuestSub(QuestEventID event_id) +{ + if ( + !perl || + merc_quest_status_ != questLoaded || + event_id >= _LargestEventID + ) { + return false; + } + + return perl->SubExists("qst_merc", QuestEventSubroutines[event_id]); +} + +bool PerlembParser::GlobalMercHasQuestSub(QuestEventID event_id) +{ + if ( + !perl || + global_merc_quest_status_ != questLoaded || + event_id >= _LargestEventID + ) { + return false; + } + + return (perl->SubExists("qst_global_merc", QuestEventSubroutines[event_id])); +} + +int PerlembParser::EventMerc( + QuestEventID event_id, + Merc* merc, + Mob* mob, + std::string data, + uint32 extra_data, + std::vector* extra_pointers +) +{ + return EventCommon( + event_id, + 0, + data.c_str(), + merc, + nullptr, + nullptr, + mob, + extra_data, + false, + extra_pointers + ); +} + +int PerlembParser::EventGlobalMerc( + QuestEventID event_id, + Merc* merc, + Mob* mob, + std::string data, + uint32 extra_data, + std::vector* extra_pointers +) +{ + return EventCommon( + event_id, + 0, + data.c_str(), + merc, + nullptr, + nullptr, + mob, + extra_data, + true, + extra_pointers + ); +} + #endif diff --git a/zone/embparser.h b/zone/embparser.h index 9fc0f921f..dd4ffe8d8 100644 --- a/zone/embparser.h +++ b/zone/embparser.h @@ -118,6 +118,24 @@ public: std::vector* extra_pointers ); + virtual int EventMerc( + QuestEventID event_id, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + + virtual int EventGlobalMerc( + QuestEventID event_id, + Merc* merc, + Mob* init, + 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); @@ -126,6 +144,8 @@ public: virtual bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); virtual bool BotHasQuestSub(QuestEventID event_id); virtual bool GlobalBotHasQuestSub(QuestEventID event_id); + virtual bool MercHasQuestSub(QuestEventID event_id); + virtual bool GlobalMercHasQuestSub(QuestEventID event_id); virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadGlobalNPCScript(std::string filename); @@ -135,6 +155,8 @@ public: virtual void LoadSpellScript(std::string filename, uint32 spell_id); virtual void LoadBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename); + virtual void LoadMercScript(std::string filename); + virtual void LoadGlobalMercScript(std::string filename); virtual void AddVar(std::string name, std::string val); virtual std::string GetVar(std::string name); @@ -182,6 +204,8 @@ private: 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, @@ -197,6 +221,8 @@ private: 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, @@ -216,6 +242,8 @@ private: 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, @@ -230,6 +258,8 @@ private: 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, @@ -263,6 +293,8 @@ private: PerlQuestStatus global_player_quest_status_; PerlQuestStatus bot_quest_status_; PerlQuestStatus global_bot_quest_status_; + PerlQuestStatus merc_quest_status_; + PerlQuestStatus global_merc_quest_status_; SV* _empty_sv; diff --git a/zone/entity.cpp b/zone/entity.cpp index d171c2807..c21856d37 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -776,6 +776,10 @@ void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue) merc_list.emplace(std::pair(merc->GetID(), merc)); mob_list.emplace(std::pair(merc->GetID(), merc)); + + if (parse->MercHasQuestSub(EVENT_SPAWN)) { + parse->EventMerc(EVENT_SPAWN, merc, nullptr, "", 0); + } } } diff --git a/zone/lua_entity.cpp b/zone/lua_entity.cpp index 26ae9df1b..18ffdee2a 100644 --- a/zone/lua_entity.cpp +++ b/zone/lua_entity.cpp @@ -12,6 +12,7 @@ #include "lua_door.h" #include "lua_bot.h" +#include "lua_merc.h" bool Lua_Entity::IsClient() { Lua_Safe_Call_Bool(); @@ -140,6 +141,12 @@ Lua_Bot Lua_Entity::CastToBot() { return Lua_Bot(b); } +Lua_Merc Lua_Entity::CastToMerc() { + void *d = GetLuaPtrData(); + Merc *m = reinterpret_cast(d); + return Lua_Merc(m); +} + luabind::scope lua_register_entity() { return luabind::class_("Entity") .def(luabind::constructor<>()) @@ -149,6 +156,7 @@ luabind::scope lua_register_entity() { .def("CastToClient", &Lua_Entity::CastToClient) .def("CastToCorpse", &Lua_Entity::CastToCorpse) .def("CastToDoor", &Lua_Entity::CastToDoor) + .def("CastToMerc", &Lua_Entity::CastToMerc) .def("CastToMob", &Lua_Entity::CastToMob) .def("CastToNPC", &Lua_Entity::CastToNPC) .def("CastToObject", &Lua_Entity::CastToObject) diff --git a/zone/lua_entity.h b/zone/lua_entity.h index 0b08b8a0c..3ac07a2e3 100644 --- a/zone/lua_entity.h +++ b/zone/lua_entity.h @@ -7,6 +7,7 @@ class Entity; class Lua_Client; class Lua_Bot; +class Lua_Merc; class Lua_NPC; class Lua_Mob; struct Lua_HateList; @@ -59,6 +60,7 @@ public: Lua_Object CastToObject(); Lua_Door CastToDoor(); Lua_Bot CastToBot(); + Lua_Merc CastToMerc(); }; #endif diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index 825b78ada..45c0e6621 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -16,6 +16,7 @@ #include "lua_spawn.h" #include "lua_bot.h" +#include "lua_merc.h" struct Lua_Mob_List { std::vector entries; diff --git a/zone/lua_entity_list.h b/zone/lua_entity_list.h index bc4d3f576..ad4f0fbd5 100644 --- a/zone/lua_entity_list.h +++ b/zone/lua_entity_list.h @@ -8,6 +8,7 @@ class EntityList; class Lua_Mob; class Lua_Client; class Lua_Bot; +class Lua_Merc; class Lua_NPC; class Lua_Door; class Lua_Corpse; diff --git a/zone/lua_merc.cpp b/zone/lua_merc.cpp new file mode 100644 index 000000000..8e153c19f --- /dev/null +++ b/zone/lua_merc.cpp @@ -0,0 +1,229 @@ +#ifdef LUA_EQEMU + +#include "lua.hpp" +#include + +#include "merc.h" +#include "lua_client.h" +#include "lua_merc.h" +#include "lua_group.h" +#include "lua_item.h" +#include "lua_iteminst.h" +#include "lua_mob.h" + +uint32 Lua_Merc::GetCostFormula() +{ + Lua_Safe_Call_Int(); + return self->GetCostFormula(); +} + +Lua_Group Lua_Merc::GetGroup() +{ + Lua_Safe_Call_Class(Lua_Group); + return self->GetGroup(); +} + +int Lua_Merc::GetHatedCount() +{ + Lua_Safe_Call_Int(); + return self->GetHatedCount(); +} + +float Lua_Merc::GetMaxMeleeRangeToTarget(Lua_Mob target) +{ + Lua_Safe_Call_Real(); + return self->GetMaxMeleeRangeToTarget(target); +} + +uint32 Lua_Merc::GetMercenaryCharacterID() +{ + Lua_Safe_Call_Int(); + return self->GetMercenaryCharacterID(); +} + +uint32 Lua_Merc::GetMercenaryID() +{ + Lua_Safe_Call_Int(); + return self->GetMercenaryID(); +} + +uint32 Lua_Merc::GetMercenaryNameType() +{ + Lua_Safe_Call_Int(); + return self->GetMercNameType(); +} + +Lua_Client Lua_Merc::GetMercenaryOwner() +{ + Lua_Safe_Call_Class(Lua_Client); + return Lua_Client(self->GetMercenaryOwner()); +} + +uint32 Lua_Merc::GetMercenarySubtype() +{ + Lua_Safe_Call_Int(); + return self->GetMercenarySubType(); +} + +uint32 Lua_Merc::GetMercenaryTemplateID() +{ + Lua_Safe_Call_Int(); + return self->GetMercenaryTemplateID(); +} + +uint32 Lua_Merc::GetMercenaryType() +{ + Lua_Safe_Call_Int(); + return self->GetMercenaryType(); +} + +Lua_Mob Lua_Merc::GetOwner() +{ + Lua_Safe_Call_Class(Lua_Mob); + return Lua_Mob(self->GetOwner()); +} + +Lua_Mob Lua_Merc::GetOwnerOrSelf() +{ + Lua_Safe_Call_Class(Lua_Mob); + return Lua_Mob(self->GetOwnerOrSelf()); +} + +uint8 Lua_Merc::GetProficiencyID() +{ + Lua_Safe_Call_Int(); + return self->GetProficiencyID(); +} + +uint8 Lua_Merc::GetStance() +{ + Lua_Safe_Call_Int(); + return self->GetStance(); +} + +uint8 Lua_Merc::GetTierID() +{ + Lua_Safe_Call_Int(); + return self->GetTierID(); +} + +bool Lua_Merc::HasOrMayGetAggro() +{ + Lua_Safe_Call_Bool(); + return self->HasOrMayGetAggro(); +} + +bool Lua_Merc::IsMercenaryCaster() +{ + Lua_Safe_Call_Bool(); + return self->IsMercCaster(); +} + +bool Lua_Merc::IsMercenaryCasterCombatRange(Lua_Mob target) +{ + Lua_Safe_Call_Bool(); + return self->IsMercCasterCombatRange(target); +} + +bool Lua_Merc::IsSitting() +{ + Lua_Safe_Call_Bool(); + return self->IsSitting(); +} + +bool Lua_Merc::IsStanding() +{ + Lua_Safe_Call_Bool(); + return self->IsStanding(); +} + +void Lua_Merc::ScaleStats(int scale_percentage) +{ + Lua_Safe_Call_Void(); + self->ScaleStats(scale_percentage); +} + +void Lua_Merc::ScaleStats(int scale_percentage, bool set_to_max) +{ + Lua_Safe_Call_Void(); + self->ScaleStats(scale_percentage, set_to_max); +} + +void Lua_Merc::SendPayload(int payload_id, std::string payload_value) +{ + Lua_Safe_Call_Void(); + self->SendPayload(payload_id, payload_value); +} + +void Lua_Merc::SetTarget(Lua_Mob target) +{ + Lua_Safe_Call_Void(); + self->SetTarget(target); +} + +void Lua_Merc::Signal(int signal_id) +{ + Lua_Safe_Call_Void(); + self->Signal(signal_id); +} + +void Lua_Merc::Sit() +{ + Lua_Safe_Call_Void(); + self->Sit(); +} + +void Lua_Merc::Stand() +{ + Lua_Safe_Call_Void(); + self->Stand(); +} + +bool Lua_Merc::Suspend() +{ + Lua_Safe_Call_Bool(); + return self->Suspend(); +} + +bool Lua_Merc::UseDiscipline(uint16 spell_id, uint16 target_id) +{ + Lua_Safe_Call_Bool(); + return self->UseDiscipline(spell_id, target_id); +} + +luabind::scope lua_register_merc() { + return luabind::class_("Merc") + .def(luabind::constructor<>()) + .def("GetCostFormula", &Lua_Merc::GetCostFormula) + .def("GetGroup", &Lua_Merc::GetGroup) + .def("GetHatedCount", &Lua_Merc::GetHatedCount) + .def("GetMaxMeleeRangeToTarget", &Lua_Merc::GetMaxMeleeRangeToTarget) + .def("GetMercenaryCharacterID", &Lua_Merc::GetMercenaryCharacterID) + .def("GetMercenaryID", &Lua_Merc::GetMercenaryID) + .def("GetMercenaryNameType", &Lua_Merc::GetMercenaryNameType) + .def("GetMercenaryOwner", &Lua_Merc::GetMercenaryOwner) + .def("GetMercenarySubtype", &Lua_Merc::GetMercenarySubtype) + .def("GetMercenaryTemplateID", &Lua_Merc::GetMercenaryTemplateID) + .def("GetMercenaryType", &Lua_Merc::GetMercenaryType) + .def("GetOwner", &Lua_Merc::GetOwner) + .def("GetOwnerOrSelf", &Lua_Merc::GetOwnerOrSelf) + .def("GetProficiencyID", &Lua_Merc::GetProficiencyID) + .def("GetStance", &Lua_Merc::GetStance) + .def("GetTierID", &Lua_Merc::GetTierID) + .def("HasOrMayGetAggro", &Lua_Merc::HasOrMayGetAggro) + .def("IsMercenaryCaster", &Lua_Merc::IsMercenaryCaster) + .def("IsMercenaryCasterCombatRange", &Lua_Merc::IsMercenaryCasterCombatRange) + .def("IsSitting", &Lua_Merc::IsSitting) + .def("IsStanding", &Lua_Merc::IsStanding) + .def("ScaleStats", (void(Lua_Merc::*)(int))&Lua_Merc::ScaleStats) + .def("ScaleStats", (void(Lua_Merc::*)(int,bool))&Lua_Merc::ScaleStats) + .def("SendPayload", &Lua_Merc::SendPayload) + .def("SetTarget", &Lua_Merc::SetTarget) + .def("Signal", &Lua_Merc::Signal) + .def("Sit", &Lua_Merc::Sit) + .def("Stand", &Lua_Merc::Stand) + .def("Suspend", &Lua_Merc::Suspend) + .def("UseDiscipline", &Lua_Merc::UseDiscipline); +} + +#endif diff --git a/zone/lua_merc.h b/zone/lua_merc.h new file mode 100644 index 000000000..90a7b3ab8 --- /dev/null +++ b/zone/lua_merc.h @@ -0,0 +1,63 @@ +#ifndef EQEMU_LUA_MERC_H +#define EQEMU_LUA_MERC_H +#ifdef LUA_EQEMU + +#include "lua_mob.h" + +class Merc; +class Lua_Group; +class Lua_Merc; +class Lua_Mob; + +namespace luabind { + struct scope; +} + +luabind::scope lua_register_merc(); + +class Lua_Merc : public Lua_Mob +{ + typedef Merc NativeType; +public: + Lua_Merc() { SetLuaPtrData(nullptr); } + Lua_Merc(Merc *d) { SetLuaPtrData(reinterpret_cast(d)); } + virtual ~Lua_Merc() { } + + operator Merc*() { + return reinterpret_cast(GetLuaPtrData()); + } + + uint32 GetCostFormula(); + Lua_Group GetGroup(); + int GetHatedCount(); + float GetMaxMeleeRangeToTarget(Lua_Mob target); + uint32 GetMercenaryCharacterID(); + uint32 GetMercenaryID(); + uint32 GetMercenaryNameType(); + Lua_Client GetMercenaryOwner(); + uint32 GetMercenarySubtype(); + uint32 GetMercenaryTemplateID(); + uint32 GetMercenaryType(); + Lua_Mob GetOwner(); + Lua_Mob GetOwnerOrSelf(); + uint8 GetProficiencyID(); + uint8 GetStance(); + uint8 GetTierID(); + bool HasOrMayGetAggro(); + bool IsMercenaryCaster(); + bool IsMercenaryCasterCombatRange(Lua_Mob target); + bool IsSitting(); + bool IsStanding(); + void ScaleStats(int scale_percentage); + void ScaleStats(int scale_percentage, bool set_to_max); + void SendPayload(int payload_id, std::string payload_value); + void SetTarget(Lua_Mob target); + void Signal(int signal_id); + void Sit(); + void Stand(); + bool Suspend(); + bool UseDiscipline(uint16 spell_id, uint16 target_id); +}; + +#endif // LUA_EQEMU +#endif // EQEMU_LUA_MERC_H diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 2e42bd9f6..dfe646cc0 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -10,6 +10,7 @@ class Lua_Item; class Lua_ItemInst; class Lua_StatBonuses; class Lua_Bot; +class Lua_Merc; class Lua_NPC; class Lua_Client; struct Lua_Mob_List; diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index be1e5155b..b485d02ae 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -32,6 +32,7 @@ #include "lua_inventory.h" #include "lua_item.h" #include "lua_iteminst.h" +#include "lua_merc.h" #include "lua_mob.h" #include "lua_npc.h" #include "lua_object.h" @@ -1279,6 +1280,7 @@ void LuaParser::MapFunctions(lua_State *L) { lua_register_npc(), lua_register_client(), lua_register_bot(), + lua_register_merc(), lua_register_inventory(), lua_register_inventory_where(), lua_register_iteminst(), @@ -1835,3 +1837,190 @@ void LuaParser::LoadBotScript(std::string filename) { void LuaParser::LoadGlobalBotScript(std::string filename) { LoadScript(filename, "global_bot"); } + +int LuaParser::EventMerc( + QuestEventID evt, + Merc *merc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return 0; + } + + if (!merc) { + return 0; + } + + if (!MercHasQuestSub(evt)) { + return 0; + } + + return _EventMerc("merc", evt, merc, init, data, extra_data, extra_pointers); +} + +int LuaParser::EventGlobalMerc( + QuestEventID evt, + Merc *merc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return 0; + } + + if (!merc) { + return 0; + } + + if (!GlobalMercHasQuestSub(evt)) { + return 0; + } + + return _EventMerc("global_merc", evt, merc, init, data, extra_data, extra_pointers); +} + +int LuaParser::_EventMerc( + std::string package_name, + QuestEventID evt, + Merc *merc, + Mob *init, + 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_Merc l_merc(merc); + luabind::adl::object l_merc_o = luabind::adl::object(L, l_merc); + l_merc_o.push(L); + lua_setfield(L, -2, "self"); + + auto arg_function = NPCArgumentDispatch[evt]; + arg_function(this, L, merc, init, data, extra_data, extra_pointers); + auto* c = (init && init->IsClient()) ? init->CastToClient() : nullptr; + + quest_manager.StartQuest(merc, c); + 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 Merc [{}] in [{}]: {}", + sub_name, + merc->GetID(), + 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::DispatchEventMerc( + QuestEventID evt, + Merc *merc, + Mob *init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers +) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return 0; + } + + std::string package_name = "merc"; + + 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 = _EventMerc(package_name, evt, merc, init, data, extra_data, extra_pointers, &riter->lua_reference); + if (i != 0) { + ret = i; + } + } + + ++riter; + } + + return ret; +} + +bool LuaParser::MercHasQuestSub(QuestEventID evt) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return false; + } + + const char *subname = LuaEvents[evt]; + return HasFunction(subname, "merc"); +} + +bool LuaParser::GlobalMercHasQuestSub(QuestEventID evt) { + evt = ConvertLuaEvent(evt); + if (evt >= _LargestEventID) { + return false; + } + + const char *subname = LuaEvents[evt]; + return HasFunction(subname, "global_merc"); +} + +void LuaParser::LoadMercScript(std::string filename) { + LoadScript(filename, "merc"); +} + +void LuaParser::LoadGlobalMercScript(std::string filename) { + LoadScript(filename, "global_merc"); +} diff --git a/zone/lua_parser.h b/zone/lua_parser.h index f1bf3a8e3..fbd50a984 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -109,6 +109,22 @@ public: uint32 extra_data, std::vector *extra_pointers ); + virtual int EventMerc( + QuestEventID evt, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + virtual int EventGlobalMerc( + QuestEventID evt, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt); @@ -120,6 +136,8 @@ public: virtual bool HasEncounterSub(const std::string& package_name, QuestEventID evt); virtual bool BotHasQuestSub(QuestEventID evt); virtual bool GlobalBotHasQuestSub(QuestEventID evt); + virtual bool MercHasQuestSub(QuestEventID evt); + virtual bool GlobalMercHasQuestSub(QuestEventID evt); virtual void LoadNPCScript(std::string filename, int npc_id); virtual void LoadGlobalNPCScript(std::string filename); @@ -130,6 +148,8 @@ public: virtual void LoadEncounterScript(std::string filename, std::string encounter_name); virtual void LoadBotScript(std::string filename); virtual void LoadGlobalBotScript(std::string filename); + virtual void LoadMercScript(std::string filename); + virtual void LoadGlobalMercScript(std::string filename); virtual void AddVar(std::string name, std::string val); virtual std::string GetVar(std::string name); @@ -179,6 +199,14 @@ public: uint32 extra_data, std::vector *extra_pointers ); + virtual int DispatchEventMerc( + QuestEventID evt, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); static LuaParser* Instance() { static LuaParser inst; @@ -269,6 +297,16 @@ private: std::vector *extra_pointers, luabind::adl::object *l_func = nullptr ); + int _EventMerc( + std::string package_name, + QuestEventID evt, + Merc* merc, + Mob* init, + 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); diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index f0186056c..fb6cb8708 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -734,6 +734,18 @@ void handle_player_death( 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"); } void handle_player_timer( diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 526046f91..4a9a1bbb9 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -8,6 +8,7 @@ typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, EQ::Ite typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, Mob*, Client*, uint32, std::string, uint32, std::vector*); 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*); // NPC void handle_npc_event_say( diff --git a/zone/merc.cpp b/zone/merc.cpp index e659fabcd..6200e5b89 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -5,6 +5,7 @@ #include "entity.h" #include "groups.h" #include "mob.h" +#include "quest_parser_collection.h" #include "zone.h" #include "string_ids.h" @@ -4078,12 +4079,6 @@ bool Merc::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillT Save(); - //no corpse, no exp if we're a merc. - //We'll suspend instead, since that's what live does. - //Not actually sure live supports 'depopping' merc corpses. - //if(entity_list.GetCorpseByID(GetID())) - // entity_list.GetCorpseByID(GetID())->Depop(); - // If client is in zone, suspend merc, else depop it. if (!Suspend()) { Depop(); @@ -4671,7 +4666,6 @@ bool Merc::Spawn(Client *owner) { //UpdateMercAppearance(); - return true; } @@ -5189,9 +5183,6 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) { merc->SetStance(GetMercInfo().Stance); Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName()); - - return; - } bool Merc::Suspend() { @@ -5914,3 +5905,19 @@ uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type return cost; } + +void Merc::Signal(int signal_id) +{ + if (parse->MercHasQuestSub(EVENT_SIGNAL)) { + parse->EventMerc(EVENT_SIGNAL, this, nullptr, std::to_string(signal_id), 0); + } +} + +void Merc::SendPayload(int payload_id, std::string payload_value) +{ + if (parse->MercHasQuestSub(EVENT_PAYLOAD)) { + const auto& export_string = fmt::format("{} {}", payload_id, payload_value); + + parse->EventMerc(EVENT_PAYLOAD, this, nullptr, export_string, 0); + } +} diff --git a/zone/merc.h b/zone/merc.h index 70ce4c717..028b08299 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -146,6 +146,9 @@ public: bool IsMedding() { return _medding; }; bool IsSuspended() { return _suspended; }; + void Signal(int signal_id); + void SendPayload(int payload_id, std::string payload_value); + static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0); diff --git a/zone/mob.cpp b/zone/mob.cpp index 8791e2436..a1d9b5d38 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5514,37 +5514,13 @@ void Mob::SetTarget(Mob *mob) target = mob; entity_list.UpdateHoTT(this); - const auto has_target_change_event = ( - parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE) || - parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE) || - parse->BotHasQuestSub(EVENT_TARGET_CHANGE) - ); - if (IsClient() && CastToClient()->admin > AccountStatus::GMMgmt) { DisplayInfo(mob); } - if (has_target_change_event) { - std::vector args; + std::vector args = { mob }; - args.emplace_back(mob); - - if (IsNPC()) { - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_TARGET_CHANGE)) { - parse->EventNPC(EVENT_TARGET_CHANGE, CastToNPC(), mob, "", 0, &args); - } - } else if (IsClient()) { - if (parse->PlayerHasQuestSub(EVENT_TARGET_CHANGE)) { - parse->EventPlayer(EVENT_TARGET_CHANGE, CastToClient(), "", 0, &args); - } - - CastToClient()->SetBotPrecombat(false); // Any change in target will nullify this flag (target == mob checked above) - } else if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_TARGET_CHANGE)) { - parse->EventBot(EVENT_TARGET_CHANGE, CastToBot(), mob, "", 0, &args); - } - } - } + parse->EventMob(EVENT_TARGET_CHANGE, this, mob, [&]() { return ""; }, 0, &args); if (IsPet() && GetOwner() && GetOwner()->IsClient()) { GetOwner()->CastToClient()->UpdateXTargetType(MyPetTarget, mob); @@ -5717,22 +5693,10 @@ bool Mob::ClearEntityVariables() return false; } - if ( - (IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) || - (IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) || - (IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE)) - ) { - for (const auto& e : m_EntityVariables) { - std::vector args = { e.first, e.second }; + for (const auto& e : m_EntityVariables) { + std::vector args = { e.first, e.second }; - if (IsBot()) { - parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args); - } else if (IsClient()) { - parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args); - } else if (IsNPC()) { - parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args); - } - } + parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args); } m_EntityVariables.clear(); @@ -5750,24 +5714,11 @@ bool Mob::DeleteEntityVariable(std::string variable_name) return false; } + std::vector args = { v->first, v->second }; + parse->EventMob(EVENT_ENTITY_VARIABLE_DELETE, this, nullptr, [&]() { return ""; }, 0, &args); + m_EntityVariables.erase(v); - if ( - (IsBot() && parse->BotHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) || - (IsClient() && parse->PlayerHasQuestSub(EVENT_ENTITY_VARIABLE_DELETE)) || - (IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_ENTITY_VARIABLE_DELETE)) - ) { - std::vector args = { v->first, v->second }; - - if (IsBot()) { - parse->EventBot(EVENT_ENTITY_VARIABLE_DELETE, CastToBot(), nullptr, "", 0, &args); - } else if (IsClient()) { - parse->EventPlayer(EVENT_ENTITY_VARIABLE_DELETE, CastToClient(), "", 0, &args); - } else if (IsNPC()) { - parse->EventNPC(EVENT_ENTITY_VARIABLE_DELETE, CastToNPC(), nullptr, "", 0, &args); - } - } - return true; } @@ -5814,32 +5765,16 @@ void Mob::SetEntityVariable(std::string variable_name, std::string variable_valu return; } - const QuestEventID event_id = ( - !EntityVariableExists(variable_name) ? - EVENT_ENTITY_VARIABLE_SET : - EVENT_ENTITY_VARIABLE_UPDATE - ); + std::vector args; - if ( - (IsBot() && parse->BotHasQuestSub(event_id)) || - (IsClient() && parse->PlayerHasQuestSub(event_id)) || - (IsNPC() && parse->HasQuestSub(GetNPCTypeID(), event_id)) - ) { - std::vector args; + if (!EntityVariableExists(variable_name)) { + args = { variable_name, variable_value }; - if (event_id != EVENT_ENTITY_VARIABLE_UPDATE) { - args = { variable_name, variable_value }; - } else { - args = { variable_name, GetEntityVariable(variable_name), variable_value }; - } + parse->EventMob(EVENT_ENTITY_VARIABLE_SET, this, nullptr, [&]() { return ""; }, 0, &args); + } else { + args = { variable_name, GetEntityVariable(variable_name), variable_value }; - if (IsBot()) { - parse->EventBot(event_id, CastToBot(), nullptr, "", 0, &args); - } else if (IsClient()) { - parse->EventPlayer(event_id, CastToClient(), "", 0, &args); - } else if (IsNPC()) { - parse->EventNPC(event_id, CastToNPC(), nullptr, "", 0, &args); - } + parse->EventMob(EVENT_ENTITY_VARIABLE_UPDATE, this, nullptr, [&]() { return ""; }, 0, &args); } m_EntityVariables[variable_name] = variable_value; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index af10c1713..fa2a48aa5 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1755,6 +1755,8 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help) SetAppearance(eaStanding); + parse->EventBotMerc(EVENT_COMBAT, this, attacker, [&] { return "1"; }); + if (IsNPC()) { CastToNPC()->AIautocastspell_timer->Start(300, false); @@ -1793,12 +1795,6 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help) } } } - - if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_COMBAT)) { - parse->EventBot(EVENT_COMBAT, CastToBot(), attacker, "1", 0); - } - } } // Note: Hate list may not be actually clear until after this function call completes @@ -1819,27 +1815,19 @@ void Mob::AI_Event_NoLongerEngaged() { StopNavigation(); ClearRampage(); + parse->EventBotMercNPC(EVENT_COMBAT, this, nullptr, [&]() { return "0"; }); + if (IsNPC()) { SetPrimaryAggro(false); SetAssistAggro(false); if (CastToNPC()->GetCombatEvent() && GetHP() > 0) { - if (entity_list.GetNPCByID(GetID())) { - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_COMBAT)) { - parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0); - } - - const uint32 emote_id = CastToNPC()->GetEmoteID(); - if (emote_id) { - CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id); - } - - m_combat_record.Stop(); - CastToNPC()->SetCombatEvent(false); + const uint32 emote_id = CastToNPC()->GetEmoteID(); + if (emote_id) { + CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::LeaveCombat, emote_id); } - } - } else if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_COMBAT)) { - parse->EventBot(EVENT_COMBAT, CastToBot(), nullptr, "0", 0); + + m_combat_record.Stop(); + CastToNPC()->SetCombatEvent(false); } } } diff --git a/zone/npc.cpp b/zone/npc.cpp index ecdfb803a..2172a529e 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -833,22 +833,10 @@ void NPC::Depop(bool start_spawn_timer) { DoNPCEmote(EQ::constants::EmoteEventTypes::OnDespawn, emoteid); } - if (IsNPC()) { - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DESPAWN)) { - parse->EventNPC(EVENT_DESPAWN, this, nullptr, "", 0); - } + parse->EventBotMercNPC(EVENT_DESPAWN, this, nullptr); - if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) { - DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr); - } - } else if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_DESPAWN)) { - parse->EventBot(EVENT_DESPAWN, CastToBot(), nullptr, "", 0); - } - - if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) { - DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr); - } + if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DESPAWN_ZONE)) { + DispatchZoneControllerEvent(EVENT_DESPAWN_ZONE, this, "", 0, nullptr); } p_depop = true; diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index af9981df3..8bb39afda 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3207,6 +3207,11 @@ void Perl_Client_AreaTaunt(Client* self, float range, int bonus_hate) entity_list.AETaunt(self, range, bonus_hate); } +Merc* Perl_Client_GetMerc(Client* self) +{ + return self->GetMerc(); +} + void perl_register_client() { perl::interpreter perl(PERL_GET_THX); @@ -3438,6 +3443,7 @@ void perl_register_client() package.add("GetLearnedDisciplines", &Perl_Client_GetLearnedDisciplines); package.add("GetLockoutExpeditionUUID", &Perl_Client_GetLockoutExpeditionUUID); package.add("GetMaxEndurance", &Perl_Client_GetMaxEndurance); + package.add("GetMerc", &Perl_Client_GetMerc); package.add("GetMemmedSpells", &Perl_Client_GetMemmedSpells); package.add("GetModCharacterFactionLevel", &Perl_Client_GetModCharacterFactionLevel); package.add("GetMoney", &Perl_Client_GetMoney); diff --git a/zone/perl_merc.cpp b/zone/perl_merc.cpp new file mode 100644 index 000000000..00f7e4625 --- /dev/null +++ b/zone/perl_merc.cpp @@ -0,0 +1,195 @@ +#include "../common/features.h" +#ifdef EMBPERL_XS_CLASSES +#include "../common/global_define.h" +#include "embperl.h" +#include "merc.h" + +uint32 Perl_Merc_GetCostFormula(Merc* self) +{ + return self->GetCostFormula(); +} + +Group* Perl_Merc_GetGroup(Merc* self) +{ + return self->GetGroup(); +} + +int Perl_Merc_GetHatedCount(Merc* self) +{ + return self->GetHatedCount(); +} + +float Perl_Merc_GetMaxMeleeRangeToTarget(Merc* self, Mob* target) +{ + return self->GetMaxMeleeRangeToTarget(target); +} + +uint32 Perl_Merc_GetMercenaryCharacterID(Merc* self) +{ + return self->GetMercenaryCharacterID(); +} + +uint32 Perl_Merc_GetMercenaryID(Merc* self) +{ + return self->GetMercenaryID(); +} + +uint32 Perl_Merc_GetMercenaryNameType(Merc* self) +{ + return self->GetMercNameType(); +} + +Client* Perl_Merc_GetMercenaryOwner(Merc* self) +{ + return self->GetMercenaryOwner(); +} + +uint32 Perl_Merc_GetMercenarySubtype(Merc* self) +{ + return self->GetMercenarySubType(); +} + +uint32 Perl_Merc_GetMercenaryTemplateID(Merc* self) +{ + return self->GetMercenaryTemplateID(); +} + +uint32 Perl_Merc_GetMercenaryType(Merc* self) +{ + return self->GetMercenaryType(); +} + +Mob* Perl_Merc_GetOwner(Merc* self) +{ + return self->GetOwner(); +} + +Mob* Perl_Merc_GetOwnerOrSelf(Merc* self) +{ + return self->GetOwnerOrSelf(); +} + +uint8 Perl_Merc_GetProficiencyID(Merc* self) +{ + return self->GetProficiencyID(); +} + +uint8 Perl_Merc_GetStance(Merc* self) +{ + return self->GetStance(); +} + +uint8 Perl_Merc_GetTierID(Merc* self) +{ + return self->GetTierID(); +} + +bool Perl_Merc_HasOrMayGetAggro(Merc* self) +{ + return self->HasOrMayGetAggro(); +} + +bool Perl_Merc_IsMercenaryCaster(Merc* self) +{ + return self->IsMercCaster(); +} + +bool Perl_Merc_IsMercenaryCasterCombatRange(Merc* self, Mob* target) +{ + return self->IsMercCasterCombatRange(target); +} + +bool Perl_Merc_IsSitting(Merc* self) +{ + return self->IsSitting(); +} + +bool Perl_Merc_IsStanding(Merc* self) +{ + return self->IsStanding(); +} + +void Perl_Merc_ScaleStats(Merc* self, int scale_percentage) +{ + self->ScaleStats(scale_percentage); +} + +void Perl_Merc_ScaleStats(Merc* self, int scale_percentage, bool set_to_max) +{ + self->ScaleStats(scale_percentage, set_to_max); +} + +void Perl_Merc_SendPayload(Merc* self, int payload_id, std::string payload_value) +{ + self->SendPayload(payload_id, payload_value); +} + +void Perl_Merc_SetTarget(Merc* self, Mob* target) +{ + self->SetTarget(target); +} + +void Perl_Merc_Signal(Merc* self, int signal_id) +{ + self->Signal(signal_id); +} + +void Perl_Merc_Sit(Merc* self) +{ + self->Sit(); +} + +void Perl_Merc_Stand(Merc* self) +{ + self->Stand(); +} + +bool Perl_Merc_Suspend(Merc* self) +{ + return self->Suspend(); +} + +bool Perl_Merc_UseDiscipline(Merc* self, uint16 spell_id, uint16 target_id) +{ + return self->UseDiscipline(spell_id, target_id); +} + +void perl_register_merc() +{ + perl::interpreter state(PERL_GET_THX); + + auto package = state.new_class("Merc"); + package.add_base_class("NPC"); + package.add("GetCostFormula", &Perl_Merc_GetCostFormula); + package.add("GetGroup", &Perl_Merc_GetGroup); + package.add("GetHatedCount", &Perl_Merc_GetHatedCount); + package.add("GetMaxMeleeRangeToTarget", &Perl_Merc_GetMaxMeleeRangeToTarget); + package.add("GetMercenaryCharacterID", &Perl_Merc_GetMercenaryCharacterID); + package.add("GetMercenaryID", &Perl_Merc_GetMercenaryID); + package.add("GetMercenaryNameType", &Perl_Merc_GetMercenaryNameType); + package.add("GetMercenaryOwner", &Perl_Merc_GetMercenaryOwner); + package.add("GetMercenarySubtype", &Perl_Merc_GetMercenarySubtype); + package.add("GetMercenaryTemplateID", &Perl_Merc_GetMercenaryTemplateID); + package.add("GetMercenaryType", &Perl_Merc_GetMercenaryType); + package.add("GetOwner", &Perl_Merc_GetOwner); + package.add("GetOwnerOrSelf", &Perl_Merc_GetOwnerOrSelf); + package.add("GetProficiencyID", &Perl_Merc_GetProficiencyID); + package.add("GetStance", &Perl_Merc_GetStance); + package.add("GetTierID", &Perl_Merc_GetTierID); + package.add("HasOrMayGetAggro", &Perl_Merc_HasOrMayGetAggro); + package.add("IsMercenaryCaster", &Perl_Merc_IsMercenaryCaster); + package.add("IsMercenaryCasterCombatRange", &Perl_Merc_IsMercenaryCasterCombatRange); + package.add("IsSitting", &Perl_Merc_IsSitting); + package.add("IsStanding", &Perl_Merc_IsStanding); + package.add("ScaleStats", (void(*)(Merc*, int))&Perl_Merc_ScaleStats); + package.add("ScaleStats", (void(*)(Merc*, int, bool))&Perl_Merc_ScaleStats); + package.add("SendPayload", &Perl_Merc_SendPayload); + package.add("SetTarget", &Perl_Merc_SetTarget); + package.add("Signal", &Perl_Merc_Signal); + package.add("Sit", &Perl_Merc_Sit); + package.add("Stand", &Perl_Merc_Stand); + package.add("Suspend", &Perl_Merc_Suspend); + package.add("UseDiscipline", &Perl_Merc_UseDiscipline); +} + +#endif //EMBPERL_XS_CLASSES diff --git a/zone/quest_interface.h b/zone/quest_interface.h index deeaa4df3..687a90f58 100644 --- a/zone/quest_interface.h +++ b/zone/quest_interface.h @@ -139,6 +139,30 @@ public: return 0; } + virtual int EventMerc( + QuestEventID event_id, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ) + { + return 0; + } + + virtual int EventGlobalMerc( + QuestEventID event_id, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ) + { + return 0; + } + virtual bool HasQuestSub(uint32 npc_id, QuestEventID event_id) { return false; @@ -189,6 +213,16 @@ public: return false; } + virtual bool MercHasQuestSub(QuestEventID event_id) + { + return false; + } + + virtual bool GlobalMercHasQuestSub(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) { } @@ -198,6 +232,8 @@ public: virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { } virtual void LoadBotScript(std::string filename) { } virtual void LoadGlobalBotScript(std::string filename) { } + virtual void LoadMercScript(std::string filename) { } + virtual void LoadGlobalMercScript(std::string filename) { } virtual int DispatchEventNPC( QuestEventID event_id, @@ -260,6 +296,18 @@ public: return 0; } + virtual int DispatchEventMerc( + QuestEventID event_id, + Merc* merc, + Mob* init, + 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 dccdbdd67..ba9b5745b 100644 --- a/zone/quest_parser_collection.cpp +++ b/zone/quest_parser_collection.cpp @@ -47,6 +47,8 @@ QuestParserCollection::QuestParserCollection() _global_npc_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded; + _merc_quest_status = QuestUnloaded; + _global_merc_quest_status = QuestUnloaded; } QuestParserCollection::~QuestParserCollection() { } @@ -94,6 +96,8 @@ void QuestParserCollection::ReloadQuests(bool reset_timers) _global_npc_quest_status = QuestUnloaded; _bot_quest_status = QuestUnloaded; _global_bot_quest_status = QuestUnloaded; + _merc_quest_status = QuestUnloaded; + _global_merc_quest_status = QuestUnloaded; _spell_quest_status.clear(); _item_quest_status.clear(); @@ -379,6 +383,49 @@ 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); +} + int QuestParserCollection::EventNPC( QuestEventID event_id, NPC* npc, @@ -793,6 +840,86 @@ int QuestParserCollection::EventBotGlobal( 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; +} + QuestInterface* QuestParserCollection::GetQIByNPCQuest(uint32 npc_id, std::string& filename) { if (!zone) { @@ -1188,6 +1315,81 @@ QuestInterface* QuestParserCollection::GetQIByGlobalBotQuest(std::string& filena return nullptr; } +QuestInterface* QuestParserCollection::GetQIByMercQuest(std::string& filename) +{ + if (!zone || !zone->IsLoaded()) { + return nullptr; + } + + const std::string& global_path = fmt::format( + "{}/{}", + path.GetQuestsPath(), + QUEST_GLOBAL_DIRECTORY + ); + + const std::string& zone_path = fmt::format( + "{}/{}", + path.GetQuestsPath(), + zone->GetShortName() + ); + + const std::string& zone_versioned_path = fmt::format( + "{}/{}/v{}", + path.GetQuestsPath(), + 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* e: _load_precedence) { + file_name = fmt::format( + "{}/{}/global_merc.{}", + path.GetQuestsPath(), + 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(); @@ -1303,6 +1505,27 @@ int QuestParserCollection::DispatchEventBot( 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; +} + void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* s) { for (int i = 0; i < _LargestEventID; i++) { @@ -1325,3 +1548,81 @@ void QuestParserCollection::LoadPerlEventExportSettings(PerlEventExportSettings* 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 +} diff --git a/zone/quest_parser_collection.h b/zone/quest_parser_collection.h index 30997c709..cb0a5e8d1 100644 --- a/zone/quest_parser_collection.h +++ b/zone/quest_parser_collection.h @@ -71,6 +71,7 @@ public: bool SpellHasQuestSub(uint32 spell_id, QuestEventID event_id); bool ItemHasQuestSub(EQ::ItemInstance* inst, QuestEventID event_id); bool BotHasQuestSub(QuestEventID event_id); + bool MercHasQuestSub(QuestEventID event_id); int EventNPC( QuestEventID event_id, @@ -126,6 +127,51 @@ public: std::vector *extra_pointers = nullptr ); + int EventMerc( + QuestEventID event_id, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector *extra_pointers = nullptr + ); + + int EventBotMerc( + QuestEventID event_id, + Mob* e, + Mob* init, + std::function lazy_data = []() { return ""; }, + uint32 extra_data = 0, + std::vector* extra_pointers = nullptr + ); + + int EventMercNPC( + QuestEventID event_id, + Mob* e, + Mob* init, + std::function lazy_data = []() { return ""; }, + uint32 extra_data = 0, + std::vector* extra_pointers = nullptr + ); + + int EventBotMercNPC( + QuestEventID event_id, + Mob* e, + Mob* init, + std::function lazy_data = []() { return ""; }, + uint32 extra_data = 0, + std::vector* extra_pointers = nullptr + ); + + int EventMob( + QuestEventID event_id, + Mob* e, + Mob* init, + std::function lazy_data = []() { return ""; }, + uint32 extra_data = 0, + std::vector* extra_pointers = nullptr + ); + void GetErrors(std::list &quest_errors); /* @@ -161,6 +207,8 @@ private: bool HasEncounterSub(QuestEventID event_id, const std::string& package_name); bool BotHasQuestSubLocal(QuestEventID event_id); bool BotHasQuestSubGlobal(QuestEventID event_id); + bool MercHasQuestSubLocal(QuestEventID event_id); + bool MercHasQuestSubGlobal(QuestEventID event_id); int EventNPCLocal( QuestEventID event_id, @@ -214,6 +262,24 @@ private: std::vector *extra_pointers ); + int EventMercLocal( + QuestEventID event_id, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + + int EventMercGlobal( + QuestEventID event_id, + Merc* merc, + Mob* init, + 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); @@ -223,6 +289,8 @@ private: QuestInterface* GetQIByEncounterQuest(std::string encounter_name, std::string& filename); QuestInterface* GetQIByBotQuest(std::string& filename); QuestInterface* GetQIByGlobalBotQuest(std::string& filename); + QuestInterface* GetQIByMercQuest(std::string& filename); + QuestInterface* GetQIByGlobalMercQuest(std::string& filename); int DispatchEventNPC( QuestEventID event_id, @@ -270,6 +338,15 @@ private: std::vector* extra_pointers ); + int DispatchEventMerc( + QuestEventID event_id, + Merc* merc, + Mob* init, + std::string data, + uint32 extra_data, + std::vector* extra_pointers + ); + std::map _interfaces; std::map _extensions; std::list _load_precedence; @@ -280,6 +357,8 @@ private: uint32 _global_player_quest_status; uint32 _bot_quest_status; uint32 _global_bot_quest_status; + uint32 _merc_quest_status; + uint32 _global_merc_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 0cf61bb9d..7ef1bace4 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -90,12 +90,9 @@ void QuestManager::Process() { while (cur != end) { if (cur->Timer_.Enabled() && cur->Timer_.Check()) { if (cur->mob) { - if (cur->mob->IsNPC()) { - if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) { - parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0); - } - } - else if (cur->mob->IsEncounter()) { + parse->EventMob(EVENT_TIMER, cur->mob, nullptr, [&]() { return cur->name; }, 0); + + if (cur->mob->IsEncounter()) { parse->EventEncounter( EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), @@ -104,17 +101,6 @@ void QuestManager::Process() { nullptr ); } - else if (cur->mob->IsClient()) { - if (parse->PlayerHasQuestSub(EVENT_TIMER)) { - //this is inheriently unsafe if we ever make it so more than npc/client start timers - parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0); - } - } - else if (cur->mob->IsBot()) { - if (parse->BotHasQuestSub(EVENT_TIMER)) { - parse->EventBot(EVENT_TIMER, cur->mob->CastToBot(), nullptr, cur->name, 0); - } - } //we MUST reset our iterator since the quest could have removed/added any //number of timers... worst case we have to check a bunch of timers twice @@ -539,32 +525,20 @@ void QuestManager::settimer(const std::string& timer_name, uint32 seconds, Mob* return; } - const bool has_start_event = ( - (mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || - (mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || - (mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_START)) - ); + std::function f = [&]() { + return fmt::format( + "{} {}", + timer_name, + seconds * 1000 + ); + }; if (!QTimerList.empty()) { for (auto& e : QTimerList) { if (e.mob && e.mob == mob && e.name == timer_name) { e.Timer_.Start(seconds * 1000, false); - if (has_start_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - seconds * 1000 - ); - - if (mob->IsClient()) { - parse->EventPlayer(EVENT_TIMER_START, mob->CastToClient(), export_string, 0); - } else if (mob->IsBot()) { - parse->EventBot(EVENT_TIMER_START, mob->CastToBot(), nullptr, export_string, 0); - } else if (mob->IsNPC()) { - parse->EventNPC(EVENT_TIMER_START, mob->CastToNPC(), nullptr, export_string, 0); - } - } + parse->EventMob(EVENT_TIMER_START, mob, nullptr, f); return; } @@ -573,21 +547,7 @@ void QuestManager::settimer(const std::string& timer_name, uint32 seconds, Mob* QTimerList.emplace_back(QuestTimer(seconds * 1000, mob, timer_name)); - if (has_start_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - seconds * 1000 - ); - - if (mob->IsClient()) { - parse->EventPlayer(EVENT_TIMER_START, mob->CastToClient(), export_string, 0); - } else if (mob->IsBot()) { - parse->EventBot(EVENT_TIMER_START, mob->CastToBot(), nullptr, export_string, 0); - } else if (mob->IsNPC()) { - parse->EventNPC(EVENT_TIMER_START, mob->CastToNPC(), nullptr, export_string, 0); - } - } + parse->EventMob(EVENT_TIMER_START, mob, nullptr, f); } void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds) @@ -598,11 +558,13 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds return; } - const bool has_start_event = ( - (owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || - (owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || - (owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_START)) - ); + std::function f = [&]() { + return fmt::format( + "{} {}", + timer_name, + milliseconds + ); + }; if (questitem) { questitem->SetTimer(timer_name, milliseconds); @@ -625,21 +587,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds if (e.mob && e.mob == owner && e.name == timer_name) { e.Timer_.Start(milliseconds, false); - if (has_start_event) { - const std::string& export_string = fmt::format( - "{} {}", - e.name, - milliseconds - ); - - if (owner->IsClient()) { - parse->EventPlayer(EVENT_TIMER_START, owner->CastToClient(), export_string, 0); - } else if (owner->IsBot()) { - parse->EventBot(EVENT_TIMER_START, owner->CastToBot(), nullptr, export_string, 0); - } else if (owner->IsNPC()) { - parse->EventNPC(EVENT_TIMER_START, owner->CastToNPC(), nullptr, export_string, 0); - } - } + parse->EventMob(EVENT_TIMER_START, owner, nullptr, f); return; } @@ -648,21 +596,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds QTimerList.emplace_back(QuestTimer(milliseconds, owner, timer_name)); - if (has_start_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - milliseconds - ); - - if (owner->IsClient()) { - parse->EventPlayer(EVENT_TIMER_START, owner->CastToClient(), export_string, 0); - } else if (owner->IsBot()) { - parse->EventBot(EVENT_TIMER_START, owner->CastToBot(), nullptr, export_string, 0); - } else if (owner->IsNPC()) { - parse->EventNPC(EVENT_TIMER_START, owner->CastToNPC(), nullptr, export_string, 0); - } - } + parse->EventMob(EVENT_TIMER_START, owner, nullptr, f); } void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds, EQ::ItemInstance* inst) @@ -678,32 +612,20 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds return; } - const bool has_start_event = ( - (m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_START)) || - (m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_START)) || - (m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_START)) - ); + std::function f = [&]() { + return fmt::format( + "{} {}", + timer_name, + milliseconds + ); + }; if (!QTimerList.empty()) { for (auto& e : QTimerList) { if (e.mob && e.mob == m && e.name == timer_name) { e.Timer_.Start(milliseconds, false); - if (has_start_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - milliseconds - ); - - if (m->IsClient()) { - parse->EventPlayer(EVENT_TIMER_START, m->CastToClient(), export_string, 0); - } else if (m->IsBot()) { - parse->EventBot(EVENT_TIMER_START, m->CastToBot(), nullptr, export_string, 0); - } else if (m->IsNPC()) { - parse->EventNPC(EVENT_TIMER_START, m->CastToNPC(), nullptr, export_string, 0); - } - } + parse->EventMob(EVENT_TIMER_START, m, nullptr, f); return; } @@ -712,21 +634,7 @@ void QuestManager::settimerMS(const std::string& timer_name, uint32 milliseconds QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name)); - if (has_start_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - milliseconds - ); - - if (m->IsClient()) { - parse->EventPlayer(EVENT_TIMER_START, m->CastToClient(), export_string, 0); - } else if (m->IsBot()) { - parse->EventBot(EVENT_TIMER_START, m->CastToBot(), nullptr, export_string, 0); - } else if (m->IsNPC()) { - parse->EventNPC(EVENT_TIMER_START, m->CastToNPC(), nullptr, export_string, 0); - } - } + parse->EventMob(EVENT_TIMER_START, m, nullptr, f); } void QuestManager::stoptimer(const std::string& timer_name) @@ -751,23 +659,16 @@ void QuestManager::stoptimer(const std::string& timer_name) return; } - const bool has_stop_event = ( - (owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) || - (owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) || - (owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_STOP)) - ); - for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { if (e->mob && e->mob == owner && e->name == timer_name) { - if (has_stop_event) { - if (owner->IsClient()) { - parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), timer_name, 0); - } else if (owner->IsBot()) { - parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, timer_name, 0); - } else if (owner->IsNPC()) { - parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, timer_name, 0); + parse->EventMob( + EVENT_TIMER_STOP, + owner, + nullptr, + [&]() { + return timer_name; } - } + ); QTimerList.erase(e); break; @@ -792,23 +693,16 @@ void QuestManager::stoptimer(const std::string& timer_name, Mob* m) return; } - const bool has_stop_event = ( - (m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) || - (m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) || - (m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_STOP)) - ); - for (auto e = QTimerList.begin(); e != QTimerList.end();) { if (e->mob && e->mob == m) { - if (has_stop_event) { - if (m->IsClient()) { - parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0); - } else if (m->IsBot()) { - parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0); - } else if (m->IsNPC()) { - parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0); + parse->EventMob( + EVENT_TIMER_STOP, + m, + nullptr, + [&]() { + return timer_name; } - } + ); QTimerList.erase(e); break; @@ -847,23 +741,16 @@ void QuestManager::stopalltimers() return; } - const bool has_stop_event = ( - (owner->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) || - (owner->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) || - (owner->IsNPC() && parse->HasQuestSub(owner->GetNPCTypeID(), EVENT_TIMER_STOP)) - ); - for (auto e = QTimerList.begin(); e != QTimerList.end();) { if (e->mob && e->mob == owner) { - if (has_stop_event) { - if (owner->IsClient()) { - parse->EventPlayer(EVENT_TIMER_STOP, owner->CastToClient(), e->name, 0); - } else if (owner->IsBot()) { - parse->EventBot(EVENT_TIMER_STOP, owner->CastToBot(), nullptr, e->name, 0); - } else if (owner->IsNPC()) { - parse->EventNPC(EVENT_TIMER_STOP, owner->CastToNPC(), nullptr, e->name, 0); + parse->EventMob( + EVENT_TIMER_STOP, + owner, + nullptr, + [&]() { + return e->name; } - } + ); e = QTimerList.erase(e); } else { @@ -903,23 +790,16 @@ void QuestManager::stopalltimers(Mob* m) return; } - const bool has_stop_event = ( - (m->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_STOP)) || - (m->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_STOP)) || - (m->IsNPC() && parse->HasQuestSub(m->GetNPCTypeID(), EVENT_TIMER_STOP)) - ); - for (auto e = QTimerList.begin(); e != QTimerList.end();) { if (e->mob && e->mob == m) { - if (has_stop_event) { - if (m->IsClient()) { - parse->EventPlayer(EVENT_TIMER_STOP, m->CastToClient(), e->name, 0); - } else if (m->IsBot()) { - parse->EventBot(EVENT_TIMER_STOP, m->CastToBot(), nullptr, e->name, 0); - } else if (m->IsNPC()) { - parse->EventNPC(EVENT_TIMER_STOP, m->CastToNPC(), nullptr, e->name, 0); + parse->EventMob( + EVENT_TIMER_STOP, + m, + nullptr, + [&]() { + return e->name; } - } + ); e = QTimerList.erase(e); } else { @@ -955,12 +835,6 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m) uint32 milliseconds = 0; - const bool has_pause_event = ( - (mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_PAUSE)) || - (mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_PAUSE)) || - (mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_PAUSE)) - ); - if (!QTimerList.empty()) { for (auto e = QTimerList.begin(); e != QTimerList.end(); ++e) { if (e->mob && e->mob == mob && e->name == timer_name) { @@ -979,21 +853,18 @@ void QuestManager::pausetimer(const std::string& timer_name, Mob* m) } ); - if (has_pause_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - milliseconds - ); - - if (mob->IsClient()) { - parse->EventPlayer(EVENT_TIMER_PAUSE, mob->CastToClient(), export_string, 0); - } else if (mob->IsBot()) { - parse->EventBot(EVENT_TIMER_PAUSE, mob->CastToBot(), nullptr, export_string, 0); - } else if (mob->IsNPC()) { - parse->EventNPC(EVENT_TIMER_PAUSE, mob->CastToNPC(), nullptr, export_string, 0); + parse->EventMob( + EVENT_TIMER_PAUSE, + mob, + nullptr, + [&]() { + return fmt::format( + "{} {}", + timer_name, + milliseconds + ); } - } + ); LogQuests("Pausing timer [{}] for [{}] with [{}] ms remaining", timer_name, owner->GetName(), milliseconds); } @@ -1031,11 +902,13 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m) return; } - const bool has_resume_event = ( - (mob->IsClient() && parse->PlayerHasQuestSub(EVENT_TIMER_RESUME)) || - (mob->IsBot() && parse->BotHasQuestSub(EVENT_TIMER_RESUME)) || - (mob->IsNPC() && parse->HasQuestSub(mob->GetNPCTypeID(), EVENT_TIMER_RESUME)) - ); + std::function f = [&]() { + return fmt::format( + "{} {}", + timer_name, + milliseconds + ); + }; if (!QTimerList.empty()) { for (auto e : QTimerList) { @@ -1049,21 +922,8 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m) milliseconds ); - if (has_resume_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - milliseconds - ); + parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f); - if (mob->IsClient()) { - parse->EventPlayer(EVENT_TIMER_RESUME, mob->CastToClient(), export_string, 0); - } else if (mob->IsBot()) { - parse->EventBot(EVENT_TIMER_RESUME, mob->CastToBot(), nullptr, export_string, 0); - } else if (mob->IsNPC()) { - parse->EventNPC(EVENT_TIMER_RESUME, mob->CastToNPC(), nullptr, export_string, 0); - } - } return; } } @@ -1071,21 +931,7 @@ void QuestManager::resumetimer(const std::string& timer_name, Mob* m) QTimerList.emplace_back(QuestTimer(milliseconds, m, timer_name)); - if (has_resume_event) { - const std::string& export_string = fmt::format( - "{} {}", - timer_name, - milliseconds - ); - - if (mob->IsClient()) { - parse->EventPlayer(EVENT_TIMER_RESUME, mob->CastToClient(), export_string, 0); - } else if (mob->IsBot()) { - parse->EventBot(EVENT_TIMER_RESUME, mob->CastToBot(), nullptr, export_string, 0); - } else if (mob->IsNPC()) { - parse->EventNPC(EVENT_TIMER_RESUME, mob->CastToNPC(), nullptr, export_string, 0); - } - } + parse->EventMob(EVENT_TIMER_RESUME, mob, nullptr, f); LogQuests( "Creating a new timer and resuming [{}] for [{}] with [{}] ms remaining", @@ -4213,6 +4059,15 @@ Bot *QuestManager::GetBot() const { return nullptr; } +Merc *QuestManager::GetMerc() const { + if (!quests_running_.empty()) { + running_quest e = quests_running_.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(); diff --git a/zone/questmgr.h b/zone/questmgr.h index 31e177ed6..463a3baa6 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -360,6 +360,7 @@ public: Bot *GetBot() const; Client *GetInitiator() const; + Merc* GetMerc() const; NPC *GetNPC() const; Mob *GetOwner() const; EQ::InventoryProfile* GetInventory() const; diff --git a/zone/spells.cpp b/zone/spells.cpp index e8e3e2757..5a1b427e4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -254,53 +254,33 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } } - if (IsClient()) { - if (parse->PlayerHasQuestSub(EVENT_CAST_BEGIN)) { - Mob* spell_target = entity_list.GetMobID(target_id); - std::vector args = { spell_target }; - const auto& export_string = fmt::format( + Mob* spell_target = entity_list.GetMobID(target_id); + std::vector args = { spell_target }; + int return_value = parse->EventMob( + EVENT_CAST_BEGIN, + this, + nullptr, + [&]() { + return fmt::format( "{} {} {} {}", spell_id, GetID(), GetCasterLevel(spell_id), target_id ); - if (parse->EventPlayer(EVENT_CAST_BEGIN, CastToClient(), export_string, 0, &args) != 0) { - if (IsDiscipline(spell_id)) { - CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0); - } - else { - CastToClient()->SendSpellBarEnable(spell_id); - } - return false; - } - } - } else if (IsNPC()) { - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_CAST_BEGIN)) { - Mob* spell_target = entity_list.GetMobID(target_id); - std::vector args = { spell_target }; - const auto& export_string = fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - GetCasterLevel(spell_id), - target_id - ); - parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, export_string, 0, &args); - } - } else if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_CAST_BEGIN)) { - Mob* spell_target = entity_list.GetMobID(target_id); - std::vector args = { spell_target }; - const auto& export_string = fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - GetCasterLevel(spell_id), - target_id - ); - parse->EventBot(EVENT_CAST_BEGIN, CastToBot(), nullptr, export_string, 0, &args); + }, + 0, + &args + ); + + if (IsClient() && return_value != 0) { + if (IsDiscipline(spell_id)) { + CastToClient()->SendDisciplineTimer(spells[spell_id].timer_id, 0); + } else { + CastToClient()->SendSpellBarEnable(spell_id); } + + return false; } //To prevent NPC ghosting when spells are cast from scripts @@ -1822,47 +1802,24 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo } } - // - // at this point the spell has successfully been cast - // + std::vector args = { spell_target }; - if (IsClient()) { - if (parse->PlayerHasQuestSub(EVENT_CAST)) { - std::vector args = { spell_target }; - const auto& export_string = fmt::format( + parse->EventMob( + EVENT_CAST, + this, + nullptr, + [&]() { + return fmt::format( "{} {} {} {}", spell_id, GetID(), GetCasterLevel(spell_id), target_id ); - parse->EventPlayer(EVENT_CAST, CastToClient(), export_string, 0, &args); - } - } else if (IsNPC()) { - if (parse->HasQuestSub(GetNPCTypeID(), EVENT_CAST)) { - std::vector args = { spell_target }; - const auto& export_string = fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - GetCasterLevel(spell_id), - target_id - ); - parse->EventNPC(EVENT_CAST, CastToNPC(), nullptr, export_string, 0, &args); - } - } else if (IsBot()) { - if (parse->BotHasQuestSub(EVENT_CAST)) { - std::vector args = { spell_target }; - const auto& export_string = fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - GetCasterLevel(spell_id), - target_id - ); - parse->EventBot(EVENT_CAST, CastToBot(), nullptr, export_string, 0, &args); - } - } + }, + 0, + &args + ); if(bard_song_mode) { @@ -3622,44 +3579,18 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid ); } - const bool caster_has_block_event = ( - (caster->IsBot() && parse->BotHasQuestSub(EVENT_SPELL_BLOCKED)) || - (caster->IsClient() && parse->PlayerHasQuestSub(EVENT_SPELL_BLOCKED)) || - (caster->IsNPC() && parse->HasQuestSub(caster->GetNPCTypeID(), EVENT_SPELL_BLOCKED)) - ); - - const bool cast_on_has_block_event = ( - (IsBot() && parse->BotHasQuestSub(EVENT_SPELL_BLOCKED)) || - (IsClient() && parse->PlayerHasQuestSub(EVENT_SPELL_BLOCKED)) || - (IsNPC() && parse->HasQuestSub(GetNPCTypeID(), EVENT_SPELL_BLOCKED)) - ); - - if (caster_has_block_event || cast_on_has_block_event) { - const std::string& export_string = fmt::format( + std::function f = [&]() { + return fmt::format( "{} {}", curbuf.spellid, spell_id ); + }; - if (caster_has_block_event) { - if (caster->IsBot()) { - parse->EventBot(EVENT_SPELL_BLOCKED, caster->CastToBot(), this, export_string, 0); - } else if (caster->IsClient()) { - parse->EventPlayer(EVENT_SPELL_BLOCKED, caster->CastToClient(), export_string, 0); - } else if (caster->IsNPC()) { - parse->EventNPC(EVENT_SPELL_BLOCKED, caster->CastToNPC(), this, export_string, 0); - } - } + parse->EventMob(EVENT_SPELL_BLOCKED, caster, this, f); - if (cast_on_has_block_event && caster != this) { - if (IsBot()) { - parse->EventBot(EVENT_SPELL_BLOCKED, CastToBot(), caster, export_string, 0); - } else if (IsClient()) { - parse->EventPlayer(EVENT_SPELL_BLOCKED, CastToClient(), export_string, 0); - } else if (IsNPC()) { - parse->EventNPC(EVENT_SPELL_BLOCKED, CastToNPC(), caster, export_string, 0); - } - } + if (caster != this) { + parse->EventMob(EVENT_SPELL_BLOCKED, this, caster, f); } } @@ -4033,43 +3964,24 @@ bool Mob::SpellOnTarget( (spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells) /* EQ Filter Type: (8 or 9) */ ); - if (spelltar->IsNPC()) { - if (parse->HasQuestSub(spelltar->GetNPCTypeID(), EVENT_CAST_ON)) { - std::vector args = { spelltar }; - const auto& export_string = fmt::format( + std::vector args = { spelltar }; + + parse->EventMob( + EVENT_CAST_ON, + spelltar, + this, + [&]() { + return fmt::format( "{} {} {} {}", spell_id, GetID(), caster_level, target_id ); - parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, export_string, 0, &args); - } - } else if (spelltar->IsClient()) { - if (parse->PlayerHasQuestSub(EVENT_CAST_ON)) { - std::vector args = { spelltar }; - const auto& export_string = fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - caster_level, - target_id - ); - parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(), export_string, 0, &args); - } - } else if (spelltar->IsBot()) { - if (parse->BotHasQuestSub(EVENT_CAST_ON)) { - std::vector args = { spelltar }; - const auto& export_string = fmt::format( - "{} {} {} {}", - spell_id, - GetID(), - caster_level, - target_id - ); - parse->EventBot(EVENT_CAST_ON, spelltar->CastToBot(), this, export_string, 0, &args); - } - } + }, + 0, + &args + ); if (!DoCastingChecksOnTarget(false, spell_id, spelltar)) { safe_delete(action_packet);