mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
# Perl - Add `quest::silent_saylink(text)`. - Add `quest::silent_saylink(text, link_name)`. # Lua - Add `eq.silent_say_link(text)`. - Add `eq.silent_say_link(text, link_name)`. # Notes - Allows operators to more easily use silent saylinks without an optional silent parameter in the traditional saylink methods. - Sets `silent` parameter default to `false` so we do not need to pass `false` when we are not using a a silent saylink. - Changes all places that used `EQ::SayLinkEngine::GenerateQuestSaylink` to `Saylink::Create` where possible. - Removed `questmgr` method that is no longer necessary. - Cleaned up Lua methods to use the strings directly instead of building one out.
429 lines
14 KiB
C++
429 lines
14 KiB
C++
/* EQEMu: Everquest Server Emulator
|
|
|
|
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; version 2 of the License.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
|
are required to give you total support for your newly bought product;
|
|
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "say_link.h"
|
|
#include "emu_constants.h"
|
|
|
|
#include "strings.h"
|
|
#include "item_instance.h"
|
|
#include "item_data.h"
|
|
#include "../zone/zonedb.h"
|
|
#include <algorithm>
|
|
|
|
// static bucket global
|
|
std::vector<SaylinkRepository::Saylink> g_cached_saylinks = {};
|
|
|
|
bool EQ::saylink::DegenerateLinkBody(SayLinkBody_Struct &say_link_body_struct, const std::string &say_link_body)
|
|
{
|
|
memset(&say_link_body_struct, 0, sizeof(say_link_body_struct));
|
|
if (say_link_body.length() != EQ::constants::SAY_LINK_BODY_SIZE) {
|
|
return false;
|
|
}
|
|
|
|
say_link_body_struct.action_id = (uint8) strtol(say_link_body.substr(0, 1).c_str(), nullptr, 16);
|
|
say_link_body_struct.item_id = (uint32) strtol(say_link_body.substr(1, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.augment_1 = (uint32) strtol(say_link_body.substr(6, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.augment_2 = (uint32) strtol(say_link_body.substr(11, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.augment_3 = (uint32) strtol(say_link_body.substr(16, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.augment_4 = (uint32) strtol(say_link_body.substr(21, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.augment_5 = (uint32) strtol(say_link_body.substr(26, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.augment_6 = (uint32) strtol(say_link_body.substr(31, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.is_evolving = (uint8) strtol(say_link_body.substr(36, 1).c_str(), nullptr, 16);
|
|
say_link_body_struct.evolve_group = (uint32) strtol(say_link_body.substr(37, 4).c_str(), nullptr, 16);
|
|
say_link_body_struct.evolve_level = (uint8) strtol(say_link_body.substr(41, 2).c_str(), nullptr, 16);
|
|
say_link_body_struct.ornament_icon = (uint32) strtol(say_link_body.substr(43, 5).c_str(), nullptr, 16);
|
|
say_link_body_struct.hash = (uint32) strtol(say_link_body.substr(48, 8).c_str(), nullptr, 16);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EQ::saylink::GenerateLinkBody(std::string &say_link_body, const SayLinkBody_Struct &say_link_body_struct)
|
|
{
|
|
say_link_body = StringFormat(
|
|
"%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X",
|
|
(0x0F & say_link_body_struct.action_id),
|
|
(0x000FFFFF & say_link_body_struct.item_id),
|
|
(0x000FFFFF & say_link_body_struct.augment_1),
|
|
(0x000FFFFF & say_link_body_struct.augment_2),
|
|
(0x000FFFFF & say_link_body_struct.augment_3),
|
|
(0x000FFFFF & say_link_body_struct.augment_4),
|
|
(0x000FFFFF & say_link_body_struct.augment_5),
|
|
(0x000FFFFF & say_link_body_struct.augment_6),
|
|
(0x0F & say_link_body_struct.is_evolving),
|
|
(0x0000FFFF & say_link_body_struct.evolve_group),
|
|
(0xFF & say_link_body_struct.evolve_level),
|
|
(0x000FFFFF & say_link_body_struct.ornament_icon),
|
|
(0xFFFFFFFF & say_link_body_struct.hash)
|
|
);
|
|
|
|
if (say_link_body.length() != EQ::constants::SAY_LINK_BODY_SIZE) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
EQ::SayLinkEngine::SayLinkEngine()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
const std::string &EQ::SayLinkEngine::GenerateLink()
|
|
{
|
|
m_Link.clear();
|
|
m_LinkBody.clear();
|
|
m_LinkText.clear();
|
|
|
|
generate_body();
|
|
generate_text();
|
|
|
|
if ((m_LinkBody.length() == EQ::constants::SAY_LINK_BODY_SIZE) && (m_LinkText.length() > 0)) {
|
|
m_Link.push_back(0x12);
|
|
m_Link.append(m_LinkBody);
|
|
m_Link.append(m_LinkText);
|
|
m_Link.push_back(0x12);
|
|
}
|
|
|
|
if ((m_Link.length() == 0) || (m_Link.length() > (EQ::constants::SAY_LINK_MAXIMUM_SIZE))) {
|
|
m_Error = true;
|
|
m_Link = "<LINKER ERROR>";
|
|
LogError("SayLinkEngine::GenerateLink() failed to generate a useable say link");
|
|
LogError(">> LinkType: {}, Lengths: [link: {}({}), body: {}({}), text: {}({})]",
|
|
m_LinkType,
|
|
m_Link.length(),
|
|
EQ::constants::SAY_LINK_MAXIMUM_SIZE,
|
|
m_LinkBody.length(),
|
|
EQ::constants::SAY_LINK_BODY_SIZE,
|
|
m_LinkText.length(),
|
|
EQ::constants::SAY_LINK_TEXT_SIZE
|
|
);
|
|
LogError(">> LinkBody: {}", m_LinkBody.c_str());
|
|
LogError(">> LinkText: {}", m_LinkText.c_str());
|
|
}
|
|
|
|
return m_Link;
|
|
}
|
|
|
|
void EQ::SayLinkEngine::Reset()
|
|
{
|
|
m_LinkType = saylink::SayLinkBlank;
|
|
m_ItemData = nullptr;
|
|
m_LootData = nullptr;
|
|
m_ItemInst = nullptr;
|
|
|
|
memset(&m_LinkBodyStruct, 0, sizeof(SayLinkBody_Struct));
|
|
memset(&m_LinkProxyStruct, 0, sizeof(SayLinkProxy_Struct));
|
|
|
|
m_TaskUse = false;
|
|
m_Link.clear();
|
|
m_LinkBody.clear();
|
|
m_LinkText.clear();
|
|
m_Error = false;
|
|
}
|
|
|
|
void EQ::SayLinkEngine::generate_body()
|
|
{
|
|
/*
|
|
Current server mask: EQClientRoF2
|
|
|
|
RoF2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X" (56)
|
|
RoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (55)
|
|
SoF: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%05X" "%08X" (50)
|
|
6.2: "%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%1X" "%08X" (45)
|
|
*/
|
|
|
|
memset(&m_LinkBodyStruct, 0, sizeof(SayLinkBody_Struct));
|
|
|
|
const EQ::ItemData *item_data = nullptr;
|
|
|
|
switch (m_LinkType) {
|
|
case saylink::SayLinkBlank:
|
|
break;
|
|
case saylink::SayLinkItemData:
|
|
if (m_ItemData == nullptr) { break; }
|
|
m_LinkBodyStruct.item_id = m_ItemData->ID;
|
|
m_LinkBodyStruct.evolve_group = m_ItemData->LoreGroup; // this probably won't work for all items
|
|
//m_LinkBodyStruct.evolve_level = m_ItemData->EvolvingLevel;
|
|
// TODO: add hash call
|
|
break;
|
|
case saylink::SayLinkLootItem:
|
|
if (m_LootData == nullptr) { break; }
|
|
item_data = database.GetItem(m_LootData->item_id);
|
|
if (item_data == nullptr) { break; }
|
|
m_LinkBodyStruct.item_id = item_data->ID;
|
|
m_LinkBodyStruct.augment_1 = m_LootData->aug_1;
|
|
m_LinkBodyStruct.augment_2 = m_LootData->aug_2;
|
|
m_LinkBodyStruct.augment_3 = m_LootData->aug_3;
|
|
m_LinkBodyStruct.augment_4 = m_LootData->aug_4;
|
|
m_LinkBodyStruct.augment_5 = m_LootData->aug_5;
|
|
m_LinkBodyStruct.augment_6 = m_LootData->aug_6;
|
|
m_LinkBodyStruct.evolve_group = item_data->LoreGroup; // see note above
|
|
//m_LinkBodyStruct.evolve_level = item_data->EvolvingLevel;
|
|
// TODO: add hash call
|
|
break;
|
|
case saylink::SayLinkItemInst:
|
|
if (m_ItemInst == nullptr) { break; }
|
|
if (m_ItemInst->GetItem() == nullptr) { break; }
|
|
m_LinkBodyStruct.item_id = m_ItemInst->GetItem()->ID;
|
|
m_LinkBodyStruct.augment_1 = m_ItemInst->GetAugmentItemID(0);
|
|
m_LinkBodyStruct.augment_2 = m_ItemInst->GetAugmentItemID(1);
|
|
m_LinkBodyStruct.augment_3 = m_ItemInst->GetAugmentItemID(2);
|
|
m_LinkBodyStruct.augment_4 = m_ItemInst->GetAugmentItemID(3);
|
|
m_LinkBodyStruct.augment_5 = m_ItemInst->GetAugmentItemID(4);
|
|
m_LinkBodyStruct.augment_6 = m_ItemInst->GetAugmentItemID(5);
|
|
m_LinkBodyStruct.is_evolving = (m_ItemInst->IsEvolving() ? 1 : 0);
|
|
m_LinkBodyStruct.evolve_group = m_ItemInst->GetItem()->LoreGroup; // see note above
|
|
m_LinkBodyStruct.evolve_level = m_ItemInst->GetEvolveLvl();
|
|
m_LinkBodyStruct.ornament_icon = m_ItemInst->GetOrnamentationIcon();
|
|
// TODO: add hash call
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (m_LinkProxyStruct.action_id) {
|
|
m_LinkBodyStruct.action_id = m_LinkProxyStruct.action_id;
|
|
}
|
|
if (m_LinkProxyStruct.item_id) {
|
|
m_LinkBodyStruct.item_id = m_LinkProxyStruct.item_id;
|
|
}
|
|
if (m_LinkProxyStruct.augment_1) {
|
|
m_LinkBodyStruct.augment_1 = m_LinkProxyStruct.augment_1;
|
|
}
|
|
if (m_LinkProxyStruct.augment_2) {
|
|
m_LinkBodyStruct.augment_2 = m_LinkProxyStruct.augment_2;
|
|
}
|
|
if (m_LinkProxyStruct.augment_3) {
|
|
m_LinkBodyStruct.augment_3 = m_LinkProxyStruct.augment_3;
|
|
}
|
|
if (m_LinkProxyStruct.augment_4) {
|
|
m_LinkBodyStruct.augment_4 = m_LinkProxyStruct.augment_4;
|
|
}
|
|
if (m_LinkProxyStruct.augment_5) {
|
|
m_LinkBodyStruct.augment_5 = m_LinkProxyStruct.augment_5;
|
|
}
|
|
if (m_LinkProxyStruct.augment_6) {
|
|
m_LinkBodyStruct.augment_6 = m_LinkProxyStruct.augment_6;
|
|
}
|
|
if (m_LinkProxyStruct.is_evolving) {
|
|
m_LinkBodyStruct.is_evolving = m_LinkProxyStruct.is_evolving;
|
|
}
|
|
if (m_LinkProxyStruct.evolve_group) {
|
|
m_LinkBodyStruct.evolve_group = m_LinkProxyStruct.evolve_group;
|
|
}
|
|
if (m_LinkProxyStruct.evolve_level) {
|
|
m_LinkBodyStruct.evolve_level = m_LinkProxyStruct.evolve_level;
|
|
}
|
|
if (m_LinkProxyStruct.ornament_icon) {
|
|
m_LinkBodyStruct.ornament_icon = m_LinkProxyStruct.ornament_icon;
|
|
}
|
|
if (m_LinkProxyStruct.hash) {
|
|
m_LinkBodyStruct.hash = m_LinkProxyStruct.hash;
|
|
}
|
|
|
|
|
|
if (m_TaskUse) {
|
|
m_LinkBodyStruct.hash = 0x14505DC2;
|
|
}
|
|
|
|
m_LinkBody = StringFormat(
|
|
"%1X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%05X" "%1X" "%04X" "%02X" "%05X" "%08X",
|
|
(0x0F & m_LinkBodyStruct.action_id),
|
|
(0x000FFFFF & m_LinkBodyStruct.item_id),
|
|
(0x000FFFFF & m_LinkBodyStruct.augment_1),
|
|
(0x000FFFFF & m_LinkBodyStruct.augment_2),
|
|
(0x000FFFFF & m_LinkBodyStruct.augment_3),
|
|
(0x000FFFFF & m_LinkBodyStruct.augment_4),
|
|
(0x000FFFFF & m_LinkBodyStruct.augment_5),
|
|
(0x000FFFFF & m_LinkBodyStruct.augment_6),
|
|
(0x0F & m_LinkBodyStruct.is_evolving),
|
|
(0x0000FFFF & m_LinkBodyStruct.evolve_group),
|
|
(0xFF & m_LinkBodyStruct.evolve_level),
|
|
(0x000FFFFF & m_LinkBodyStruct.ornament_icon),
|
|
(0xFFFFFFFF & m_LinkBodyStruct.hash)
|
|
);
|
|
}
|
|
|
|
void EQ::SayLinkEngine::generate_text()
|
|
{
|
|
if (m_LinkProxyStruct.text != nullptr) {
|
|
m_LinkText = m_LinkProxyStruct.text;
|
|
return;
|
|
}
|
|
|
|
const EQ::ItemData *item_data = nullptr;
|
|
|
|
switch (m_LinkType) {
|
|
case saylink::SayLinkBlank:
|
|
break;
|
|
case saylink::SayLinkItemData:
|
|
if (m_ItemData == nullptr) { break; }
|
|
m_LinkText = m_ItemData->Name;
|
|
return;
|
|
case saylink::SayLinkLootItem:
|
|
if (m_LootData == nullptr) { break; }
|
|
item_data = database.GetItem(m_LootData->item_id);
|
|
if (item_data == nullptr) { break; }
|
|
m_LinkText = item_data->Name;
|
|
return;
|
|
case saylink::SayLinkItemInst:
|
|
if (m_ItemInst == nullptr) { break; }
|
|
if (m_ItemInst->GetItem() == nullptr) { break; }
|
|
m_LinkText = m_ItemInst->GetItem()->Name;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
m_LinkText = "null";
|
|
}
|
|
|
|
std::string EQ::SayLinkEngine::GenerateQuestSaylink(const std::string& saylink_text, bool silent, const std::string& link_name)
|
|
{
|
|
uint32 saylink_id = 0;
|
|
|
|
SaylinkRepository::Saylink saylink = GetOrSaveSaylink(saylink_text);
|
|
if (saylink.id > 0) {
|
|
saylink_id = saylink.id;
|
|
}
|
|
|
|
/**
|
|
* Generate the actual link
|
|
*/
|
|
EQ::SayLinkEngine linker;
|
|
linker.SetProxyItemID(SAYLINK_ITEM_ID);
|
|
if (silent) {
|
|
linker.SetProxyAugment2ID(saylink_id);
|
|
}
|
|
else {
|
|
linker.SetProxyAugment1ID(saylink_id);
|
|
}
|
|
|
|
linker.SetProxyText(link_name.c_str());
|
|
|
|
return linker.GenerateLink();
|
|
}
|
|
|
|
std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message)
|
|
{
|
|
LogSaylinkDetail("message [{}]", message);
|
|
|
|
std::string new_message;
|
|
new_message.reserve(strlen(message));
|
|
|
|
bool in_bracket_state = false;
|
|
bool in_link_state = false;
|
|
|
|
const char* ch = message;
|
|
const char* startpos = message;
|
|
for (; *ch != '\0'; ++ch)
|
|
{
|
|
// saylinks not added if spaces touch brackets
|
|
bool abort_space = in_bracket_state && *ch == ' ' && (*(ch-1) == '[' || *(ch+1) == ']');
|
|
|
|
if (in_bracket_state && (*ch == '[' || *ch == '\x12' || abort_space))
|
|
{
|
|
// abort due to nested bracket (which starts another) or existing saylink
|
|
new_message.append(startpos, ch - startpos);
|
|
in_bracket_state = false;
|
|
}
|
|
else if (in_bracket_state && *ch == ']')
|
|
{
|
|
if (ch != startpos)
|
|
{
|
|
std::string str(startpos, ch - startpos);
|
|
new_message += Saylink::Create(str);
|
|
}
|
|
in_bracket_state = false;
|
|
}
|
|
|
|
if (!in_bracket_state)
|
|
{
|
|
new_message.push_back(*ch);
|
|
}
|
|
|
|
if (*ch == '[' && !in_link_state)
|
|
{
|
|
startpos = ch + 1;
|
|
in_bracket_state = true;
|
|
}
|
|
else if (*ch == '\x12')
|
|
{
|
|
in_link_state = !in_link_state;
|
|
}
|
|
}
|
|
|
|
LogSaylinkDetail("new_message [{}]", new_message);
|
|
|
|
return new_message;
|
|
}
|
|
|
|
void EQ::SayLinkEngine::LoadCachedSaylinks()
|
|
{
|
|
auto saylinks = SaylinkRepository::GetWhere(database, "phrase not REGEXP '[A-Z]' and phrase not REGEXP '[0-9]'");
|
|
LogSaylink("Loaded [{}] saylinks into cache", saylinks.size());
|
|
g_cached_saylinks = saylinks;
|
|
}
|
|
|
|
SaylinkRepository::Saylink EQ::SayLinkEngine::GetOrSaveSaylink(std::string saylink_text)
|
|
{
|
|
// return cached saylink if exist
|
|
if (!g_cached_saylinks.empty()) {
|
|
for (auto &s: g_cached_saylinks) {
|
|
if (s.phrase == saylink_text) {
|
|
return s;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto saylinks = SaylinkRepository::GetWhere(
|
|
database,
|
|
fmt::format("phrase = '{}'", Strings::Escape(saylink_text))
|
|
);
|
|
|
|
// return if found from the database
|
|
if (!saylinks.empty()) {
|
|
g_cached_saylinks.emplace_back(saylinks[0]);
|
|
return saylinks[0];
|
|
}
|
|
|
|
// if not found in database - save
|
|
auto new_saylink = SaylinkRepository::NewEntity();
|
|
new_saylink.phrase = saylink_text;
|
|
|
|
// persist to database
|
|
auto link = SaylinkRepository::InsertOne(database, new_saylink);
|
|
if (link.id > 0) {
|
|
g_cached_saylinks.emplace_back(link);
|
|
return link;
|
|
}
|
|
|
|
return {};
|
|
}
|
|
|
|
std::string Saylink::Create(const std::string& saylink_text, bool silent, const std::string& link_name)
|
|
{
|
|
return EQ::SayLinkEngine::GenerateQuestSaylink(saylink_text, silent, (link_name.empty() ? saylink_text : link_name));
|
|
}
|
|
|
|
std::string Saylink::Silent(const std::string& saylink_text, const std::string& link_name)
|
|
{
|
|
return EQ::SayLinkEngine::GenerateQuestSaylink(saylink_text, true, (link_name.empty() ? saylink_text : link_name));
|
|
}
|