diff --git a/common/ruletypes.h b/common/ruletypes.h index 533567b0e..7f463e733 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -601,6 +601,8 @@ RULE_INT(Chat, KarmaGlobalChatLimit, 72, "Amount of karma you need to be able to RULE_INT(Chat, GlobalChatLevelLimit, 8, "Level limit you need to of reached to talk in ooc/auction/chat if your karma is too low") RULE_BOOL(Chat, AutoInjectSaylinksToSay, true, "Automatically injects saylinks into dialogue that has [brackets in them]") RULE_BOOL(Chat, AutoInjectSaylinksToClientMessage, true, "Automatically injects saylinks into dialogue that has [brackets in them]") +RULE_BOOL(Chat, QuestDialogueUsesDialogueWindow, false, "Pipes all quest dialogue to dialogue window") +RULE_BOOL(Chat, DialogueWindowAnimatesNPCsIfNoneSet, true, "If there is no animation specified in the dialogue window markdown then it will choose a random greet animation such as wave or salute") RULE_CATEGORY_END() RULE_CATEGORY(Merchant) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index a74f65180..513cd5942 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -5001,7 +5001,7 @@ void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) if (parse->EventPlayer(EVENT_CONSIDER_CORPSE, this, fmt::format("{}", conin->targetid), 0) == 1) { return; } - + uint32 day, hour, min, sec, ttime; if ((ttime = tcorpse->GetDecayTime()) != 0) { sec = (ttime / 1000) % 60; // Total seconds diff --git a/zone/dialogue_window.cpp b/zone/dialogue_window.cpp index 941bcbc82..bae0d8533 100644 --- a/zone/dialogue_window.cpp +++ b/zone/dialogue_window.cpp @@ -4,6 +4,10 @@ void DialogueWindow::Render(Client *c, std::string markdown) { std::string output = markdown; + if (!c->ClientDataLoaded()) { + return; + } + // this is the NPC that the client is interacting with if there is dialogue going on Mob *target; if (c->GetTarget()) { @@ -81,6 +85,21 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } + if (animation.empty() && RuleB(Chat, DialogueWindowAnimatesNPCsIfNoneSet)) { + std::vector greet_animations = { + 29, // wave + 48, // nodyes + 64, // point + 67, // salute + 69, // tapfoot + 70, // bowto + }; + + int random_animation = rand() % (greet_animations.size() - 1) + 0; + + target->DoAnim(greet_animations[random_animation]); + } + // window expire time std::string expire_time = get_between(output, "=", "="); uint32 window_expire_seconds = 0; @@ -234,7 +253,7 @@ void DialogueWindow::Render(Client *c, std::string markdown) button_one_name = button_one.c_str(); } } - + LogDiaWind("Client [{}] Rendering button_two option.", c->GetCleanName()); auto two_first_split = split_string(output, "button_two:"); @@ -266,6 +285,33 @@ void DialogueWindow::Render(Client *c, std::string markdown) std::vector responses; std::vector bracket_responses; if (markdown.find('[') != std::string::npos && markdown.find(']') != std::string::npos) { + + // record any saylinks that may be in saylink form + std::string strip_saylinks = output; + std::map replacements = {}; + while (strip_saylinks.find('[') != std::string::npos && strip_saylinks.find(']') != std::string::npos) { + std::string bracket_message = get_between(strip_saylinks, "[", "]"); + + // strip saylinks and normalize to [regular message] + size_t link_open = bracket_message.find('\x12'); + size_t link_close = bracket_message.find_last_of('\x12'); + if (link_open != link_close && (bracket_message.length() - link_open) > EQ::constants::SAY_LINK_BODY_SIZE) { + replacements.insert( + std::pair( + bracket_message, + bracket_message.substr(EQ::constants::SAY_LINK_BODY_SIZE + 1) + ) + ); + } + + find_replace(strip_saylinks, fmt::format("[{}]", bracket_message), ""); + } + + // write replacement strips + for (auto &replacement: replacements) { + find_replace(output, replacement.first, replacement.second.substr(0, replacement.second.size() - 1)); + } + // copy std::string content = output; @@ -412,8 +458,8 @@ void DialogueWindow::Render(Client *c, std::string markdown) color_tag ); - std::string html_tag; - for (const auto& color : html_colors) { + std::string html_tag; + for (const auto &color : html_colors) { if (color_tag.find(color.first) != std::string::npos) { // build html tag html_tag = fmt::format("", color.second); @@ -426,7 +472,6 @@ void DialogueWindow::Render(Client *c, std::string markdown) } } - // build the final output string std::string final_output; final_output = fmt::format("{}{}{}

{}", quote_string, output, quote_string, click_response); diff --git a/zone/entity.cpp b/zone/entity.cpp index 12898f0ea..47e03afba 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -43,6 +43,7 @@ #include "water_map.h" #include "npc_scale_manager.h" #include "../common/say_link.h" +#include "dialogue_window.h" #ifdef _WINDOWS #define snprintf _snprintf @@ -4202,8 +4203,25 @@ void EntityList::QuestJournalledSayClose( buf.WriteInt32(0); buf.WriteInt32(0); - // auto inject saylinks (say) - if (RuleB(Chat, AutoInjectSaylinksToSay)) { + if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { + for (auto &e : GetCloseMobList(sender, (dist * dist))) { + Mob *mob = e.second; + + if (!mob->IsClient()) { + continue; + } + + Client *client = mob->CastToClient(); + + if (client->GetTarget() && client->GetTarget()->IsMob() && client->GetTarget()->CastToMob() == sender) { + std::string window_markdown = message; + DialogueWindow::Render(client, window_markdown); + } + } + + return; + } + else if (RuleB(Chat, AutoInjectSaylinksToSay)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); buf.WriteString(new_message); } diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index b37841e73..72f10be76 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -11,6 +11,7 @@ #include "lua_hate_list.h" #include "lua_client.h" #include "lua_stat_bonuses.h" +#include "dialogue_window.h" struct SpecialAbilities { }; @@ -757,7 +758,11 @@ void Lua_Mob::Message(int type, const char *message) { Lua_Safe_Call_Void(); // auto inject saylinks - if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { + if (RuleB(Chat, QuestDialogueUsesDialogueWindow) && self->IsClient()) { + std::string window_markdown = message; + DialogueWindow::Render(self->CastToClient(), window_markdown); + } + else if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); self->Message(type, new_message.c_str()); } diff --git a/zone/mob.cpp b/zone/mob.cpp index 96591b9e1..240496ef4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -26,6 +26,7 @@ #include "worldserver.h" #include "mob_movement_manager.h" #include "water_map.h" +#include "dialogue_window.h" #include #include @@ -2974,16 +2975,35 @@ void Mob::Say(const char *format, ...) talker = this; } - if (RuleB(Chat, AutoInjectSaylinksToSay)) { + int16 distance = 200; + + if (RuleB(Chat, QuestDialogueUsesDialogueWindow)) { + for (auto &e : entity_list.GetCloseMobList(talker, (distance * distance))) { + Mob *mob = e.second; + + if (!mob->IsClient()) { + continue; + } + + Client *client = mob->CastToClient(); + if (client->GetTarget() && client->GetTarget()->IsMob() && client->GetTarget()->CastToMob() == talker) { + std::string window_markdown = buf; + DialogueWindow::Render(client, window_markdown); + } + } + + return; + } + else if (RuleB(Chat, AutoInjectSaylinksToSay)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(buf); entity_list.MessageCloseString( - talker, false, 200, 10, + talker, false, distance, Chat::NPCQuestSay, GENERIC_SAY, GetCleanName(), new_message.c_str() ); } else { entity_list.MessageCloseString( - talker, false, 200, 10, + talker, false, distance, Chat::NPCQuestSay, GENERIC_SAY, GetCleanName(), buf ); } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 6f48c4cd8..aa7a2eaab 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -41,6 +41,7 @@ typedef const char Const_char; #include "mob.h" #include "client.h" #include "../common/spdat.h" +#include "dialogue_window.h" #ifdef BOTS #include "bot.h" @@ -2621,8 +2622,11 @@ XS(XS_Mob_Message) { char *message = (char *) SvPV_nolen(ST(2)); VALIDATE_THIS_IS_MOB; - // auto inject saylinks - if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { + if (RuleB(Chat, QuestDialogueUsesDialogueWindow) && THIS->IsClient()) { + std::string window_markdown = message; + DialogueWindow::Render(THIS->CastToClient(), window_markdown); + } + else if (RuleB(Chat, AutoInjectSaylinksToClientMessage)) { std::string new_message = EQ::SayLinkEngine::InjectSaylinksIfNotExist(message); THIS->Message(type, new_message.c_str()); }