[NPC] Support for multiple emotes per type, emote variables (#3801)

* [forage rule feature] add a rule to disabled using common_food_ids from the list in forage.cpp.  currently set to enabled.

* NPC database emotes now supports basic variables. More than one variable can be used at a time.

* Format manifest

* Formatting

* Formatting

* Formatting

---------

Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
regneq 2023-12-24 21:28:57 -08:00 committed by GitHub
parent 6396a6fbef
commit 25872203ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 88 additions and 58 deletions

View File

@ -5133,6 +5133,16 @@ CHANGE COLUMN `slot` `inventory_slot` mediumint(9) NOT NULL DEFAULT -1 AFTER `st
ALTER TABLE `starting_items`
CHANGE COLUMN `temporary` `class_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `id`;
)"
},
ManifestEntry{
.version = 9248,
.description = "2023_12_22_drop_npc_emotes_index.sql",
.check = "show index from npc_emotes where key_name = 'emoteid'",
.condition = "not_empty",
.match = "",
.sql = R"(
ALTER TABLE `npc_emotes` DROP INDEX `emoteid`;
)"
}

View File

@ -16,6 +16,7 @@
#include "../../strings.h"
#include <ctime>
class BaseNpcEmotesRepository {
public:
struct NpcEmotes {
@ -120,8 +121,9 @@ public:
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE id = {} LIMIT 1",
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
npc_emotes_id
)
);

View File

@ -42,7 +42,7 @@
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9247
#define CURRENT_BINARY_DATABASE_VERSION 9248
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9041

View File

@ -1820,7 +1820,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
auto emote_id = killerMob->GetEmoteID();
if (emote_id) {
killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid);
killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this);
}
killerMob->TrySpellOnKill(killed_level, spell);
@ -2710,7 +2710,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
if (IsNPC()) {
auto emote_id = GetEmoteID();
if (emote_id) {
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid);
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid, killer_mob);
}
}
@ -2721,7 +2721,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
auto emote_id = oos->GetEmoteID();
if (emote_id) {
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id);
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this);
}
if (killer_mob) {
killer_mob->TrySpellOnKill(killed_level, spell);
@ -2799,7 +2799,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
ApplyIllusionToCorpse(illusion_spell_id, corpse);
if (killer != 0 && emoteid != 0)
corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid);
corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid, killer);
if (killer != 0 && killer->IsClient()) {
corpse->AllowPlayerLoot(killer, 0);
if (killer->IsGrouped()) {

View File

@ -6172,7 +6172,7 @@ void Client::CheckEmoteHail(NPC* n, const char* message)
const auto emote_id = n->GetEmoteID();
if (emote_id) {
n->DoNPCEmote(EQ::constants::EmoteEventTypes::Hailed, emote_id);
n->DoNPCEmote(EQ::constants::EmoteEventTypes::Hailed, emote_id, this);
}
}

View File

@ -7,10 +7,7 @@ void FindEmote(Client *c, const Seperator *sep)
if (sep->IsNumber(2)) {
auto emote_id = Strings::ToUnsignedInt(sep->arg[2]);
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
iterator.Reset();
while (iterator.MoreElements()) {
auto &e = iterator.GetData();
for (auto& e : zone->npc_emote_list) {
if (emote_id == e->emoteid) {
c->Message(
Chat::White,
@ -40,7 +37,6 @@ void FindEmote(Client *c, const Seperator *sep)
break;
}
iterator.Advance();
}
if (found_count == 50) {
@ -70,10 +66,7 @@ void FindEmote(Client *c, const Seperator *sep)
const std::string& search_criteria = sep->argplus[2];
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
iterator.Reset();
while (iterator.MoreElements()) {
auto &e = iterator.GetData();
for (auto& e : zone->npc_emote_list) {
const std::string& current_text = Strings::ToLower(e->text);
@ -106,7 +99,6 @@ void FindEmote(Client *c, const Seperator *sep)
break;
}
iterator.Advance();
}
if (found_count == 50) {

View File

@ -12,10 +12,7 @@ void ShowEmotes(Client *c, const Seperator *sep)
uint32 emote_count = 0;
const uint32 emote_id = t->GetEmoteID();
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
iterator.Reset();
while (iterator.MoreElements()) {
const auto& e = iterator.GetData();
for (auto& e : zone->npc_emote_list) {
if (emote_id == e->emoteid) {
c->Message(
Chat::White,
@ -41,7 +38,6 @@ void ShowEmotes(Client *c, const Seperator *sep)
emote_count++;
}
iterator.Advance();
}
c->Message(

View File

@ -1800,7 +1800,7 @@ void Mob::AI_Event_Engaged(Mob *attacker, bool yell_for_help)
auto emote_id = GetEmoteID();
if (emote_id) {
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid);
CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::EnterCombat, emoteid, attacker);
}
std::string mob_name = GetCleanName();

View File

@ -3046,42 +3046,72 @@ void NPC::SendPayload(int payload_id, std::string payload_value)
}
NPC_Emote_Struct* NPC::GetNPCEmote(uint32 emoteid, uint8 event_) {
LinkedListIterator<NPC_Emote_Struct*> iterator(zone->NPCEmoteList);
iterator.Reset();
while(iterator.MoreElements())
{
NPC_Emote_Struct* nes = iterator.GetData();
std::vector<NPC_Emote_Struct *> emotes;
for (auto &e: zone->npc_emote_list) {
NPC_Emote_Struct *nes = e;
if (emoteid == nes->emoteid && event_ == nes->event_) {
return nes;
emotes.push_back(e);
}
iterator.Advance();
}
return nullptr;
if (emotes.empty()) {
return nullptr;
}
else if (emotes.size() == 1) {
return emotes[0];
}
int index = zone->random.Roll0(emotes.size());
return emotes[index];
}
void NPC::DoNPCEmote(uint8 event_, uint32 emoteid)
void NPC::DoNPCEmote(uint8 event_, uint32 emoteid, Mob* target)
{
if (emoteid == 0)
{
if (emoteid == 0) {
return;
}
NPC_Emote_Struct* nes = GetNPCEmote(emoteid,event_);
if(nes == nullptr)
{
NPC_Emote_Struct *nes = GetNPCEmote(emoteid, event_);
if (nes == nullptr) {
return;
}
if(emoteid == nes->emoteid)
{
if(nes->type == 1)
Emote("%s",nes->text);
else if(nes->type == 2)
Shout("%s",nes->text);
else if(nes->type == 3)
entity_list.MessageCloseString(this, true, 200, 10, GENERIC_STRING, nes->text);
else
Say("%s",nes->text);
std::string processed = nes->text;
Strings::FindReplace(processed, "$mname", GetCleanName());
Strings::FindReplace(processed, "$mracep", GetRacePlural() = GetClass());
Strings::FindReplace(processed, "$mrace", GetPlayerRaceName(GetRace()));
Strings::FindReplace(processed, "$mclass", GetClassIDName(GetClass()));
if (target) {
Strings::FindReplace(processed, "$name", target->GetCleanName());
Strings::FindReplace(processed, "$racep", GetRacePlural() = target->GetClass());
Strings::FindReplace(processed, "$race", GetPlayerRaceName(target->GetRace()));
Strings::FindReplace(processed, "$class", GetClassIDName(target->GetClass()));
}
else {
Strings::FindReplace(processed, "$name", "foe");
Strings::FindReplace(processed, "$race", "race");
Strings::FindReplace(processed, "$racep", "races");
Strings::FindReplace(processed, "$class", "class");
}
if (emoteid == nes->emoteid) {
if (event_ == EQ::constants::EmoteEventTypes::Hailed && target) {
DoQuestPause(target);
}
if (nes->type == 1) {
Emote("%s", processed.c_str());
}
else if (nes->type == 2) {
Shout("%s", processed.c_str());
}
else if (nes->type == 3) {
entity_list.MessageCloseString(this, true, 200, 10, GENERIC_STRING, processed.c_str());
}
else {
Say("%s", processed.c_str());
}
}
}

View File

@ -476,7 +476,7 @@ public:
const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; }
NPC_Emote_Struct* GetNPCEmote(uint32 emoteid, uint8 event_);
void DoNPCEmote(uint8 event_, uint32 emoteid);
void DoNPCEmote(uint8 event_, uint32 emoteid, Mob* target = nullptr);
bool CanTalk();
void DoQuestPause(Mob *other);

View File

@ -2015,7 +2015,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
{
if (zone && zone->IsLoaded()) {
zone->SendReloadMessage("NPC Emotes");
zone->LoadNPCEmotes(&zone->NPCEmoteList);
zone->LoadNPCEmotes(&zone->npc_emote_list);
}
break;
}

View File

@ -1056,7 +1056,7 @@ Zone::~Zone() {
safe_delete_array(short_name);
safe_delete_array(long_name);
safe_delete(Weather_Timer);
NPCEmoteList.Clear();
npc_emote_list.clear();
zone_point_list.Clear();
entity_list.Clear();
ClearBlockedSpells();
@ -1152,7 +1152,7 @@ bool Zone::Init(bool is_static) {
LoadLDoNTrapEntries();
LoadVeteranRewards();
LoadAlternateCurrencies();
LoadNPCEmotes(&NPCEmoteList);
LoadNPCEmotes(&npc_emote_list);
LoadAlternateAdvancement();
@ -1233,8 +1233,8 @@ void Zone::ReloadStaticData() {
LoadVeteranRewards();
LoadAlternateCurrencies();
NPCEmoteList.Clear();
LoadNPCEmotes(&NPCEmoteList);
npc_emote_list.clear();
LoadNPCEmotes(&npc_emote_list);
//load the zone config file.
if (!LoadZoneCFG(GetShortName(), GetInstanceVersion())) { // try loading the zone name...
@ -2548,10 +2548,10 @@ void Zone::DoAdventureActions()
}
void Zone::LoadNPCEmotes(LinkedList<NPC_Emote_Struct*>* NPCEmoteList)
void Zone::LoadNPCEmotes(std::vector<NPC_Emote_Struct*>* NPCEmoteList)
{
NPCEmoteList->Clear();
NPCEmoteList->clear();
const std::string query = "SELECT emoteid, event_, type, text FROM npc_emotes";
auto results = content_db.QueryDatabase(query);
if (!results.Success()) {
@ -2565,7 +2565,7 @@ void Zone::LoadNPCEmotes(LinkedList<NPC_Emote_Struct*>* NPCEmoteList)
nes->event_ = Strings::ToInt(row[1]);
nes->type = Strings::ToInt(row[2]);
strn0cpy(nes->text, row[3], sizeof(nes->text));
NPCEmoteList->Insert(nes);
NPCEmoteList->push_back(nes);
}
LogInfo("Loaded [{}] npc emotes", Strings::Commify(results.RowCount()));

View File

@ -185,7 +185,7 @@ public:
DynamicZone *GetDynamicZone();
IPathfinder *pathing;
LinkedList<NPC_Emote_Struct *> NPCEmoteList;
std::vector<NPC_Emote_Struct *> npc_emote_list;
LinkedList<Spawn2 *> spawn2_list;
LinkedList<ZonePoint *> zone_point_list;
std::vector<ZonePointsRepository::ZonePoints> virtual_zone_point_list;
@ -282,7 +282,7 @@ public:
void LoadMercSpells();
void LoadMercTemplates();
void LoadNewMerchantData(uint32 merchantid);
void LoadNPCEmotes(LinkedList<NPC_Emote_Struct *> *NPCEmoteList);
void LoadNPCEmotes(std::vector<NPC_Emote_Struct *> *NPCEmoteList);
void LoadTempMerchantData();
void LoadTickItems();
void LoadVeteranRewards();