From 6398381c447ac18990f63d83a2c490e4ee84988a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 19 May 2022 20:01:14 -0400 Subject: [PATCH 001/552] [Quest API] Add CheckNameFilter to Perl/Lua. (#2175) - Add quest::checknamefilter(name) to Perl. - Add eq.check_name_filter(name) to Lua. - Allows operators to check strings against the name filter for stuff like setting custom pet names, titles, suffixes, etc in scripts. --- common/database.cpp | 84 +++++++++++++++--------------------------- common/database.h | 6 +-- zone/bot_database.cpp | 14 +++++-- zone/client_packet.cpp | 2 +- zone/embparser_api.cpp | 16 ++++++++ zone/lua_general.cpp | 5 +++ 6 files changed, 65 insertions(+), 62 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index a065b5dbe..4e7cb53d6 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1167,91 +1167,67 @@ uint8 Database::GetPEQZone(uint32 zone_id, uint32 version){ return static_cast(std::stoi(row[0])); } -bool Database::CheckNameFilter(const char* name, bool surname) +bool Database::CheckNameFilter(std::string name, bool surname) { - std::string str_name = name; + name = str_tolower(name); // the minimum 4 is enforced by the client too - if (!name || strlen(name) < 4) - { + if (name.empty() || name.size() < 4) { return false; } // Given name length is enforced by the client too - if (!surname && strlen(name) > 15) - { + if (!surname && name.size() > 15) { return false; } - for (size_t i = 0; i < str_name.size(); i++) - { - if(!isalpha(str_name[i])) - { + for (size_t i = 0; i < name.size(); i++) { + if (!isalpha(name[i])) { return false; } } - for(size_t x = 0; x < str_name.size(); ++x) - { - str_name[x] = tolower(str_name[x]); - } - char c = '\0'; uint8 num_c = 0; - for(size_t x = 0; x < str_name.size(); ++x) - { - if(str_name[x] == c) - { + for (size_t x = 0; x < name.size(); ++x) { + if (name[x] == c) { num_c++; - } - else - { + } else { num_c = 1; - c = str_name[x]; + c = name[x]; } - if(num_c > 2) - { + + if (num_c > 2) { return false; } } - - std::string query("SELECT name FROM name_filter"); + std::string query = "SELECT name FROM name_filter"; auto results = QueryDatabase(query); - - if (!results.Success()) - { - // false through to true? shouldn't it be falls through to false? + if (!results.Success()) { return true; } - for (auto row = results.begin();row != results.end();++row) - { - std::string current_row = row[0]; - - for(size_t x = 0; x < current_row.size(); ++x) - current_row[x] = tolower(current_row[x]); - - if(str_name.find(current_row) != std::string::npos) + for (auto row : results) { + std::string current_row = str_tolower(row[0]); + if (name.find(current_row) != std::string::npos) { return false; + } } return true; } -bool Database::AddToNameFilter(const char* name) { - - std::string query = StringFormat("INSERT INTO name_filter (name) values ('%s')", name); +bool Database::AddToNameFilter(std::string name) { + auto query = fmt::format( + "INSERT INTO name_filter (name) values ('{}')", + name + ); auto results = QueryDatabase(query); - - if (!results.Success()) - { + if (!results.Success() || !results.RowsAffected()) { return false; } - if (results.RowsAffected() == 0) - return false; - return true; } @@ -1339,16 +1315,16 @@ bool Database::UpdateName(const char* oldname, const char* newname) { } // If the name is used or an error occurs, it returns false, otherwise it returns true -bool Database::CheckUsedName(const char* name) { - std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name` = '%s'", name); +bool Database::CheckUsedName(std::string name) { + auto query = fmt::format( + "SELECT `id` FROM `character_data` WHERE `name` = '{}'", + name + ); auto results = QueryDatabase(query); - if (!results.Success()) { + if (!results.Success() || results.RowCount()) { return false; } - if (results.RowCount() > 0) - return false; - return true; } diff --git a/common/database.h b/common/database.h index b7492ac9f..b20a0c3a5 100644 --- a/common/database.h +++ b/common/database.h @@ -88,7 +88,6 @@ public: /* Character Creation */ - bool AddToNameFilter(const char *name); bool CreateCharacter( uint32 account_id, char *name, @@ -122,10 +121,11 @@ public: /* General Information Queries */ bool AddBannedIP(std::string banned_ip, std::string notes); //Add IP address to the banned_ips table. + bool AddToNameFilter(std::string name); bool CheckBannedIPs(std::string login_ip); //Check incoming connection against banned IP table. bool CheckGMIPs(std::string login_ip, uint32 account_id); - bool CheckNameFilter(const char* name, bool surname = false); - bool CheckUsedName(const char* name); + bool CheckNameFilter(std::string name, bool surname = false); + bool CheckUsedName(std::string name); uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0); uint32 GetAccountIDByChar(uint32 char_id); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 520f1e5f0..aca79ec38 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -167,15 +167,21 @@ bool BotDatabase::LoadBotSpellCastingChances() /* Bot functions */ bool BotDatabase::QueryNameAvailablity(const std::string& bot_name, bool& available_flag) { - if (bot_name.empty() || bot_name.size() > 60 || !database.CheckUsedName(bot_name.c_str())) + if (bot_name.empty() || bot_name.size() > 60 || !database.CheckUsedName(bot_name)) return false; - query = StringFormat("SELECT `id` FROM `vw_bot_character_mobs` WHERE `name` LIKE '%s' LIMIT 1", bot_name.c_str()); + query = fmt::format( + "SELECT `id` FROM `vw_bot_character_mobs` WHERE `name` LIKE '{}' LIMIT 1", + bot_name + ); auto results = database.QueryDatabase(query); - if (!results.Success()) + if (!results.Success()) { return false; - if (results.RowCount()) + } + + if (results.RowCount()) { return true; + } available_flag = true; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ee66f02b2..655613943 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6375,7 +6375,7 @@ void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) } Client* client = entity_list.GetClientByName(gmn->oldname); LogInfo("GM([{}]) changeing players name. Old:[{}] New:[{}]", GetName(), gmn->oldname, gmn->newname); - bool usedname = database.CheckUsedName((const char*)gmn->newname); + bool usedname = database.CheckUsedName(gmn->newname); if (client == 0) { Message(Chat::Red, "%s not found for name change. Operation failed!", gmn->oldname); return; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 3f3af3261..f2c24e0f6 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8347,6 +8347,21 @@ XS(XS__commify) { XSRETURN(1); } +XS(XS__checknamefilter); +XS(XS__checknamefilter) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: quest::checknamefilter(std::string name)"); + } + + dXSTARG; + std::string name = (std::string) SvPV_nolen(ST(0)); + bool passes = database.CheckNameFilter(name); + ST(0) = boolSV(passes); + sv_2mortal(ST(0)); + XSRETURN(1); +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -8434,6 +8449,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "buryplayercorpse"), XS__buryplayercorpse, file); newXS(strcpy(buf, "castspell"), XS__castspell, file); newXS(strcpy(buf, "changedeity"), XS__changedeity, file); + newXS(strcpy(buf, "checknamefilter"), XS__checknamefilter, file); newXS(strcpy(buf, "checktitle"), XS__checktitle, file); newXS(strcpy(buf, "clear_npctype_cache"), XS__clear_npctype_cache, file); newXS(strcpy(buf, "clear_proximity"), XS__clear_proximity, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index f62c1de9f..9896db254 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -3392,6 +3392,10 @@ std::string lua_commify(std::string number) { return commify(number); } +bool lua_check_name_filter(std::string name) { + return database.CheckNameFilter(name); +} + #define LuaCreateNPCParse(name, c_type, default_value) do { \ cur = table[#name]; \ if(luabind::type(cur) != LUA_TNIL) { \ @@ -3845,6 +3849,7 @@ luabind::scope lua_register_general() { luabind::def("get_consider_level_name", &lua_get_consider_level_name), luabind::def("get_environmental_damage_name", &lua_get_environmental_damage_name), luabind::def("commify", &lua_commify), + luabind::def("check_name_filter", &lua_check_name_filter), /* Cross Zone From 0e96099b3dd334cf7aef3abf1814840ff2854786 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 19 May 2022 20:15:44 -0400 Subject: [PATCH 002/552] [Titles] Cleanup titles, title suffix, and last name methods. (#2174) * [Titles] Cleanup titles, title suffix, and last name methods. - Use strings instead of const chars*. - Add optional parameter to SetAATitle in Lua so you can save to the database similar to Perl. - Cleanup #lastname command. - Cleanup #title command. - Cleanup #titlesuffix command. * Update npc.cpp --- zone/client.cpp | 24 +++++++------ zone/client.h | 6 ++-- zone/client_packet.cpp | 6 ++-- zone/embparser_api.cpp | 6 ++-- zone/gm_commands/lastname.cpp | 2 +- zone/gm_commands/title.cpp | 4 +-- zone/gm_commands/titlesuffix.cpp | 4 +-- zone/lua_client.cpp | 21 +++++++++--- zone/lua_client.h | 5 +-- zone/lua_npc.cpp | 6 ++-- zone/lua_npc.h | 2 +- zone/npc.cpp | 28 ++++++++------- zone/npc.h | 2 +- zone/perl_client.cpp | 58 ++++++++++++++++++-------------- zone/perl_npc.cpp | 10 +++--- zone/questmgr.cpp | 22 ++++++------ zone/questmgr.h | 2 +- zone/titles.cpp | 26 +++++++------- zone/titles.h | 4 +-- 19 files changed, 129 insertions(+), 109 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 6324db2a0..16c28e20b 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -2020,20 +2020,22 @@ void Client::Sit() { SetAppearance(eaSitting, false); } -void Client::ChangeLastName(const char* in_lastname) { +void Client::ChangeLastName(std::string last_name) { memset(m_pp.last_name, 0, sizeof(m_pp.last_name)); - strn0cpy(m_pp.last_name, in_lastname, sizeof(m_pp.last_name)); + strn0cpy(m_pp.last_name, last_name.c_str(), sizeof(m_pp.last_name)); auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); - GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer; - strcpy(gmn->name, name); - strcpy(gmn->gmname, name); - strcpy(gmn->lastname, in_lastname); - gmn->unknown[0]=1; - gmn->unknown[1]=1; - gmn->unknown[2]=1; - gmn->unknown[3]=1; + auto gmn = (GMLastName_Struct*) outapp->pBuffer; + strn0cpy(gmn->name, name, sizeof(gmn->name)); + strn0cpy(gmn->gmname, name, sizeof(gmn->gmname)); + strn0cpy(gmn->lastname, last_name.c_str(), sizeof(gmn->lastname)); + + gmn->unknown[0] = 1; + gmn->unknown[1] = 1; + gmn->unknown[2] = 1; + gmn->unknown[3] = 1; + entity_list.QueueClients(this, outapp, false); - // Send name update packet here... once know what it is + safe_delete(outapp); } diff --git a/zone/client.h b/zone/client.h index f039966eb..f755ef432 100644 --- a/zone/client.h +++ b/zone/client.h @@ -668,7 +668,7 @@ public: void RemoveFromInstance(uint16 instance_id); void WhoAll(); bool CheckLoreConflict(const EQ::ItemData* item); - void ChangeLastName(const char* in_lastname); + void ChangeLastName(std::string last_name); void GetGroupAAs(GroupLeadershipAA_Struct *into) const; void GetRaidAAs(RaidLeadershipAA_Struct *into) const; void ClearGroupAAs(); @@ -912,8 +912,8 @@ public: inline uint32 GetAAXP() const { return m_pp.expAA; } inline uint32 GetAAPercent() const { return m_epp.perAA; } int64 CalcAAFocus(focusType type, const AA::Rank &rank, uint16 spell_id); - void SetAATitle(const char *Title); - void SetTitleSuffix(const char *txt); + void SetAATitle(std::string title); + void SetTitleSuffix(std::string suffix); void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing, uint32 reduction = 0); // Item methods diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 655613943..c97c60031 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -12875,7 +12875,7 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) return; } - SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; + auto sts = (SetTitle_Struct *) app->pBuffer; if (sts->title_id && !title_manager.HasTitle(this, sts->title_id)) { return; @@ -12892,9 +12892,9 @@ void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) ); if (!sts->is_suffix) { - SetAATitle(title.c_str()); + SetAATitle(title); } else { - SetTitleSuffix(title.c_str()); + SetTitleSuffix(title); } } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index f2c24e0f6..e1164b760 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1041,11 +1041,11 @@ XS(XS__surname); XS(XS__surname) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: quest::surname(string name)"); + Perl_croak(aTHX_ "Usage: quest::surname(string last_name)"); - char *name = (char *) SvPV_nolen(ST(0)); + std::string last_name = (std::string) SvPV_nolen(ST(0)); - quest_manager.surname(name); + quest_manager.surname(last_name); XSRETURN_EMPTY; } diff --git a/zone/gm_commands/lastname.cpp b/zone/gm_commands/lastname.cpp index 8dd3c7dd0..81b3c2b85 100755 --- a/zone/gm_commands/lastname.cpp +++ b/zone/gm_commands/lastname.cpp @@ -15,7 +15,7 @@ void command_lastname(Client *c, const Seperator *sep) return; } - target->ChangeLastName(last_name.c_str()); + target->ChangeLastName(last_name); c->Message( Chat::White, fmt::format( diff --git a/zone/gm_commands/title.cpp b/zone/gm_commands/title.cpp index bd0503008..dff5a3ec4 100755 --- a/zone/gm_commands/title.cpp +++ b/zone/gm_commands/title.cpp @@ -31,9 +31,9 @@ void command_title(Client *c, const Seperator *sep) } if (!save_title || is_remove) { - target->SetAATitle(title.c_str()); + target->SetAATitle(title); } else if (save_title) { - title_manager.CreateNewPlayerTitle(target, title.c_str()); + title_manager.CreateNewPlayerTitle(target, title); } target->Save(); diff --git a/zone/gm_commands/titlesuffix.cpp b/zone/gm_commands/titlesuffix.cpp index b2e6cb4bf..34c971f64 100755 --- a/zone/gm_commands/titlesuffix.cpp +++ b/zone/gm_commands/titlesuffix.cpp @@ -31,9 +31,9 @@ void command_titlesuffix(Client *c, const Seperator *sep) } if (!save_suffix || is_remove) { - target->SetTitleSuffix(suffix.c_str()); + target->SetTitleSuffix(suffix); } else if (save_suffix) { - title_manager.CreateNewPlayerSuffix(target, suffix.c_str()); + title_manager.CreateNewPlayerSuffix(target, suffix); } target->Save(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 68b55ba8c..4fba59888 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -16,6 +16,7 @@ #include "lua_raid.h" #include "lua_packet.h" #include "dialogue_window.h" +#include "titles.h" #include "../common/expedition_lockout_timer.h" struct InventoryWhere { }; @@ -415,9 +416,9 @@ void Lua_Client::MoveZoneInstanceRaid(uint16 instance_id) { self->MoveZoneInstanceRaid(instance_id); } -void Lua_Client::ChangeLastName(const char *in) { +void Lua_Client::ChangeLastName(std::string last_name) { Lua_Safe_Call_Void(); - self->ChangeLastName(in); + self->ChangeLastName(last_name); } int Lua_Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 race, uint32 class_, uint32 deity, uint32 faction, Lua_NPC npc) { @@ -1120,11 +1121,20 @@ void Lua_Client::SendZoneFlagInfo(Lua_Client to) { self->SendZoneFlagInfo(to); } -void Lua_Client::SetAATitle(const char *title) { +void Lua_Client::SetAATitle(std::string title) { Lua_Safe_Call_Void(); self->SetAATitle(title); } +void Lua_Client::SetAATitle(std::string title, bool save_to_database) { + Lua_Safe_Call_Void(); + if (!save_to_database) { + self->SetAATitle(title); + } else { + title_manager.CreateNewPlayerTitle(self, title); + } +} + int Lua_Client::GetClientVersion() { Lua_Safe_Call_Int(); return static_cast(self->ClientVersion()); @@ -2558,7 +2568,7 @@ luabind::scope lua_register_client() { .def("CalcCurrentWeight", &Lua_Client::CalcCurrentWeight) .def("CalcPriceMod", (float(Lua_Client::*)(Lua_Mob,bool))&Lua_Client::CalcPriceMod) .def("CanHaveSkill", (bool(Lua_Client::*)(int))&Lua_Client::CanHaveSkill) - .def("ChangeLastName", (void(Lua_Client::*)(const char *in))&Lua_Client::ChangeLastName) + .def("ChangeLastName", (void(Lua_Client::*)(std::string))&Lua_Client::ChangeLastName) .def("CharacterID", (uint32(Lua_Client::*)(void))&Lua_Client::CharacterID) .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob))&Lua_Client::CheckIncreaseSkill) .def("CheckIncreaseSkill", (void(Lua_Client::*)(int,Lua_Mob,int))&Lua_Client::CheckIncreaseSkill) @@ -2838,7 +2848,8 @@ luabind::scope lua_register_client() { .def("SendZoneFlagInfo", (void(Lua_Client::*)(Lua_Client))&Lua_Client::SendZoneFlagInfo) .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,double))&Lua_Client::SetAAEXPModifier) .def("SetAAPoints", (void(Lua_Client::*)(int))&Lua_Client::SetAAPoints) - .def("SetAATitle", (void(Lua_Client::*)(const char *))&Lua_Client::SetAATitle) + .def("SetAATitle", (void(Lua_Client::*)(std::string))&Lua_Client::SetAATitle) + .def("SetAATitle", (void(Lua_Client::*)(std::string,bool))&Lua_Client::SetAATitle) .def("SetAFK", (void(Lua_Client::*)(uint8))&Lua_Client::SetAFK) .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) .def("SetAccountFlag", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetAccountFlag) diff --git a/zone/lua_client.h b/zone/lua_client.h index 0060631b8..5bb841c57 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -118,7 +118,7 @@ public: void MoveZoneInstance(uint16 instance_id); void MoveZoneInstanceGroup(uint16 instance_id); void MoveZoneInstanceRaid(uint16 instance_id); - void ChangeLastName(const char *in); + void ChangeLastName(std::string last_name); int GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 race, uint32 class_, uint32 deity, uint32 faction, Lua_NPC npc); void SetFactionLevel(uint32 char_id, uint32 npc_id, int char_class, int char_race, int char_deity); void SetFactionLevel2(uint32 char_id, int faction_id, int char_class, int char_race, int char_deity, int value, int temp); @@ -265,7 +265,8 @@ public: void LoadPEQZoneFlags(); void SendPEQZoneFlagInfo(Lua_Client to); void SetPEQZoneFlag(uint32 zone_id); - void SetAATitle(const char *title); + void SetAATitle(std::string title); + void SetAATitle(std::string title, bool save_to_database); int GetClientVersion(); uint32 GetClientVersionBit(); void SetTitleSuffix(const char *text); diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index c84cfe82e..edc0caa8a 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -575,10 +575,10 @@ bool Lua_NPC::IsRaidTarget() return self->IsRaidTarget(); } -void Lua_NPC::ChangeLastName(const char *lastname) +void Lua_NPC::ChangeLastName(std::string last_name) { Lua_Safe_Call_Void(); - self->ChangeLastName(lastname); + self->ChangeLastName(last_name); } void Lua_NPC::ClearLastName() @@ -680,7 +680,7 @@ luabind::scope lua_register_npc() { .def("AddLootTable", (void(Lua_NPC::*)(void))&Lua_NPC::AddLootTable) .def("AssignWaypoints", (void(Lua_NPC::*)(int))&Lua_NPC::AssignWaypoints) .def("CalculateNewWaypoint", (void(Lua_NPC::*)(void))&Lua_NPC::CalculateNewWaypoint) - .def("ChangeLastName", (void(Lua_NPC::*)(const char*))&Lua_NPC::ChangeLastName) + .def("ChangeLastName", (void(Lua_NPC::*)(std::string))&Lua_NPC::ChangeLastName) .def("CheckNPCFactionAlly", (int(Lua_NPC::*)(int))&Lua_NPC::CheckNPCFactionAlly) .def("ClearItemList", (void(Lua_NPC::*)(void))&Lua_NPC::ClearItemList) .def("ClearLastName", (void(Lua_NPC::*)(void))&Lua_NPC::ClearLastName) diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 6b1e71363..90a91e3aa 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -140,7 +140,7 @@ public: void RecalculateSkills(); void ScaleNPC(uint8 npc_level); bool IsRaidTarget(); - void ChangeLastName(const char *lastname); + void ChangeLastName(std::string last_name); void ClearLastName(); bool HasItem(uint32 item_id); uint16 CountItem(uint32 item_id); diff --git a/zone/npc.cpp b/zone/npc.cpp index faaf29ea2..52f7348da 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3231,27 +3231,29 @@ void NPC::DoQuestPause(Mob *other) { } -void NPC::ChangeLastName(const char* in_lastname) +void NPC::ChangeLastName(std::string last_name) { - auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct)); - GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer; - strcpy(gmn->name, GetName()); - strcpy(gmn->gmname, GetName()); - strcpy(gmn->lastname, in_lastname); - gmn->unknown[0]=1; - gmn->unknown[1]=1; - gmn->unknown[2]=1; - gmn->unknown[3]=1; + auto gmn = (GMLastName_Struct*) outapp->pBuffer; + + strn0cpy(gmn->name, GetName(), sizeof(gmn->name)); + strn0cpy(gmn->gmname, GetName(), sizeof(gmn->gmname)); + strn0cpy(gmn->lastname, last_name.c_str(), sizeof(gmn->lastname)); + + gmn->unknown[0] = 1; + gmn->unknown[1] = 1; + gmn->unknown[2] = 1; + gmn->unknown[3] = 1; + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); } void NPC::ClearLastName() { - std::string WT; - WT = '\0'; //Clear Last Name - ChangeLastName( WT.c_str()); + std::string empty; + ChangeLastName(empty); } void NPC::DepopSwarmPets() diff --git a/zone/npc.h b/zone/npc.h index bb47093d2..76ff091ef 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -441,7 +441,7 @@ public: virtual int GetKillExpMod() const { return NPCTypedata_ours ? NPCTypedata_ours->exp_mod : NPCTypedata->exp_mod; } - void ChangeLastName(const char* in_lastname); + void ChangeLastName(std::string last_name); void ClearLastName(); bool GetDepop() { return p_depop; } diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 1a82f34ec..732d6cf55 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1163,9 +1163,9 @@ XS(XS_Client_ChangeLastName) { Perl_croak(aTHX_ "Usage: Client::ChangeLastName(THIS, string last_name)"); // @categories Account and Character { Client *THIS; - char *in_lastname = (char *) SvPV_nolen(ST(1)); + std::string last_name = (std::string) SvPV_nolen(ST(1)); VALIDATE_THIS_IS_CLIENT; - THIS->ChangeLastName(in_lastname); + THIS->ChangeLastName(last_name); } XSRETURN_EMPTY; } @@ -2965,23 +2965,27 @@ XS(XS_Client_LoadZoneFlags) { XS(XS_Client_SetAATitle); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetAATitle) { dXSARGS; - if ((items < 2) || (items > 3)) + if (items < 2 || items > 3) Perl_croak(aTHX_ "Usage: Client::SetAATitle(THIS, string text, [bool save = false])"); // @categories Alternative Advancement { Client *THIS; - char *txt = (char *) SvPV_nolen(ST(1)); - bool SaveTitle = false; + std::string title = (std::string) SvPV_nolen(ST(1)); + bool save = false; VALIDATE_THIS_IS_CLIENT; - if (strlen(txt) > 31) - Perl_croak(aTHX_ "Title must be 31 characters or less"); - if (items == 3) - SaveTitle = (SvIV(ST(2)) != 0); + if (title.size() > 31) { + Perl_croak(aTHX_ "Title must be 31 characters or less."); + } - if (!SaveTitle) - THIS->SetAATitle(txt); - else - title_manager.CreateNewPlayerTitle(THIS, txt); + if (items == 3) { + save = (bool) SvTRUE(ST(2)); + } + + if (!save) { + THIS->SetAATitle(title); + } else { + title_manager.CreateNewPlayerTitle(THIS, title); + } } XSRETURN_EMPTY; } @@ -3023,23 +3027,27 @@ XS(XS_Client_GetClientVersionBit) { XS(XS_Client_SetTitleSuffix); XS(XS_Client_SetTitleSuffix) { dXSARGS; - if ((items < 2) || (items > 3)) - Perl_croak(aTHX_ "Usage: Client::SetTitleSuffix(THIS, string text, [bool save = false])"); // @categories Account and Character + if (items < 2 || items > 3) + Perl_croak(aTHX_ "Usage: Client::SetTitleSuffix(THIS, string suffix, [bool save = false])"); // @categories Account and Character { Client *THIS; - char *txt = (char *) SvPV_nolen(ST(1)); - bool SaveSuffix = false; + std::string suffix = (std::string) SvPV_nolen(ST(1)); + bool save = false; VALIDATE_THIS_IS_CLIENT; - if (strlen(txt) > 31) - Perl_croak(aTHX_ "Title must be 31 characters or less"); - if (items == 3) - SaveSuffix = (SvIV(ST(2)) != 0); + if (suffix.size() > 31) { + Perl_croak(aTHX_ "Suffix must be 31 characters or less."); + } - if (!SaveSuffix) - THIS->SetTitleSuffix(txt); - else - title_manager.CreateNewPlayerSuffix(THIS, txt); + if (items == 3) { + save = (bool) SvTRUE(ST(2)); + } + + if (!save) { + THIS->SetTitleSuffix(suffix); + } else { + title_manager.CreateNewPlayerSuffix(THIS, suffix); + } } XSRETURN_EMPTY; } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 78a9f7402..a0cb935f3 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1625,15 +1625,13 @@ XS(XS_NPC_RemoveDefensiveProc) { XS(XS_NPC_ChangeLastName); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_ChangeLastName) { dXSARGS; - if (items < 1 || items > 2) - Perl_croak(aTHX_ "Usage: NPC::ChangeLastName(THIS, string name)"); // @categories Script Utility + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::ChangeLastName(THIS, string last_name)"); // @categories Script Utility { NPC *THIS; - char *name = nullptr; + std::string last_name = (std::string) SvPV_nolen(ST(1)); VALIDATE_THIS_IS_NPC; - if (items > 1) { name = (char *) SvPV_nolen(ST(1)); } - - THIS->ChangeLastName(name); + THIS->ChangeLastName(last_name); } XSRETURN_EMPTY; } diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index a083233ba..faf58eaa9 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1111,20 +1111,18 @@ void QuestManager::rename(std::string name) { } } -void QuestManager::surname(const char *name) { +void QuestManager::surname(std::string last_name) { QuestManagerCurrentQuestVars(); //Changes the last name. - if(initiator) - { - if(initiator->IsClient()) - { - initiator->ChangeLastName(name); - initiator->Message(Chat::Yellow,"Your surname has been changed/set to: %s", name); - } - else - { - initiator->Message(Chat::Yellow,"Error changing/setting surname"); - } + if (initiator && initiator->IsClient()) { + initiator->ChangeLastName(last_name); + initiator->Message( + Chat::White, + fmt::format( + "Your last name has been set to \"{}\".", + last_name + ).c_str() + ); } } diff --git a/zone/questmgr.h b/zone/questmgr.h index d5b93b83a..96d8a51e6 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -127,7 +127,7 @@ public: void rain(int weather); void snow(int weather); void rename(std::string name); - void surname(const char *name); + void surname(std::string last_name); void permaclass(int class_id); void permarace(int race_id); void permagender(int gender_id); diff --git a/zone/titles.cpp b/zone/titles.cpp index 8bdabc678..14230c4bd 100644 --- a/zone/titles.cpp +++ b/zone/titles.cpp @@ -225,9 +225,9 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int skill_id, int skill_value) return false; } -void TitleManager::CreateNewPlayerTitle(Client *client, const char *title) +void TitleManager::CreateNewPlayerTitle(Client *client, std::string title) { - if (!client || !title) { + if (!client || title.empty()) { return; } @@ -258,15 +258,15 @@ void TitleManager::CreateNewPlayerTitle(Client *client, const char *title) safe_delete(pack); } -void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix) +void TitleManager::CreateNewPlayerSuffix(Client *client, std::string suffix) { - if (!client || !suffix) { + if (!client || suffix.empty()) { return; } client->SetTitleSuffix(suffix); - std::string query = fmt::format( + auto query = fmt::format( "SELECT `id` FROM titles WHERE `suffix` = '{}' AND char_id = {}", EscapeString(suffix), client->CharacterID() @@ -291,24 +291,24 @@ void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix) safe_delete(pack); } -void Client::SetAATitle(const char *title) +void Client::SetAATitle(std::string title) { - strn0cpy(m_pp.title, title, sizeof(m_pp.title)); + strn0cpy(m_pp.title, title.c_str(), sizeof(m_pp.title)); auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); - SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; - strn0cpy(strs->title, title, sizeof(strs->title)); + auto strs = (SetTitleReply_Struct *) outapp->pBuffer; + strn0cpy(strs->title, title.c_str(), sizeof(strs->title)); strs->entity_id = GetID(); entity_list.QueueClients(this, outapp, false); safe_delete(outapp); } -void Client::SetTitleSuffix(const char *suffix) +void Client::SetTitleSuffix(std::string suffix) { - strn0cpy(m_pp.suffix, suffix, sizeof(m_pp.suffix)); + strn0cpy(m_pp.suffix, suffix.c_str(), sizeof(m_pp.suffix)); auto outapp = new EQApplicationPacket(OP_SetTitleReply, sizeof(SetTitleReply_Struct)); - SetTitleReply_Struct *strs = (SetTitleReply_Struct *)outapp->pBuffer; + auto strs = (SetTitleReply_Struct *) outapp->pBuffer; strs->is_suffix = 1; - strn0cpy(strs->title, suffix, sizeof(strs->title)); + strn0cpy(strs->title, suffix.c_str(), sizeof(strs->title)); strs->entity_id = GetID(); entity_list.QueueClients(this, outapp, false); safe_delete(outapp); diff --git a/zone/titles.h b/zone/titles.h index 640d4d29a..5e29e512c 100644 --- a/zone/titles.h +++ b/zone/titles.h @@ -55,8 +55,8 @@ public: bool IsClientEligibleForTitle(Client *client, TitleEntry title); bool IsNewAATitleAvailable(int aa_points, int class_id); bool IsNewTradeSkillTitleAvailable(int skill_id, int skill_value); - void CreateNewPlayerTitle(Client *client, const char *title); - void CreateNewPlayerSuffix(Client *client, const char *suffix); + void CreateNewPlayerTitle(Client *client, std::string title); + void CreateNewPlayerSuffix(Client *client, std::string suffix); bool HasTitle(Client* client, uint32 title_id); protected: From f3e5423677e81fa83bdec6e57fba0f54d04cba15 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 20 May 2022 11:49:18 -0400 Subject: [PATCH 003/552] [Bug Fix] Fix duplicate and missing messages due to innate in spells (#2170) * [Bug Fix] Fix duplicate and missing messages due to innate skill in spells. * Seperate spell and melee damage range and skip * Refine when innate messages are produced. * Fix magic # (replace with constant) --- zone/attack.cpp | 53 +++++++++++++++++++++++++++++++++++++------------ zone/spells.cpp | 20 ++----------------- 2 files changed, 42 insertions(+), 31 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 58019b238..ba7453836 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3675,7 +3675,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons //we used to do a message to the client, but its gone now. // emote goes with every one ... even npcs - entity_list.MessageClose(this, true, RuleI(Range, SpellMessages), Chat::Emote, "%s beams a smile at %s", attacker->GetCleanName(), GetCleanName()); + entity_list.MessageClose(this, false, RuleI(Range, SpellMessages), Chat::Emote, "%s beams a smile at %s", attacker->GetCleanName(), GetCleanName()); } // If a client pet is damaged while sitting, stand, fix sit button, @@ -4021,20 +4021,47 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons // we don't send them here. if (!FromDamageShield) { - entity_list.QueueCloseClients( - this, /* Sender */ - outapp, /* packet */ - true, /* Skip Sender */ - RuleI(Range, SpellMessages), - skip, /* Skip this mob */ - true, /* Packet ACK */ - filter /* eqFilterType filter */ - ); + // Determine message range based on spell/other-damage + int range; + if (IsValidSpell(spell_id)) { + range = RuleI(Range, SpellMessages); + } + else { + range = RuleI(Range, DamageMessages); + } - //send the damage to ourself if we are a client - if (IsClient()) { + // If an "innate" spell, change to spell type to + // produce a spell message. Send to everyone. + // This fixes issues with npc-procs like 1002 and 918 which + // need to spit out extra spell color. + if (IsValidSpell(spell_id) && skill_used == EQ::skills::SkillTigerClaw) { + a->type = DamageTypeSpell; + entity_list.QueueCloseClients( + this, /* Sender */ + outapp, /* packet */ + false, /* Skip Sender */ + range, /* distance packet travels at the speed of sound */ + 0, /* don't skip anyone on spell */ + true, /* Packet ACK */ + filter /* eqFilterType filter */ + ); + } + else { //I dont think any filters apply to damage affecting us - CastToClient()->QueuePacket(outapp); + if (IsClient()) { + CastToClient()->QueuePacket(outapp); + } + + // Otherwise, send normal spell or melee message to observers. + entity_list.QueueCloseClients( + this, /* Sender */ + outapp, /* packet */ + true, /* Skip Sender */ + range, /* distance packet travels at the speed of sound */ + (IsValidSpell(spell_id) && skill_used != EQ::skills::SkillTigerClaw) ? 0 : skip, + true, /* Packet ACK */ + filter /* eqFilterType filter */ + ); } } diff --git a/zone/spells.cpp b/zone/spells.cpp index d46ae3528..dfa5c7eb8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3509,8 +3509,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes if (!IsValidSpell(spell_id)) return false; - bool is_damage_or_lifetap_spell = IsDamageSpell(spell_id) || IsLifetapSpell(spell_id); - if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar, true) && !IsResurrectionEffects(spell_id) && !IsEffectInSpell(spell_id, SE_BindSight)) { if(!IsClient() || !CastToClient()->GetGM()) { MessageString(Chat::SpellFailure, SPELL_NO_HOLD); @@ -4107,9 +4105,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes if(IsClient()) // send to caster CastToClient()->QueuePacket(action_packet); } - // send to people in the area, ignoring caster and target - //live dosent send this to anybody but the caster - //entity_list.QueueCloseClients(spelltar, action_packet, true, 200, this, true, spelltar->IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); + message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; cd->target = action->target; @@ -4121,7 +4117,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes cd->hit_pitch = action->hit_pitch; cd->damage = 0; - if(!IsEffectInSpell(spell_id, SE_BindAffinity) && !is_damage_or_lifetap_spell){ + if (!IsLifetapSpell(spell_id) && !IsEffectInSpell(spell_id, SE_BindAffinity) && !IsAENukeSpell(spell_id) && !IsDamageSpell(spell_id)) { entity_list.QueueCloseClients( spelltar, /* Sender */ message_packet, /* Packet */ @@ -4131,18 +4127,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, int reflect_effectivenes true, /* Packet ACK */ (spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells) /* Message Filter Type: (8 or 9) */ ); - } else if (is_damage_or_lifetap_spell) { - // Sends the client owner a message like "%T staggers" - if (spellOwner->IsClient()) { - spellOwner->CastToClient()->QueuePacket(message_packet, true, - Mob::CLIENT_CONNECTINGALL, FilterPCSpells); - } - // Show the "you feel your life force drain away" on target client... - if (IsLifetapSpell(spell_id) && spelltar->IsClient()) { - spelltar->CastToClient()->QueuePacket(message_packet, true, - Mob::CLIENT_CONNECTINGALL, - (spellOwner->IsClient() ? FilterPCSpells : FilterNPCSpells)); - } } safe_delete(action_packet); safe_delete(message_packet); From 089246db53e368563c3ac36ca8687f8913b0ba46 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 21 May 2022 10:26:45 -0400 Subject: [PATCH 004/552] [Cleanup] Move Client::Undye() to client.cpp from #path Command. (#2188) - Client::Undye() was inside the #path command file. --- zone/client.cpp | 18 ++++++++++++++++++ zone/gm_commands/path.cpp | 18 ------------------ 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 16c28e20b..a7f0f0cbd 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -11730,3 +11730,21 @@ std::map Client::GetMerchantDataBuckets() return merchant_data_buckets; } + +void Client::Undye() +{ + for (uint8 slot = EQ::textures::textureBegin; slot <= EQ::textures::LastTexture; slot++) { + auto inventory_slot = SlotConvert(slot); + auto inst = m_inv.GetItem(inventory_slot); + + if (inst) { + inst->SetColor(inst->GetItem()->Color); + database.SaveInventory(CharacterID(), inst, inventory_slot); + } + + m_pp.item_tint.Slot[slot].Color = 0; + SendWearChange(slot); + } + + database.DeleteCharacterDye(CharacterID()); +} \ No newline at end of file diff --git a/zone/gm_commands/path.cpp b/zone/gm_commands/path.cpp index 80207e07c..79d33c3cd 100755 --- a/zone/gm_commands/path.cpp +++ b/zone/gm_commands/path.cpp @@ -7,21 +7,3 @@ void command_path(Client *c, const Seperator *sep) } } -void Client::Undye() -{ - for (int cur_slot = EQ::textures::textureBegin; cur_slot <= EQ::textures::LastTexture; cur_slot++) { - uint8 slot2 = SlotConvert(cur_slot); - EQ::ItemInstance *inst = m_inv.GetItem(slot2); - - if (inst != nullptr) { - inst->SetColor(inst->GetItem()->Color); - database.SaveInventory(CharacterID(), inst, slot2); - } - - m_pp.item_tint.Slot[cur_slot].Color = 0; - SendWearChange(cur_slot); - } - - database.DeleteCharacterDye(CharacterID()); -} - From 6b85c914a5acdd9bb9bd9a952b65c162c2ded31f Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Sat, 21 May 2022 22:44:04 -0500 Subject: [PATCH 005/552] Schema consistency fixes (#2192) --- common/version.h | 2 +- utils/sql/db_update_manifest.txt | 1 + utils/sql/git/required/2022_04_30_hp_regen_per_second.sql | 2 +- utils/sql/git/required/2022_05_02_npc_types_int64.sql | 8 ++++---- utils/sql/git/required/2022_05_21_schema_consistency.sql | 5 +++++ 5 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 utils/sql/git/required/2022_05_21_schema_consistency.sql diff --git a/common/version.h b/common/version.h index b9f3d43b3..7f186361a 100644 --- a/common/version.h +++ b/common/version.h @@ -34,7 +34,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9183 +#define CURRENT_BINARY_DATABASE_VERSION 9184 #ifdef BOTS #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 8b17eba27..0b4410c81 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -437,6 +437,7 @@ 9181|2022_05_03_task_activity_goal_match_list.sql|SHOW COLUMNS FROM `task_activities` LIKE 'goal_match_list'|empty| 9182|2022_05_02_npc_types_int64.sql|SHOW COLUMNS FROM `npc_types` LIKE 'hp'|missing|bigint 9183|2022_05_07_merchant_data_buckets.sql|SHOW COLUMNS FROM `merchantlist` LIKE 'bucket_comparison'|empty +9184|2022_05_21_schema_consistency.sql|SELECT * FROM db_version WHERE version >= 9184|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/required/2022_04_30_hp_regen_per_second.sql b/utils/sql/git/required/2022_04_30_hp_regen_per_second.sql index cc2d85a47..6f61ae838 100644 --- a/utils/sql/git/required/2022_04_30_hp_regen_per_second.sql +++ b/utils/sql/git/required/2022_04_30_hp_regen_per_second.sql @@ -1 +1 @@ -ALTER TABLE npc_types ADD COLUMN hp_regen_per_second bigint(11) DEFAULT 0 AFTER hp_regen_rate; \ No newline at end of file +ALTER TABLE npc_types ADD COLUMN hp_regen_per_second bigint DEFAULT 0 AFTER hp_regen_rate; diff --git a/utils/sql/git/required/2022_05_02_npc_types_int64.sql b/utils/sql/git/required/2022_05_02_npc_types_int64.sql index 5a018b919..fb52824ee 100644 --- a/utils/sql/git/required/2022_05_02_npc_types_int64.sql +++ b/utils/sql/git/required/2022_05_02_npc_types_int64.sql @@ -1,4 +1,4 @@ -ALTER TABLE npc_types MODIFY COLUMN hp BIGINT; -ALTER TABLE npc_types MODIFY COLUMN mana BIGINT; -ALTER TABLE npc_types MODIFY COLUMN hp_regen_rate BIGINT; -ALTER TABLE npc_types MODIFY COLUMN mana_regen_rate BIGINT; +ALTER TABLE npc_types MODIFY COLUMN hp BIGINT NOT NULL DEFAULT 0; +ALTER TABLE npc_types MODIFY COLUMN mana BIGINT NOT NULL DEFAULT 0; +ALTER TABLE npc_types MODIFY COLUMN hp_regen_rate BIGINT NOT NULL DEFAULT 0; +ALTER TABLE npc_types MODIFY COLUMN mana_regen_rate BIGINT NOT NULL DEFAULT 0; diff --git a/utils/sql/git/required/2022_05_21_schema_consistency.sql b/utils/sql/git/required/2022_05_21_schema_consistency.sql new file mode 100644 index 000000000..5f8004f15 --- /dev/null +++ b/utils/sql/git/required/2022_05_21_schema_consistency.sql @@ -0,0 +1,5 @@ +ALTER TABLE npc_types MODIFY COLUMN hp BIGINT NOT NULL DEFAULT 0; +ALTER TABLE npc_types MODIFY COLUMN mana BIGINT NOT NULL DEFAULT 0; +ALTER TABLE npc_types MODIFY COLUMN hp_regen_rate BIGINT NOT NULL DEFAULT 0; +ALTER TABLE npc_types MODIFY COLUMN mana_regen_rate BIGINT NOT NULL DEFAULT 0; +ALTER TABLE npc_types MODIFY COLUMN hp_regen_per_second BIGINT NOT NULL DEFAULT 0; From 992e4ac59e0cb243c3d951fa1417b70ef3d06961 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 22 May 2022 22:30:56 -0400 Subject: [PATCH 006/552] [Commands] Consolidate #lock and #unlock Commands into #serverlock. (#2193) - Convert the two commands into one command. - Cleanup struct naming. --- common/servertalk.h | 4 ++-- world/zoneserver.cpp | 16 ++++++++-------- zone/command.cpp | 6 ++---- zone/command.h | 2 +- zone/gm_commands/lock.cpp | 15 --------------- zone/gm_commands/serverlock.cpp | 22 ++++++++++++++++++++++ zone/gm_commands/unlock.cpp | 15 --------------- 7 files changed, 35 insertions(+), 45 deletions(-) delete mode 100755 zone/gm_commands/lock.cpp create mode 100644 zone/gm_commands/serverlock.cpp delete mode 100755 zone/gm_commands/unlock.cpp diff --git a/common/servertalk.h b/common/servertalk.h index bcc4d739f..b780175cb 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -740,8 +740,8 @@ struct ServerMultiLineMsg_Struct { }; struct ServerLock_Struct { - char myname[64]; // User that did it - uint8 mode; // 0 = Unlocked ; 1 = Locked + char character_name[64]; + bool is_locked; }; struct ServerMotd_Struct { diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 88e3dfcf0..83a00572f 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -972,8 +972,8 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { break; } - auto slock = (ServerLock_Struct*) pack->pBuffer; - if (slock->mode >= 1) { + auto l = (ServerLock_Struct*) pack->pBuffer; + if (l->is_locked) { WorldConfig::LockWorld(); } else { WorldConfig::UnlockWorld(); @@ -982,24 +982,24 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { if (loginserverlist.Connected()) { loginserverlist.SendStatus(); SendEmoteMessage( - slock->myname, + l->character_name, 0, AccountStatus::Player, - Chat::Red, + Chat::Yellow, fmt::format( "World {}.", - slock->mode ? "locked" : "unlocked" + l->is_locked ? "locked" : "unlocked" ).c_str() ); } else { SendEmoteMessage( - slock->myname, + l->character_name, 0, AccountStatus::Player, - Chat::Red, + Chat::Yellow, fmt::format( "World {}, but login server not connected.", - slock->mode ? "locked" : "unlocked" + l->is_locked ? "locked" : "unlocked" ).c_str() ); } diff --git a/zone/command.cpp b/zone/command.cpp index 43e755a7c..d5873fe23 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -229,7 +229,6 @@ int command_init(void) command_add("listpetition", "- List petitions", AccountStatus::Guide, command_listpetition) || command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", AccountStatus::GMImpossible, command_load_shared_memory) || command_add("loc", "- Print out your or your target's current location and heading", AccountStatus::Player, command_loc) || - command_add("lock", "- Lock the worldserver", AccountStatus::GMLeadAdmin, command_lock) || command_add("logs", "Manage anything to do with logs", AccountStatus::GMImpossible, command_logs) || command_add("makepet", "[Pet Name] - Make a pet", AccountStatus::Guide, command_makepet) || command_add("mana", "- Fill your or your target's mana", AccountStatus::Guide, command_mana) || @@ -299,6 +298,7 @@ int command_init(void) command_add("sendzonespawns", "- Refresh spawn list for all clients in zone", AccountStatus::GMLeadAdmin, command_sendzonespawns) || command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", AccountStatus::Player, command_sensetrap) || command_add("serverinfo", "- Get CPU, Operating System, and Process Information about the server", AccountStatus::GMMgmt, command_serverinfo) || + command_add("serverlock", "[0|1] - Lock or Unlock the World Server (0 = Unlocked, 1 = Locked)", AccountStatus::GMLeadAdmin, command_serverlock) || command_add("serverrules", "- Read this server's rules", AccountStatus::Player, command_serverrules) || command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", AccountStatus::GMAdmin, command_setaapts) || command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", AccountStatus::GMAdmin, command_setaaxp) || @@ -355,7 +355,6 @@ int command_init(void) command_add("undye", "- Remove dye from all of your or your target's armor slots", AccountStatus::GMAdmin, command_undye) || command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || - command_add("unlock", "- Unlock the worldserver", AccountStatus::GMLeadAdmin, command_unlock) || command_add("unmemspell", "[Spell ID] - Unmemorize a Spell by ID for you or your target", AccountStatus::Guide, command_unmemspell) || command_add("unmemspells", " - Unmemorize all spells for you or your target", AccountStatus::Guide, command_unmemspells) || command_add("unscribespell", "[Spell ID] - Unscribe a spell from your or your target's spell book by Spell ID", AccountStatus::GMCoder, command_unscribespell) || @@ -1242,7 +1241,6 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/list.cpp" #include "gm_commands/listpetition.cpp" #include "gm_commands/loc.cpp" -#include "gm_commands/lock.cpp" #include "gm_commands/logcommand.cpp" #include "gm_commands/logs.cpp" #include "gm_commands/makepet.cpp" @@ -1314,6 +1312,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/sendzonespawns.cpp" #include "gm_commands/sensetrap.cpp" #include "gm_commands/serverinfo.cpp" +#include "gm_commands/serverlock.cpp" #include "gm_commands/serverrules.cpp" #include "gm_commands/set_adventure_points.cpp" #include "gm_commands/setaapts.cpp" @@ -1368,7 +1367,6 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/undye.cpp" #include "gm_commands/undyeme.cpp" #include "gm_commands/unfreeze.cpp" -#include "gm_commands/unlock.cpp" #include "gm_commands/unmemspell.cpp" #include "gm_commands/unmemspells.cpp" #include "gm_commands/unscribespell.cpp" diff --git a/zone/command.h b/zone/command.h index ab25202ba..b6d766d70 100644 --- a/zone/command.h +++ b/zone/command.h @@ -139,7 +139,6 @@ void command_list(Client *c, const Seperator *sep); void command_listpetition(Client *c, const Seperator *sep); void command_load_shared_memory(Client *c, const Seperator *sep); void command_loc(Client *c, const Seperator *sep); -void command_lock(Client *c, const Seperator *sep); void command_logs(Client *c, const Seperator *sep); void command_makepet(Client *c, const Seperator *sep); void command_mana(Client *c, const Seperator *sep); @@ -214,6 +213,7 @@ void command_revoke(Client *c, const Seperator *sep); void command_roambox(Client *c, const Seperator *sep); void command_rules(Client *c, const Seperator *sep); void command_save(Client *c, const Seperator *sep); +void command_serverlock(Client *c, const Seperator *sep); void command_scale(Client *c, const Seperator *sep); void command_scribespell(Client *c, const Seperator *sep); void command_scribespells(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/lock.cpp b/zone/gm_commands/lock.cpp deleted file mode 100755 index 1fda53841..000000000 --- a/zone/gm_commands/lock.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "../client.h" -#include "../worldserver.h" - -extern WorldServer worldserver; - -void command_lock(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); - ServerLock_Struct *lss = (ServerLock_Struct *) outpack->pBuffer; - strcpy(lss->myname, c->GetName()); - lss->mode = 1; - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - diff --git a/zone/gm_commands/serverlock.cpp b/zone/gm_commands/serverlock.cpp new file mode 100644 index 000000000..59a43f912 --- /dev/null +++ b/zone/gm_commands/serverlock.cpp @@ -0,0 +1,22 @@ +#include "../client.h" +#include "../worldserver.h" + +extern WorldServer worldserver; + +void command_serverlock(Client *c, const Seperator *sep) +{ + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #serverlock [0|1] - Lock or Unlock the World Server (0 = Unlocked, 1 = Locked)"); + return; + } + + auto is_locked = std::stoi(sep->arg[1]) ? true : false; + + auto pack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); + auto l = (ServerLock_Struct *) pack->pBuffer; + strn0cpy(l->character_name, c->GetCleanName(), sizeof(l->character_name)); + l->is_locked = is_locked; + worldserver.SendPacket(pack); + safe_delete(pack); +} + diff --git a/zone/gm_commands/unlock.cpp b/zone/gm_commands/unlock.cpp deleted file mode 100755 index 2d35220e6..000000000 --- a/zone/gm_commands/unlock.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "../client.h" -#include "../worldserver.h" - -extern WorldServer worldserver; - -void command_unlock(Client *c, const Seperator *sep) -{ - auto outpack = new ServerPacket(ServerOP_Lock, sizeof(ServerLock_Struct)); - ServerLock_Struct *lss = (ServerLock_Struct *) outpack->pBuffer; - strcpy(lss->myname, c->GetName()); - lss->mode = 0; - worldserver.SendPacket(outpack); - safe_delete(outpack); -} - From 5b90d26a33caa3c334ad37496450dd2e92fa642f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 22 May 2022 22:31:03 -0400 Subject: [PATCH 007/552] [Bug Fix] Fix bot guild removal. (#2194) --- zone/bot.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8e656617a..36e037b85 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6788,7 +6788,7 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { if(guildOfficer && !botName.empty()) { Bot* botToUnGuild = entity_list.GetBotByBotName(botName); if(botToUnGuild) { - if (database.botdb.SaveGuildMembership(botToUnGuild->GetBotID(), 0, 0)) + if (database.botdb.DeleteGuildMembership(botToUnGuild->GetBotID())) Result = true; } else { uint32 ownerId = 0; @@ -6797,7 +6797,7 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { uint32 botId = 0; if (!database.botdb.LoadBotID(ownerId, botName, botId)) guildOfficer->Message(Chat::Red, "%s for '%s'", BotDatabase::fail::LoadBotID(), botName.c_str()); - if (botId && database.botdb.SaveGuildMembership(botId, 0, 0)) + if (botId && database.botdb.DeleteGuildMembership(botId)) Result = true; } From e43538cf7346db73bb6aa72efeaff2bd9c47b352 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 22 May 2022 22:31:08 -0400 Subject: [PATCH 008/552] [Commands] Cleanup #kill Command. (#2195) - Cleanup messages and logic. --- zone/gm_commands/kill.cpp | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/zone/gm_commands/kill.cpp b/zone/gm_commands/kill.cpp index d54245662..989df7294 100755 --- a/zone/gm_commands/kill.cpp +++ b/zone/gm_commands/kill.cpp @@ -2,11 +2,27 @@ void command_kill(Client *c, const Seperator *sep) { - if (!c->GetTarget()) { - c->Message(Chat::White, "Error: #Kill: No target."); + auto target = c->GetTarget(); + if (!target) { + c->Message(Chat::White, "You must have a target to use this command."); + return; } - else if (!c->GetTarget()->IsClient() || c->GetTarget()->CastToClient()->Admin() <= c->Admin()) { - c->GetTarget()->Kill(); + + if ( + !target->IsClient() || + target->CastToClient()->Admin() <= c->Admin() + ) { + if (c != target) { + c->Message( + Chat::White, + fmt::format( + "Killing {}.", + c->GetTargetDescription(target) + ).c_str() + ); + } + + target->Kill(); } } From a7a525ed0bf7fa668ac727a022e1e8a640c13862 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 22 May 2022 22:31:14 -0400 Subject: [PATCH 009/552] [Commands] #bind Typo. (#2196) --- zone/gm_commands/bind.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/gm_commands/bind.cpp b/zone/gm_commands/bind.cpp index 4c1b56644..f90e131af 100755 --- a/zone/gm_commands/bind.cpp +++ b/zone/gm_commands/bind.cpp @@ -16,7 +16,7 @@ void command_bind(Client *c, const Seperator *sep) ); if (!bind_allowed) { - c->Message(Chat::White, "Yu cannot bind here."); + c->Message(Chat::White, "You cannot bind here."); return; } From eeacc62a9121a99079ab4ec69f450dc60205945e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 23 May 2022 19:56:03 -0400 Subject: [PATCH 010/552] [Rules] Cleanup all unused rules. (#2184) --- common/ruletypes.h | 41 ------------------------------------- zone/mod_functions.cpp | 1 - zone/mod_functions_base.cpp | 1 - 3 files changed, 43 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index d06f9280c..159eedd5d 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -150,8 +150,6 @@ RULE_INT(Character, InvSnapshotMinRetryM, 30, "Time to re-attempt an inventory s RULE_INT(Character, InvSnapshotHistoryD, 30, "Time to keep snapshot entries (days)") RULE_BOOL(Character, RestrictSpellScribing, false, "Setting whether to restrict spell scribing to allowable races/classes of spell scroll") RULE_BOOL(Character, UseStackablePickPocketing, true, "Allows stackable pickpocketed items to stack instead of only being allowed in empty inventory slots") -RULE_BOOL(Character, EnableAvoidanceCap, false, "Setting whether the avoidance cap should be activated") -RULE_INT(Character, AvoidanceCap, 750, "750 Is a pretty good value, seen people dodge all attacks beyond 1,000 Avoidance") RULE_BOOL(Character, AllowMQTarget, false, "Disables putting players in the 'hackers' list for targeting beyond the clip plane or attempting to target something untargetable") RULE_BOOL(Character, UseOldBindWound, false, "Uses the original bind wound behavior") RULE_BOOL(Character, GrantHoTTOnCreate, false, "Grant Health of Target's Target leadership AA on character creation") @@ -350,7 +348,6 @@ RULE_INT(Spells, MaxTotalSlotsPET, 30, "Maximum total of pet slots. The default RULE_BOOL (Spells, EnableBlockedBuffs, true, "Allow blocked spells") RULE_INT(Spells, ReflectType, 4, "Reflect type. 0=disabled, 1=single target player spells only, 2=all player spells, 3=all single target spells, 4=all spells") RULE_BOOL(Spells, ReflectMessagesClose, true, "True (Live functionality) is for Reflect messages to show to players within close proximity. False shows just player reflecting") -RULE_INT(Spells, VirusSpreadDistance, 30, "The distance a viral spell will jump to its next victim") RULE_BOOL(Spells, LiveLikeFocusEffects, true, "Determines whether specific healing, dmg and mana reduction focuses are randomized") RULE_INT(Spells, BaseImmunityLevel, 55, "The level that targets start to be immune to stun, fear and mez spells with a maximum level of 0") RULE_BOOL(Spells, NPCIgnoreBaseImmunity, true, "Whether or not NPC get to ignore the BaseImmunityLevel for their spells") @@ -441,17 +438,6 @@ RULE_REAL(Combat, AvgProcsPerMinute, 2.0, "Average proc rate per minute") RULE_REAL(Combat, ProcPerMinDexContrib, 0.075, "Increases the probability of a proc increased by DEX by the value indicated") RULE_REAL(Combat, BaseProcChance, 0.035, "Base chance for procs") RULE_REAL(Combat, ProcDexDivideBy, 11000, "Divisor for the probability of a proc increased by dexterity") -RULE_REAL(Combat, BaseHitChance, 69.0, "Base chance to hit") -RULE_REAL(Combat, NPCBonusHitChance, 26.0, "Bonus chance to hit for NPC") -RULE_REAL(Combat, HitFalloffMinor, 5.0, "Hit will fall off up to value over the initial level range (percent)") -RULE_REAL(Combat, HitFalloffModerate, 7.0, "Hit will fall off up to value over the three levels after the initial level range (percent)") -RULE_REAL(Combat, HitFalloffMajor, 50.0, "Hit will fall off sharply if we're outside the minor and moderate range") -RULE_REAL(Combat, HitBonusPerLevel, 1.2, "You gain this percentage of hit for every level you are above your target") -RULE_REAL(Combat, WeaponSkillFalloff, 0.33, "For every weapon skill point that's not maxed you lose this percentage of hit") -RULE_REAL(Combat, ArcheryHitPenalty, 0.25, "Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it") -RULE_REAL(Combat, AgiHitFactor, 0.01, "Factor with which agility is taken into account in the hit probability. Higher is better") -RULE_REAL(Combat, MinChancetoHit, 5.0, "Minimum percentage chance to hit with regular melee/ranged") -RULE_REAL(Combat, MaxChancetoHit, 95.0, "Maximum percentage chance to hit with regular melee/ranged") RULE_INT(Combat, MinRangedAttackDist, 25, "Minimum Distance to use Ranged Attacks") RULE_BOOL(Combat, ArcheryBonusRequiresStationary, true, "does the 2x archery bonus chance require a stationary npc") RULE_REAL(Combat, ArcheryNPCMultiplier, 1.0, "Value is multiplied by the regular dmg to get the archery dmg") @@ -460,40 +446,13 @@ RULE_INT(Combat, MaxRampageTargets, 3, "Maximum number of people hit with rampag RULE_INT(Combat, DefaultRampageTargets, 1, "Default number of people to hit with rampage") RULE_BOOL(Combat, RampageHitsTarget, false, "Rampage will hit the target if it still has targets left") RULE_INT(Combat, MaxFlurryHits, 2, "Maximum number of extra hits from flurry") -RULE_REAL(Combat, NPCACFactor, 2.25, "If UseIntervalAC is enabled, the armor class for NPC is divided by this value") -RULE_INT(Combat, ClothACSoftcap, 75, "If OldACSoftcapRules is true: armorclass softcap for cloth armor") -RULE_INT(Combat, LeatherACSoftcap, 100, "If OldACSoftcapRules is true: armorclass softcap for leather armor") -RULE_INT(Combat, MonkACSoftcap, 120, "If OldACSoftcapRules is true: armorclass softcap for monks") -RULE_INT(Combat, ChainACSoftcap, 200, "If OldACSoftcapRules is true: armorclass softcap for chain armor") -RULE_INT(Combat, PlateACSoftcap, 300, "If OldACSoftcapRules is true: armorclass softcap for plate armor") -RULE_REAL(Combat, AAMitigationACFactor, 3.0, "If OldACSoftcapRules: AA mitgation armorclass factor") -RULE_REAL(Combat, WarriorACSoftcapReturn, 0.45, "If OldACSoftcapRules: warrior armorclass softcap increase-factor") -RULE_REAL(Combat, KnightACSoftcapReturn, 0.33, "If OldACSoftcapRules: SHD/PAL/MNK armorclass softcap increase-factor") -RULE_REAL(Combat, LowPlateChainACSoftcapReturn, 0.23, "If OldACSoftcapRules: CLR/BRD/BSK/ROG/SHA/MNK armorclass softcap increase-factor") -RULE_REAL(Combat, LowChainLeatherACSoftcapReturn, 0.17, "If OldACSoftcapRules: RNG/BST armorclass softcap increase-factor") -RULE_REAL(Combat, CasterACSoftcapReturn, 0.06, "If OldACSoftcapRules: WIZ/MAG/NEC/ENC/DRU armorclass softcap increase-factor") -RULE_REAL(Combat, MiscACSoftcapReturn, 0.3, "If OldACSoftcapRules true/false: unspecified classes armorclass softcap increase-factor") -RULE_BOOL(Combat, OldACSoftcapRules, false, "Setting if the old softcap values should be used") -RULE_BOOL(Combat, UseOldDamageIntervalRules, false, "Use old damage formulas for everything") -RULE_REAL(Combat, WarACSoftcapReturn, 0.3448, "WAR armorclass softcap increase-factor") -RULE_REAL(Combat, ClrRngMnkBrdACSoftcapReturn, 0.3030, "CLR/RNG/MNK/BRD armorclass softcap increase-factor") -RULE_REAL(Combat, PalShdACSoftcapReturn, 0.3226, "SHD/PAL armorclass softcap increase-factor") -RULE_REAL(Combat, DruNecWizEncMagACSoftcapReturn, 0.2000, "DRU/NEC/WIZ/ENC/MAG softcap increase-factor") -RULE_REAL(Combat, RogShmBstBerACSoftcapReturn, 0.2500, "ROG/SHM/BST/BER softcap increase-factor") -RULE_REAL(Combat, SoftcapFactor, 1.88, "When UseIntervalAC is enabled, the softcap for mitigation capability is multiplied by this value") -RULE_REAL(Combat, ACthac0Factor, 0.55, "If a mob is attacked and the attack roll is greater than his defense roll, the attack rating is multiplied by this value") -RULE_REAL(Combat, ACthac20Factor, 0.55, "If a mob is attacked and his defense roll is greater than the attack roll, the attack rating is multiplied by this value") -RULE_INT(Combat, HitCapPre20, 40, "Hit cap before level 20. Live has it capped at 40") -RULE_INT(Combat, HitCapPre10, 20, "Hit cap before level 10. Live has it capped at 20") RULE_INT(Combat, MinHastedDelay, 400, "Minimum hasted combat delay") RULE_REAL(Combat, AvgDefProcsPerMinute, 2.0, "Average defense procs per minute") RULE_REAL(Combat, DefProcPerMinAgiContrib, 0.075, "How much agility contributes to defensive proc rate") -RULE_INT(Combat, SpecialAttackACBonus, 15, "Percent amount of damage per AC gained for certain special attacks (damage = AC*SpecialAttackACBonus/100)") RULE_INT(Combat, NPCFlurryChance, 20, "Chance for NPC to flurry") RULE_BOOL(Combat, TauntOverLevel, 1, "Allows you to taunt NPC's over warriors level") RULE_REAL(Combat, TauntSkillFalloff, 0.33, "For every taunt skill point that's not maxed you lose this percentage chance to taunt") RULE_BOOL(Combat, EXPFromDmgShield, false, "Determine if damage from a damage shield counts for experience gain") -RULE_INT(Combat, MonkACBonusWeight, 15, "Usually, a monk under this weight threshold gets an AC bonus") RULE_INT(Combat, QuiverHasteCap, 1000, "Quiver haste cap 1000 on live for a while, currently 700 on live") RULE_INT(Combat, BerserkerFrenzyStart, 35, "Percentage Health Points below which Warrior and Berserker start frenzy") RULE_INT(Combat, BerserkerFrenzyEnd, 45, "Percentage Health Points above which Warrior and Berserker end frenzy") diff --git a/zone/mod_functions.cpp b/zone/mod_functions.cpp index 8e5e7316a..523302495 100644 --- a/zone/mod_functions.cpp +++ b/zone/mod_functions.cpp @@ -128,7 +128,6 @@ float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychan //Final dodge chance float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechance); } -//Monk AC Bonus weight cap. Defined in Combat:MonkACBonusWeight //Usually 15, a monk under this weight threshold gets an AC bonus float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); } diff --git a/zone/mod_functions_base.cpp b/zone/mod_functions_base.cpp index a328a868f..3f30b066a 100644 --- a/zone/mod_functions_base.cpp +++ b/zone/mod_functions_base.cpp @@ -129,7 +129,6 @@ float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychan //Final dodge chance float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechance); } -//Monk AC Bonus weight cap. Defined in Combat:MonkACBonusWeight //Usually 15, a monk under this weight threshold gets an AC bonus float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); } From efd04f8324ebfde070945c86c22ed9bcb8ec8067 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 23 May 2022 19:56:19 -0400 Subject: [PATCH 011/552] [Commands] Cleanup #motd Command. (#2190) --- zone/command.cpp | 2 +- zone/gm_commands/motd.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index d5873fe23..efd259d28 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -237,7 +237,7 @@ int command_init(void) command_add("merchant_close_shop", "Closes a merchant shop", AccountStatus::GMAdmin, command_merchantcloseshop) || command_add("merchant_open_shop", "Opens a merchants shop", AccountStatus::GMAdmin, command_merchantopenshop) || command_add("modifynpcstat", "- Modifys a NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || - command_add("motd", "[new motd] - Set message of the day", AccountStatus::GMLeadAdmin, command_motd) || + command_add("motd", "[Message of the Day] - Set Message of the Day (leave empty to have no Message of the Day)", AccountStatus::GMLeadAdmin, command_motd) || command_add("movechar", "[Character ID|Character Name] [Zone ID|Zone Short Name] - Move an offline character to the specified zone", AccountStatus::Guide, command_movechar) || command_add("movement", "Various movement commands", AccountStatus::GMMgmt, command_movement) || command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) || diff --git a/zone/gm_commands/motd.cpp b/zone/gm_commands/motd.cpp index fdfba84b4..55a6d8ccc 100755 --- a/zone/gm_commands/motd.cpp +++ b/zone/gm_commands/motd.cpp @@ -5,11 +5,11 @@ extern WorldServer worldserver; void command_motd(Client *c, const Seperator *sep) { - auto outpack = new ServerPacket(ServerOP_Motd, sizeof(ServerMotd_Struct)); - ServerMotd_Struct *mss = (ServerMotd_Struct *) outpack->pBuffer; - strn0cpy(mss->myname, c->GetName(), 64); - strn0cpy(mss->motd, sep->argplus[1], 512); - worldserver.SendPacket(outpack); - safe_delete(outpack); + auto pack = new ServerPacket(ServerOP_Motd, sizeof(ServerMotd_Struct)); + auto m = (ServerMotd_Struct *) pack->pBuffer; + strn0cpy(m->myname, c->GetName(), sizeof(m->myname)); + strn0cpy(m->motd, sep->argplus[1], sizeof(m->motd)); + worldserver.SendPacket(pack); + safe_delete(pack); } From e69f7a6cf1eab40bbba186d7a02cf75ca4f04892 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 23 May 2022 19:56:30 -0400 Subject: [PATCH 012/552] [Commands] Remove unused/broken #deletegraveyard and #setgraveyard Commands. (#2198) - These commands don't function in their current state, and they probably haven't ever. - Removing the commands and putting this in an editor makes more sense, as #setgraveyard uses the current zone XYZ coordinates of a target to set a graveyard for another zone. and they also only allow version 0 graveyards. - Not sure of a better idea than just deleting, as setting data based on another zone using your current zone's data seems beyond the scope of a command. --- common/database.cpp | 15 ---------- common/database.h | 2 -- zone/command.cpp | 4 --- zone/command.h | 2 -- zone/gm_commands/deletegraveyard.cpp | 35 ---------------------- zone/gm_commands/setgraveyard.cpp | 44 ---------------------------- zone/zonedb.cpp | 33 --------------------- zone/zonedb.h | 4 --- 8 files changed, 139 deletions(-) delete mode 100755 zone/gm_commands/deletegraveyard.cpp delete mode 100755 zone/gm_commands/setgraveyard.cpp diff --git a/common/database.cpp b/common/database.cpp index 4e7cb53d6..93535e584 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1107,21 +1107,6 @@ bool Database::GetZoneLongName(const char* short_name, char** long_name, char* f return true; } -uint32 Database::GetZoneGraveyardID(uint32 zone_id, uint32 version) { - - std::string query = StringFormat("SELECT graveyard_id FROM zone WHERE zoneidnumber='%u' AND (version=%i OR version=0) ORDER BY version DESC", zone_id, version); - auto results = QueryDatabase(query); - - if (!results.Success()) - return 0; - - if (results.RowCount() == 0) - return 0; - - auto row = results.begin(); - return atoi(row[0]); -} - bool Database::GetZoneGraveyard(const uint32 graveyard_id, uint32* graveyard_zoneid, float* graveyard_x, float* graveyard_y, float* graveyard_z, float* graveyard_heading) { std::string query = StringFormat("SELECT zone_id, x, y, z, heading FROM graveyard WHERE id=%i", graveyard_id); diff --git a/common/database.h b/common/database.h index b20a0c3a5..d70960e99 100644 --- a/common/database.h +++ b/common/database.h @@ -251,8 +251,6 @@ public: bool GetZoneLongName(const char* short_name, char** long_name, char* file_name = 0, float* safe_x = 0, float* safe_y = 0, float* safe_z = 0, uint32* graveyard_id = 0, uint32* maxclients = 0); bool LoadPTimers(uint32 charid, PTimerList &into); - uint32 GetZoneGraveyardID(uint32 zone_id, uint32 version); - uint8 GetPEQZone(uint32 zone_id, uint32 version); uint8 GetRaceSkill(uint8 skillid, uint8 in_race); uint8 GetServerType(); diff --git a/zone/command.cpp b/zone/command.cpp index efd259d28..3f25dbd83 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -145,7 +145,6 @@ int command_init(void) command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_date) || command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", AccountStatus::GMAdmin, command_dbspawn2) || command_add("delacct", "[accountname] - Delete an account", AccountStatus::GMLeadAdmin, command_delacct) || - command_add("deletegraveyard", "[zone name] - Deletes the graveyard for the specified zone.", AccountStatus::GMMgmt, command_deletegraveyard) || command_add("delpetition", "[petition number] - Delete a petition", AccountStatus::ApprenticeGuide, command_delpetition) || command_add("depop", "- Depop your NPC target", AccountStatus::Guide, command_depop) || command_add("depopzone", "- Depop the zone", AccountStatus::GMAdmin, command_depopzone) || @@ -308,7 +307,6 @@ int command_init(void) command_add("setcrystals", "[value] - Set your or your player target's available radiant or ebon crystals", AccountStatus::GMAdmin, command_setcrystals) || command_add("setendurance", "[Endurance] - Set your or your target's Endurance", AccountStatus::GMAdmin, command_setendurance) || command_add("setfaction", "[Faction ID] - Sets targeted NPC's faction in the database", AccountStatus::GMAreas, command_setfaction) || - command_add("setgraveyard", "[zone name] - Creates a graveyard for the specified zone based on your target's LOC.", AccountStatus::GMMgmt, command_setgraveyard) || command_add("sethp", "[Health] - Set your or your target's Health", AccountStatus::GMAdmin, command_sethp) || command_add("setlanguage", "[language ID] [value] - Set your target's language skillnum to value", AccountStatus::Guide, command_setlanguage) || command_add("setlsinfo", "[Email] [Password] - Set loginserver email address and password (if supported by loginserver)", AccountStatus::Steward, command_setlsinfo) || @@ -1161,7 +1159,6 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/date.cpp" #include "gm_commands/dbspawn2.cpp" #include "gm_commands/delacct.cpp" -#include "gm_commands/deletegraveyard.cpp" #include "gm_commands/delpetition.cpp" #include "gm_commands/depop.cpp" #include "gm_commands/depopzone.cpp" @@ -1322,7 +1319,6 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/setcrystals.cpp" #include "gm_commands/setendurance.cpp" #include "gm_commands/setfaction.cpp" -#include "gm_commands/setgraveyard.cpp" #include "gm_commands/sethp.cpp" #include "gm_commands/setlanguage.cpp" #include "gm_commands/setlsinfo.cpp" diff --git a/zone/command.h b/zone/command.h index b6d766d70..884a85d65 100644 --- a/zone/command.h +++ b/zone/command.h @@ -54,7 +54,6 @@ void command_databuckets(Client *c, const Seperator *sep); void command_date(Client *c, const Seperator *sep); void command_dbspawn2(Client *c, const Seperator *sep); void command_delacct(Client *c, const Seperator *sep); -void command_deletegraveyard(Client *c, const Seperator *sep); void command_delpetition(Client *c, const Seperator *sep); void command_depop(Client *c, const Seperator *sep); void command_depopzone(Client *c, const Seperator *sep); @@ -229,7 +228,6 @@ void command_setanim(Client *c, const Seperator *sep); void command_setcrystals(Client *c, const Seperator *sep); void command_setendurance(Client *c, const Seperator *sep); void command_setfaction(Client *c, const Seperator *sep); -void command_setgraveyard(Client *c, const Seperator *sep); void command_sethp(Client *c, const Seperator *sep); void command_setlanguage(Client *c, const Seperator *sep); void command_setlsinfo(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/deletegraveyard.cpp b/zone/gm_commands/deletegraveyard.cpp deleted file mode 100755 index 43560eb6f..000000000 --- a/zone/gm_commands/deletegraveyard.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "../client.h" - -void command_deletegraveyard(Client *c, const Seperator *sep) -{ - uint32 zoneid = 0; - uint32 graveyard_id = 0; - - if (!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #deletegraveyard [zonename]"); - return; - } - - zoneid = ZoneID(sep->arg[1]); - graveyard_id = content_db.GetZoneGraveyardID(zoneid, 0); - - if (zoneid > 0 && graveyard_id > 0) { - if (content_db.DeleteGraveyard(zoneid, graveyard_id)) { - c->Message(Chat::White, "Successfuly deleted graveyard %u for zone %s.", graveyard_id, sep->arg[1]); - } - else { - c->Message(Chat::White, "Unable to delete graveyard %u for zone %s.", graveyard_id, sep->arg[1]); - } - } - else { - if (zoneid <= 0) { - c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); - } - else if (graveyard_id <= 0) { - c->Message(Chat::White, "Unable to retrieve a valid GraveyardID for the zone: %s", sep->arg[1]); - } - } - - return; -} - diff --git a/zone/gm_commands/setgraveyard.cpp b/zone/gm_commands/setgraveyard.cpp deleted file mode 100755 index e1b043e4b..000000000 --- a/zone/gm_commands/setgraveyard.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "../client.h" - -void command_setgraveyard(Client *c, const Seperator *sep) -{ - uint32 zoneid = 0; - uint32 graveyard_id = 0; - Client *t = c; - - if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM()) { - t = c->GetTarget()->CastToClient(); - } - - if (!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #setgraveyard [zonename]"); - return; - } - - zoneid = ZoneID(sep->arg[1]); - - if (zoneid > 0) { - graveyard_id = content_db.CreateGraveyardRecord(zoneid, t->GetPosition()); - - if (graveyard_id > 0) { - c->Message(Chat::White, "Successfuly added a new record for this graveyard!"); - if (content_db.AddGraveyardIDToZone(zoneid, graveyard_id) > 0) { - c->Message(Chat::White, "Successfuly added this new graveyard for the zone %s.", sep->arg[1]); - // TODO: Set graveyard data to the running zone process. - c->Message(Chat::White, "Done!"); - } - else { - c->Message(Chat::White, "Unable to add this new graveyard to the zone %s.", sep->arg[1]); - } - } - else { - c->Message(Chat::White, "Unable to create a new graveyard record in the database."); - } - } - else { - c->Message(Chat::White, "Unable to retrieve a ZoneID for the zone: %s", sep->arg[1]); - } - - return; -} - diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index c370e14ce..273898b1b 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4187,39 +4187,6 @@ bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::listrandom.Real(-20,20)); diff --git a/zone/zonedb.h b/zone/zonedb.h index 08ded3155..122005688 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -399,17 +399,13 @@ public: bool BuryAllCharacterCorpses(uint32 charid); bool DeleteCharacterCorpse(uint32 dbid); bool SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, const glm::vec4& position); - bool SummonAllGraveyardCorpses(uint32 cur_zoneid, uint32 dest_zoneid, uint16 dest_instanceid, const glm::vec4& position); int CountCharacterCorpses(uint32 char_id); int CountCharacterCorpsesByZoneID(uint32 char_id, uint32 zone_id); bool UnburyCharacterCorpse(uint32 dbid, uint32 new_zoneid, uint16 dest_instanceid, const glm::vec4& position); bool LoadCharacterCorpses(uint32 iZoneID, uint16 iInstanceID); - bool DeleteGraveyard(uint32 zone_id, uint32 graveyard_id); uint32 GetCharacterCorpseDecayTimer(uint32 corpse_db_id); uint32 GetCharacterBuriedCorpseCount(uint32 char_id); uint32 SendCharacterCorpseToGraveyard(uint32 dbid, uint32 zoneid, uint16 instanceid, const glm::vec4& position); - uint32 CreateGraveyardRecord(uint32 graveyard_zoneid, const glm::vec4& position); - uint32 AddGraveyardIDToZone(uint32 zone_id, uint32 graveyard_id); uint32 SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position, uint32 guildid); uint32 UpdateCharacterCorpse(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, const glm::vec4& position, uint32 guildid, bool rezzed = false); uint32 UpdateCharacterCorpseConsent(uint32 charid, uint32 guildid); From 9ccdb9eb8461412fb1fe136a3d9d1d9ed1aeb8e7 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 23 May 2022 19:56:56 -0400 Subject: [PATCH 013/552] [Bot Commands] Use Account Status Constants. (#2201) - Convert bot_command_add calls to use constants instead of magic numbers. --- zone/bot_command.cpp | 220 +++++++++++++++++++++---------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 5f174320c..43a071ad3 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1309,116 +1309,116 @@ int bot_command_init(void) bot_command_aliases.clear(); if ( - bot_command_add("actionable", "Lists actionable command arguments and use descriptions", 0, bot_command_actionable) || - bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", 0, bot_command_aggressive) || - bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", 0, bot_command_apply_poison) || - bot_command_add("applypotion", "Applies cursor-held potion to a bot's effects", 0, bot_command_apply_potion) || - bot_command_add("attack", "Orders bots to attack a designated target", 0, bot_command_attack) || - bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", 0, bot_command_bind_affinity) || - bot_command_add("bot", "Lists the available bot management [subcommands]", 0, bot_command_bot) || - bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", 0, bot_subcommand_bot_appearance) || - bot_command_add("botbeardcolor", "Changes the beard color of a bot", 0, bot_subcommand_bot_beard_color) || - bot_command_add("botbeardstyle", "Changes the beard style of a bot", 0, bot_subcommand_bot_beard_style) || - bot_command_add("botcamp", "Orders a bot(s) to camp", 0, bot_subcommand_bot_camp) || - bot_command_add("botclone", "Creates a copy of a bot", 200, bot_subcommand_bot_clone) || - bot_command_add("botcreate", "Creates a new bot", 0, bot_subcommand_bot_create) || - bot_command_add("botdelete", "Deletes all record of a bot", 0, bot_subcommand_bot_delete) || - bot_command_add("botdetails", "Changes the Drakkin details of a bot", 0, bot_subcommand_bot_details) || - bot_command_add("botdyearmor", "Changes the color of a bot's (bots') armor", 0, bot_subcommand_bot_dye_armor) || - bot_command_add("boteyes", "Changes the eye colors of a bot", 0, bot_subcommand_bot_eyes) || - bot_command_add("botface", "Changes the facial appearance of your bot", 0, bot_subcommand_bot_face) || - bot_command_add("botfollowdistance", "Changes the follow distance(s) of a bot(s)", 0, bot_subcommand_bot_follow_distance) || - bot_command_add("botgroup", "Lists the available bot-group [subcommands]", 0, bot_command_botgroup) || - bot_command_add("botgroupaddmember", "Adds a member to a bot-group", 0, bot_subcommand_botgroup_add_member) || - bot_command_add("botgroupcreate", "Creates a bot-group and designates a leader", 0, bot_subcommand_botgroup_create) || - bot_command_add("botgroupdelete", "Deletes a bot-group and releases its members", 0, bot_subcommand_botgroup_delete) || - bot_command_add("botgrouplist", "Lists all of your existing bot-groups", 0, bot_subcommand_botgroup_list) || - bot_command_add("botgroupload", "Loads all members of a bot-group", 0, bot_subcommand_botgroup_load) || - bot_command_add("botgroupremovemember", "Removes a bot from its bot-group", 0, bot_subcommand_botgroup_remove_member) || - bot_command_add("bothaircolor", "Changes the hair color of a bot", 0, bot_subcommand_bot_hair_color) || - bot_command_add("bothairstyle", "Changes the hairstyle of a bot", 0, bot_subcommand_bot_hairstyle) || - bot_command_add("botheritage", "Changes the Drakkin heritage of a bot", 0, bot_subcommand_bot_heritage) || - bot_command_add("botinspectmessage", "Changes the inspect message of a bot", 0, bot_subcommand_bot_inspect_message) || - bot_command_add("botlist", "Lists the bots that you own", 0, bot_subcommand_bot_list) || - bot_command_add("botoutofcombat", "Toggles your bot between standard and out-of-combat spell/skill use - if any specialized behaviors exist", 0, bot_subcommand_bot_out_of_combat) || - bot_command_add("botreport", "Orders a bot to report its readiness", 0, bot_subcommand_bot_report) || - bot_command_add("botspawn", "Spawns a created bot", 0, bot_subcommand_bot_spawn) || - bot_command_add("botstance", "Changes the stance of a bot", 0, bot_subcommand_bot_stance) || - bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", 0, bot_subcommand_bot_stop_melee_level) || - bot_command_add("botsuffix", "Sets a bots suffix", 0, bot_subcommand_bot_suffix) || - bot_command_add("botsummon", "Summons bot(s) to your location", 0, bot_subcommand_bot_summon) || - bot_command_add("botsurname", "Sets a bots surname (last name)", 0, bot_subcommand_bot_surname) || - bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", 0, bot_subcommand_bot_tattoo) || - bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", 0, bot_subcommand_bot_toggle_archer) || - bot_command_add("bottogglehelm", "Toggles the helm visibility of a bot between shown and hidden", 0, bot_subcommand_bot_toggle_helm) || - bot_command_add("bottitle", "Sets a bots title", 0, bot_subcommand_bot_title) || - bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", 0, bot_subcommand_bot_update) || - bot_command_add("botwoad", "Changes the Barbarian woad of a bot", 0, bot_subcommand_bot_woad) || - bot_command_add("charm", "Attempts to have a bot charm your target", 0, bot_command_charm) || - bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", 0, bot_subcommand_circle) || - bot_command_add("cure", "Orders a bot to remove any ailments", 0, bot_command_cure) || - bot_command_add("defensive", "Orders a bot to use a defensive discipline", 0, bot_command_defensive) || - bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", 0, bot_command_depart) || - bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", 0, bot_command_escape) || - bot_command_add("findaliases", "Find available aliases for a bot command", 0, bot_command_find_aliases) || - bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", 0, bot_command_follow) || - bot_command_add("guard", "Orders bots to guard their current positions", 0, bot_command_guard) || - bot_command_add("healrotation", "Lists the available bot heal rotation [subcommands]", 0, bot_command_heal_rotation) || - bot_command_add("healrotationadaptivetargeting", "Enables or disables adaptive targeting within the heal rotation instance", 0, bot_subcommand_heal_rotation_adaptive_targeting) || - bot_command_add("healrotationaddmember", "Adds a bot to a heal rotation instance", 0, bot_subcommand_heal_rotation_add_member) || - bot_command_add("healrotationaddtarget", "Adds target to a heal rotation instance", 0, bot_subcommand_heal_rotation_add_target) || - bot_command_add("healrotationadjustcritical", "Adjusts the critial HP limit of the heal rotation instance's Class Armor Type criteria", 0, bot_subcommand_heal_rotation_adjust_critical) || - bot_command_add("healrotationadjustsafe", "Adjusts the safe HP limit of the heal rotation instance's Class Armor Type criteria", 0, bot_subcommand_heal_rotation_adjust_safe) || - bot_command_add("healrotationcastingoverride", "Enables or disables casting overrides within the heal rotation instance", 0, bot_subcommand_heal_rotation_casting_override) || - bot_command_add("healrotationchangeinterval", "Changes casting interval between members within the heal rotation instance", 0, bot_subcommand_heal_rotation_change_interval) || - bot_command_add("healrotationclearhot", "Clears the HOT of a heal rotation instance", 0, bot_subcommand_heal_rotation_clear_hot) || - bot_command_add("healrotationcleartargets", "Removes all targets from a heal rotation instance", 0, bot_subcommand_heal_rotation_clear_targets) || - bot_command_add("healrotationcreate", "Creates a bot heal rotation instance and designates a leader", 0, bot_subcommand_heal_rotation_create) || - bot_command_add("healrotationdelete", "Deletes a bot heal rotation entry by leader", 0, bot_subcommand_heal_rotation_delete) || - bot_command_add("healrotationfastheals", "Enables or disables fast heals within the heal rotation instance", 0, bot_subcommand_heal_rotation_fast_heals) || - bot_command_add("healrotationlist", "Reports heal rotation instance(s) information", 0, bot_subcommand_heal_rotation_list) || - bot_command_add("healrotationremovemember", "Removes a bot from a heal rotation instance", 0, bot_subcommand_heal_rotation_remove_member) || - bot_command_add("healrotationremovetarget", "Removes target from a heal rotations instance", 0, bot_subcommand_heal_rotation_remove_target) || - bot_command_add("healrotationresetlimits", "Resets all Class Armor Type HP limit criteria in a heal rotation to its default value", 0, bot_subcommand_heal_rotation_reset_limits) || - bot_command_add("healrotationsave", "Saves a bot heal rotation entry by leader", 0, bot_subcommand_heal_rotation_save) || - bot_command_add("healrotationsethot", "Sets the HOT in a heal rotation instance", 0, bot_subcommand_heal_rotation_set_hot) || - bot_command_add("healrotationstart", "Starts a heal rotation", 0, bot_subcommand_heal_rotation_start) || - bot_command_add("healrotationstop", "Stops a heal rotation", 0, bot_subcommand_heal_rotation_stop) || - bot_command_add("help", "List available commands and their description - specify partial command as argument to search", 0, bot_command_help) || - bot_command_add("hold", "Prevents a bot from attacking until released", 0, bot_command_hold) || - bot_command_add("identify", "Orders a bot to cast an item identification spell", 0, bot_command_identify) || - bot_command_add("inventory", "Lists the available bot inventory [subcommands]", 0, bot_command_inventory) || - bot_command_add("inventorygive", "Gives the item on your cursor to a bot", 0, bot_subcommand_inventory_give) || - bot_command_add("inventorylist", "Lists all items in a bot's inventory", 0, bot_subcommand_inventory_list) || - bot_command_add("inventoryremove", "Removes an item from a bot's inventory", 0, bot_subcommand_inventory_remove) || - bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", 0, bot_subcommand_inventory_window) || - bot_command_add("invisibility", "Orders a bot to cast a cloak of invisibility, or allow them to be seen", 0, bot_command_invisibility) || - bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", 0, bot_command_item_use) || - bot_command_add("levitation", "Orders a bot to cast a levitation spell", 0, bot_command_levitation) || - bot_command_add("lull", "Orders a bot to cast a pacification spell", 0, bot_command_lull) || - bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", 0, bot_command_mesmerize) || - bot_command_add("movementspeed", "Orders a bot to cast a movement speed enhancement spell", 0, bot_command_movement_speed) || - bot_command_add("owneroption", "Sets options available to bot owners", 0, bot_command_owner_option) || - bot_command_add("pet", "Lists the available bot pet [subcommands]", 0, bot_command_pet) || - bot_command_add("petgetlost", "Orders a bot to remove its summoned pet", 0, bot_subcommand_pet_get_lost) || - bot_command_add("petremove", "Orders a bot to remove its charmed pet", 0, bot_subcommand_pet_remove) || - bot_command_add("petsettype", "Orders a Magician bot to use a specified pet type", 0, bot_subcommand_pet_set_type) || - bot_command_add("picklock", "Orders a capable bot to pick the lock of the closest door", 0, bot_command_pick_lock) || - bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", 0, bot_command_precombat) || - bot_command_add("portal", "Orders a Wizard bot to open a magical doorway to a specified destination", 0, bot_subcommand_portal) || - bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", 0, bot_command_pull) || - bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", 0, bot_command_release) || - bot_command_add("resistance", "Orders a bot to cast a specified resistance buff", 0, bot_command_resistance) || - bot_command_add("resurrect", "Orders a bot to resurrect a player's (players') corpse(s)", 0, bot_command_resurrect) || - bot_command_add("rune", "Orders a bot to cast a rune of protection", 0, bot_command_rune) || - bot_command_add("sendhome", "Orders a bot to open a magical doorway home", 0, bot_command_send_home) || - bot_command_add("size", "Orders a bot to change a player's size", 0, bot_command_size) || - bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", 0, bot_command_summon_corpse) || - bot_command_add("suspend", "Suspends a bot's AI processing until released", 0, bot_command_suspend) || - bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) || - bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) || - bot_command_add("viewcombos", "Views bot race class combinations", 0, bot_command_view_combos) || - bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing) + bot_command_add("actionable", "Lists actionable command arguments and use descriptions", AccountStatus::Player, bot_command_actionable) || + bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", AccountStatus::Player, bot_command_aggressive) || + bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) || + bot_command_add("applypotion", "Applies cursor-held potion to a bot's effects", AccountStatus::Player, bot_command_apply_potion) || + bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) || + bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", AccountStatus::Player, bot_command_bind_affinity) || + bot_command_add("bot", "Lists the available bot management [subcommands]", AccountStatus::Player, bot_command_bot) || + bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", AccountStatus::Player, bot_subcommand_bot_appearance) || + bot_command_add("botbeardcolor", "Changes the beard color of a bot", AccountStatus::Player, bot_subcommand_bot_beard_color) || + bot_command_add("botbeardstyle", "Changes the beard style of a bot", AccountStatus::Player, bot_subcommand_bot_beard_style) || + bot_command_add("botcamp", "Orders a bot(s) to camp", AccountStatus::Player, bot_subcommand_bot_camp) || + bot_command_add("botclone", "Creates a copy of a bot", AccountStatus::GMMgmt, bot_subcommand_bot_clone) || + bot_command_add("botcreate", "Creates a new bot", AccountStatus::Player, bot_subcommand_bot_create) || + bot_command_add("botdelete", "Deletes all record of a bot", AccountStatus::Player, bot_subcommand_bot_delete) || + bot_command_add("botdetails", "Changes the Drakkin details of a bot", AccountStatus::Player, bot_subcommand_bot_details) || + bot_command_add("botdyearmor", "Changes the color of a bot's (bots') armor", AccountStatus::Player, bot_subcommand_bot_dye_armor) || + bot_command_add("boteyes", "Changes the eye colors of a bot", AccountStatus::Player, bot_subcommand_bot_eyes) || + bot_command_add("botface", "Changes the facial appearance of your bot", AccountStatus::Player, bot_subcommand_bot_face) || + bot_command_add("botfollowdistance", "Changes the follow distance(s) of a bot(s)", AccountStatus::Player, bot_subcommand_bot_follow_distance) || + bot_command_add("botgroup", "Lists the available bot-group [subcommands]", AccountStatus::Player, bot_command_botgroup) || + bot_command_add("botgroupaddmember", "Adds a member to a bot-group", AccountStatus::Player, bot_subcommand_botgroup_add_member) || + bot_command_add("botgroupcreate", "Creates a bot-group and designates a leader", AccountStatus::Player, bot_subcommand_botgroup_create) || + bot_command_add("botgroupdelete", "Deletes a bot-group and releases its members", AccountStatus::Player, bot_subcommand_botgroup_delete) || + bot_command_add("botgrouplist", "Lists all of your existing bot-groups", AccountStatus::Player, bot_subcommand_botgroup_list) || + bot_command_add("botgroupload", "Loads all members of a bot-group", AccountStatus::Player, bot_subcommand_botgroup_load) || + bot_command_add("botgroupremovemember", "Removes a bot from its bot-group", AccountStatus::Player, bot_subcommand_botgroup_remove_member) || + bot_command_add("bothaircolor", "Changes the hair color of a bot", AccountStatus::Player, bot_subcommand_bot_hair_color) || + bot_command_add("bothairstyle", "Changes the hairstyle of a bot", AccountStatus::Player, bot_subcommand_bot_hairstyle) || + bot_command_add("botheritage", "Changes the Drakkin heritage of a bot", AccountStatus::Player, bot_subcommand_bot_heritage) || + bot_command_add("botinspectmessage", "Changes the inspect message of a bot", AccountStatus::Player, bot_subcommand_bot_inspect_message) || + bot_command_add("botlist", "Lists the bots that you own", AccountStatus::Player, bot_subcommand_bot_list) || + bot_command_add("botoutofcombat", "Toggles your bot between standard and out-of-combat spell/skill use - if any specialized behaviors exist", AccountStatus::Player, bot_subcommand_bot_out_of_combat) || + bot_command_add("botreport", "Orders a bot to report its readiness", AccountStatus::Player, bot_subcommand_bot_report) || + bot_command_add("botspawn", "Spawns a created bot", AccountStatus::Player, bot_subcommand_bot_spawn) || + bot_command_add("botstance", "Changes the stance of a bot", AccountStatus::Player, bot_subcommand_bot_stance) || + bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", AccountStatus::Player, bot_subcommand_bot_stop_melee_level) || + bot_command_add("botsuffix", "Sets a bots suffix", AccountStatus::Player, bot_subcommand_bot_suffix) || + bot_command_add("botsummon", "Summons bot(s) to your location", AccountStatus::Player, bot_subcommand_bot_summon) || + bot_command_add("botsurname", "Sets a bots surname (last name)", AccountStatus::Player, bot_subcommand_bot_surname) || + bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", AccountStatus::Player, bot_subcommand_bot_tattoo) || + bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", AccountStatus::Player, bot_subcommand_bot_toggle_archer) || + bot_command_add("bottogglehelm", "Toggles the helm visibility of a bot between shown and hidden", AccountStatus::Player, bot_subcommand_bot_toggle_helm) || + bot_command_add("bottitle", "Sets a bots title", AccountStatus::Player, bot_subcommand_bot_title) || + bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_subcommand_bot_update) || + bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_subcommand_bot_woad) || + bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || + bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_subcommand_circle) || + bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) || + bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || + bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || + bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", AccountStatus::Player, bot_command_escape) || + bot_command_add("findaliases", "Find available aliases for a bot command", AccountStatus::Player, bot_command_find_aliases) || + bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", AccountStatus::Player, bot_command_follow) || + bot_command_add("guard", "Orders bots to guard their current positions", AccountStatus::Player, bot_command_guard) || + bot_command_add("healrotation", "Lists the available bot heal rotation [subcommands]", AccountStatus::Player, bot_command_heal_rotation) || + bot_command_add("healrotationadaptivetargeting", "Enables or disables adaptive targeting within the heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_adaptive_targeting) || + bot_command_add("healrotationaddmember", "Adds a bot to a heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_add_member) || + bot_command_add("healrotationaddtarget", "Adds target to a heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_add_target) || + bot_command_add("healrotationadjustcritical", "Adjusts the critial HP limit of the heal rotation instance's Class Armor Type criteria", AccountStatus::Player, bot_subcommand_heal_rotation_adjust_critical) || + bot_command_add("healrotationadjustsafe", "Adjusts the safe HP limit of the heal rotation instance's Class Armor Type criteria", AccountStatus::Player, bot_subcommand_heal_rotation_adjust_safe) || + bot_command_add("healrotationcastingoverride", "Enables or disables casting overrides within the heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_casting_override) || + bot_command_add("healrotationchangeinterval", "Changes casting interval between members within the heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_change_interval) || + bot_command_add("healrotationclearhot", "Clears the HOT of a heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_clear_hot) || + bot_command_add("healrotationcleartargets", "Removes all targets from a heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_clear_targets) || + bot_command_add("healrotationcreate", "Creates a bot heal rotation instance and designates a leader", AccountStatus::Player, bot_subcommand_heal_rotation_create) || + bot_command_add("healrotationdelete", "Deletes a bot heal rotation entry by leader", AccountStatus::Player, bot_subcommand_heal_rotation_delete) || + bot_command_add("healrotationfastheals", "Enables or disables fast heals within the heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_fast_heals) || + bot_command_add("healrotationlist", "Reports heal rotation instance(s) information", AccountStatus::Player, bot_subcommand_heal_rotation_list) || + bot_command_add("healrotationremovemember", "Removes a bot from a heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_remove_member) || + bot_command_add("healrotationremovetarget", "Removes target from a heal rotations instance", AccountStatus::Player, bot_subcommand_heal_rotation_remove_target) || + bot_command_add("healrotationresetlimits", "Resets all Class Armor Type HP limit criteria in a heal rotation to its default value", AccountStatus::Player, bot_subcommand_heal_rotation_reset_limits) || + bot_command_add("healrotationsave", "Saves a bot heal rotation entry by leader", AccountStatus::Player, bot_subcommand_heal_rotation_save) || + bot_command_add("healrotationsethot", "Sets the HOT in a heal rotation instance", AccountStatus::Player, bot_subcommand_heal_rotation_set_hot) || + bot_command_add("healrotationstart", "Starts a heal rotation", AccountStatus::Player, bot_subcommand_heal_rotation_start) || + bot_command_add("healrotationstop", "Stops a heal rotation", AccountStatus::Player, bot_subcommand_heal_rotation_stop) || + bot_command_add("help", "List available commands and their description - specify partial command as argument to search", AccountStatus::Player, bot_command_help) || + bot_command_add("hold", "Prevents a bot from attacking until released", AccountStatus::Player, bot_command_hold) || + bot_command_add("identify", "Orders a bot to cast an item identification spell", AccountStatus::Player, bot_command_identify) || + bot_command_add("inventory", "Lists the available bot inventory [subcommands]", AccountStatus::Player, bot_command_inventory) || + bot_command_add("inventorygive", "Gives the item on your cursor to a bot", AccountStatus::Player, bot_subcommand_inventory_give) || + bot_command_add("inventorylist", "Lists all items in a bot's inventory", AccountStatus::Player, bot_subcommand_inventory_list) || + bot_command_add("inventoryremove", "Removes an item from a bot's inventory", AccountStatus::Player, bot_subcommand_inventory_remove) || + bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", AccountStatus::Player, bot_subcommand_inventory_window) || + bot_command_add("invisibility", "Orders a bot to cast a cloak of invisibility, or allow them to be seen", AccountStatus::Player, bot_command_invisibility) || + bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", AccountStatus::Player, bot_command_item_use) || + bot_command_add("levitation", "Orders a bot to cast a levitation spell", AccountStatus::Player, bot_command_levitation) || + bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || + bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", AccountStatus::Player, bot_command_mesmerize) || + bot_command_add("movementspeed", "Orders a bot to cast a movement speed enhancement spell", AccountStatus::Player, bot_command_movement_speed) || + bot_command_add("owneroption", "Sets options available to bot owners", AccountStatus::Player, bot_command_owner_option) || + bot_command_add("pet", "Lists the available bot pet [subcommands]", AccountStatus::Player, bot_command_pet) || + bot_command_add("petgetlost", "Orders a bot to remove its summoned pet", AccountStatus::Player, bot_subcommand_pet_get_lost) || + bot_command_add("petremove", "Orders a bot to remove its charmed pet", AccountStatus::Player, bot_subcommand_pet_remove) || + bot_command_add("petsettype", "Orders a Magician bot to use a specified pet type", AccountStatus::Player, bot_subcommand_pet_set_type) || + bot_command_add("picklock", "Orders a capable bot to pick the lock of the closest door", AccountStatus::Player, bot_command_pick_lock) || + bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || + bot_command_add("portal", "Orders a Wizard bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_subcommand_portal) || + bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || + bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || + bot_command_add("resistance", "Orders a bot to cast a specified resistance buff", AccountStatus::Player, bot_command_resistance) || + bot_command_add("resurrect", "Orders a bot to resurrect a player's (players') corpse(s)", AccountStatus::Player, bot_command_resurrect) || + bot_command_add("rune", "Orders a bot to cast a rune of protection", AccountStatus::Player, bot_command_rune) || + bot_command_add("sendhome", "Orders a bot to open a magical doorway home", AccountStatus::Player, bot_command_send_home) || + bot_command_add("size", "Orders a bot to change a player's size", AccountStatus::Player, bot_command_size) || + bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) || + bot_command_add("suspend", "Suspends a bot's AI processing until released", AccountStatus::Player, bot_command_suspend) || + bot_command_add("taunt", "Toggles taunt use by a bot", AccountStatus::Player, bot_command_taunt) || + bot_command_add("track", "Orders a capable bot to track enemies", AccountStatus::Player, bot_command_track) || + bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) || + bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing) ) { bot_command_deinit(); return -1; From 1de0c2762961a7f5ca48b3a858edfbb0755c6056 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 25 May 2022 13:08:28 -0400 Subject: [PATCH 014/552] [Bug Fix] Fix HP Regen Per Second. (#2206) `hp_regen_rate` was being used for `hp_regen_per_second` incorrectly. --- zone/zonedb.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 273898b1b..c87dc1e5f 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2560,7 +2560,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load t->always_aggro = n.always_aggro != 0; t->exp_mod = n.exp_mod; t->skip_auto_scale = false; // hardcoded here for now - t->hp_regen_per_second = n.hp_regen_rate; + t->hp_regen_per_second = n.hp_regen_per_second; // If NPC with duplicate NPC id already in table, // free item we attempted to add. From e2708af6f2b0e5ddc3b2506f79432fe1960800e4 Mon Sep 17 00:00:00 2001 From: Chris Date: Wed, 25 May 2022 17:53:40 -0400 Subject: [PATCH 015/552] [Bug Fix] Blocked spells max spell id increased (#2207) https://github.com/EQEmu/Server/pull/2073 broke blocking spells. There is another location in mob.h that needs to be updated to int32. Tested as fixed on my server. --- zone/mob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 9ad8c7ab0..67aaa734a 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1239,8 +1239,8 @@ public: inline uint8 GetManaPercent() { return (uint8)((float)current_mana / (float)max_mana * 100.0f); } virtual uint8 GetEndurancePercent() { return 0; } - inline virtual bool IsBlockedBuff(int16 SpellID) { return false; } - inline virtual bool IsBlockedPetBuff(int16 SpellID) { return false; } + inline virtual bool IsBlockedBuff(int32 SpellID) { return false; } + inline virtual bool IsBlockedPetBuff(int32 SpellID) { return false; } std::string GetGlobal(const char *varname); void SetGlobal(const char *varname, const char *newvalue, int options, const char *duration, Mob *other = nullptr); From 6636c64c82a849589115309bebe320db3dbd80aa Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 25 May 2022 20:05:07 -0400 Subject: [PATCH 016/552] [Commands] Fix typos in #ban and #ipban Commands. (#2209) --- zone/gm_commands/ban.cpp | 13 ++++++------- zone/gm_commands/ipban.cpp | 6 ++++-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/zone/gm_commands/ban.cpp b/zone/gm_commands/ban.cpp index ab5131a32..bbe38d77c 100755 --- a/zone/gm_commands/ban.cpp +++ b/zone/gm_commands/ban.cpp @@ -28,9 +28,9 @@ void command_ban(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Character {} does not exist." - ).c_str(), - character_name + "Character {} does not exist.", + character_name + ).c_str() ); return; } @@ -53,20 +53,19 @@ void command_ban(Client *c, const Seperator *sep) ); ServerPacket flagUpdatePack(ServerOP_FlagUpdate, sizeof(ServerFlagUpdate_Struct)); - ServerFlagUpdate_Struct *sfus = (ServerFlagUpdate_Struct *) flagUpdatePack.pBuffer; + auto sfus = (ServerFlagUpdate_Struct *) flagUpdatePack.pBuffer; sfus->account_id = account_id; sfus->admin = -2; worldserver.SendPacket(&flagUpdatePack); - Client *client = nullptr; - client = entity_list.GetClientByName(character_name.c_str()); + auto client = entity_list.GetClientByName(character_name.c_str()); if (client) { client->WorldKick(); return; } ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct *skp = (ServerKickPlayer_Struct *) kickPlayerPack.pBuffer; + auto skp = (ServerKickPlayer_Struct *) kickPlayerPack.pBuffer; strcpy(skp->adminname, c->GetName()); strcpy(skp->name, character_name.c_str()); skp->adminrank = c->Admin(); diff --git a/zone/gm_commands/ipban.cpp b/zone/gm_commands/ipban.cpp index 40307539b..40c9ed500 100755 --- a/zone/gm_commands/ipban.cpp +++ b/zone/gm_commands/ipban.cpp @@ -25,8 +25,10 @@ void command_ipban(Client *c, const Seperator *sep) } else { c->Message( Chat::White, - "IP '{}' has failed to be banned, the IP address may already be in the table.", - ip + fmt::format( + "IP '{}' has failed to be banned, the IP address may already be in the table.", + ip + ).c_str() ); } } From 7f12ad325aba141f721d89f5908bd7c0d663950a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Thu, 26 May 2022 14:10:19 -0400 Subject: [PATCH 017/552] [Bug Fix] Fix bot compile locking client on server enter. (#2210) --- zone/bot_command.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 43a071ad3..161ede09b 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -56,6 +56,7 @@ #include "../common/string_util.h" #include "../common/say_link.h" #include "../common/eqemu_logsys.h" +#include "../common/emu_constants.h" #include "bot_command.h" From 25addc3bd9ef2aae19b1dbe6f3190f9e2c2936ef Mon Sep 17 00:00:00 2001 From: titanium-forever <95503076+titanium-forever@users.noreply.github.com> Date: Fri, 27 May 2022 06:22:31 +0100 Subject: [PATCH 018/552] Create user directory during account creation to ensure default files are copied to profile from /etc/skel (#2176) Co-authored-by: Kieren Hinch --- utils/scripts/linux_installer/install.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/utils/scripts/linux_installer/install.sh b/utils/scripts/linux_installer/install.sh index 5cef4ab8b..49397e564 100644 --- a/utils/scripts/linux_installer/install.sh +++ b/utils/scripts/linux_installer/install.sh @@ -64,11 +64,10 @@ if [ ! -f ./install_variables.txt ]; then echo "" echo "" groupadd eqemu - useradd -g eqemu -d $eqemu_server_directory eqemu + useradd -g eqemu -m -d $eqemu_server_directory eqemu passwd eqemu - #::: Make server directory and go to it - mkdir $eqemu_server_directory + #::: Go to server directory cd $eqemu_server_directory #::: Setup MySQL root user PW From 14f48fcc9336406a0673578c8f5433e278c7cac5 Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 27 May 2022 09:37:55 -0400 Subject: [PATCH 019/552] [Aggro] Rooted mobs will add other hated targets to Hate list (#2180) --- zone/aggro.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 4d020ce3d..0de43c3ca 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -456,7 +456,12 @@ bool Mob::CheckWillAggro(Mob *mob) { } // Don't aggro new clients if we are already engaged unless PROX_AGGRO is set - if (IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { + + // Frustrated mobs (all rooted up with no one to kill) + // will engage without PROX_AGGRO ability if someone new is close now. + bool is_frustrated = (IsRooted() && !CombatRange(target)); + + if (!is_frustrated && IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { LogAggro( "[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]", GetName(), From 49d751b3d5e14077dba4e349af46b222cdac1b59 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" <277429+mackal@users.noreply.github.com> Date: Fri, 27 May 2022 11:40:43 -0400 Subject: [PATCH 020/552] Revert "[Aggro] Rooted mobs will add other hated targets to Hate list (#2180)" (#2214) This reverts commit 14f48fcc9336406a0673578c8f5433e278c7cac5. --- zone/aggro.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 0de43c3ca..4d020ce3d 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -456,12 +456,7 @@ bool Mob::CheckWillAggro(Mob *mob) { } // Don't aggro new clients if we are already engaged unless PROX_AGGRO is set - - // Frustrated mobs (all rooted up with no one to kill) - // will engage without PROX_AGGRO ability if someone new is close now. - bool is_frustrated = (IsRooted() && !CombatRange(target)); - - if (!is_frustrated && IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { + if (IsEngaged() && (!GetSpecialAbility(PROX_AGGRO) || (GetSpecialAbility(PROX_AGGRO) && !CombatRange(mob)))) { LogAggro( "[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]", GetName(), From f9191d4ef4b7c3b019a69ef498839b0c6e01e99a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 14:38:40 -0400 Subject: [PATCH 021/552] [Commands] Cleanup #oocmute Command. (#2191) - Cleanup messages and logic. - Add ServerOOCMute_Struct for cleanliness. --- common/servertalk.h | 4 ++++ zone/command.cpp | 2 +- zone/gm_commands/oocmute.cpp | 26 ++++++++++++++++++-------- zone/worldserver.cpp | 3 ++- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/common/servertalk.h b/common/servertalk.h index b780175cb..092220e6b 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -1788,6 +1788,10 @@ struct ServerFlagUpdate_Struct { int16 admin; }; +struct ServerOOCMute_Struct { + bool is_muted; +}; + #pragma pack() #endif diff --git a/zone/command.cpp b/zone/command.cpp index 3f25dbd83..3a92f24d9 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -259,7 +259,7 @@ int command_init(void) command_add("nukebuffs", "[Beneficial|Detrimental|Help] - Strip all buffs by type on you or your target (no argument to remove all buffs)", AccountStatus::Guide, command_nukebuffs) || command_add("nukeitem", "[Item ID] - Removes the specified Item ID from you or your player target's inventory", AccountStatus::GMLeadAdmin, command_nukeitem) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || - command_add("oocmute", "[1/0] - Mutes OOC chat", AccountStatus::GMMgmt, command_oocmute) || + command_add("oocmute", "[0|1] - Enable or Disable Server OOC", AccountStatus::GMMgmt, command_oocmute) || command_add("opcode", "- opcode management", AccountStatus::GMImpossible, command_opcode) || command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, command_path) || command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || diff --git a/zone/gm_commands/oocmute.cpp b/zone/gm_commands/oocmute.cpp index a1e1199a2..a19f424ec 100755 --- a/zone/gm_commands/oocmute.cpp +++ b/zone/gm_commands/oocmute.cpp @@ -5,14 +5,24 @@ extern WorldServer worldserver; void command_oocmute(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0 || !(sep->arg[1][0] == '1' || sep->arg[1][0] == '0')) { - c->Message(Chat::White, "Usage: #oocmute [1/0]"); - } - else { - auto outapp = new ServerPacket(ServerOP_OOCMute, 1); - *(outapp->pBuffer) = atoi(sep->arg[1]); - worldserver.SendPacket(outapp); - safe_delete(outapp); + if (!sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #oocmute [0|1] - Enable or Disable Server OOC"); + return; } + + bool is_muted = std::stoi(sep->arg[1]) ? true : false; + + ServerPacket pack(ServerOP_OOCMute, sizeof(ServerOOCMute_Struct)); + auto o = (ServerOOCMute_Struct*) pack.pBuffer; + o->is_muted = is_muted; + worldserver.SendPacket(&pack); + + c->Message( + Chat::White, + fmt::format( + "Server OOC is {} muted.", + is_muted ? "now" : "no longer" + ).c_str() + ); } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 4820b791a..dfd4166ae 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -958,7 +958,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) break; } case ServerOP_OOCMute: { - oocmuted = *(pack->pBuffer); + auto o = (ServerOOCMute_Struct *) pack->pBuffer; + oocmuted = o->is_muted; break; } case ServerOP_Revoke: { From 5bc4cff7a904ba169d8a30c842f9b267dd95769a Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 14:39:25 -0400 Subject: [PATCH 022/552] [Regen] Fix possible overflow in CalcHPRegenCap(). (#2185) --- zone/client_mods.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index b5186c165..1394d7695 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -303,12 +303,17 @@ int64 Client::CalcHPRegen(bool bCombat) int64 Client::CalcHPRegenCap() { - int cap = RuleI(Character, ItemHealthRegenCap); - if (GetLevel() > 60) - cap = std::max(cap, GetLevel() - 30); // if the rule is set greater than normal I guess - if (GetLevel() > 65) + int64 cap = RuleI(Character, ItemHealthRegenCap); + if (GetLevel() > 60) { + cap = std::max(cap, static_cast(GetLevel() - 30)); // if the rule is set greater than normal I guess + } + + if (GetLevel() > 65) { cap += GetLevel() - 65; + } + cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap; + return (cap * RuleI(Character, HPRegenMultiplier) / 100); } From 0de90dc135350d5ee32d35f4dd4d696084afe1e0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 14:39:35 -0400 Subject: [PATCH 023/552] [Rules] Add Spells:IllusionsAlwaysPersist. (#2199) - Allows illusions to always persist beyond death or zoning. --- common/ruletypes.h | 1 + zone/spell_effects.cpp | 28 +++++++++++++++++++++++----- zone/spells.cpp | 15 ++++++++++++--- 3 files changed, 36 insertions(+), 8 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 159eedd5d..badbdae5e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -414,6 +414,7 @@ RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate dam RULE_BOOL(Spells, UseFadingMemoriesMaxLevel, false, "Enables to limit field in spell data to set the max level that over which an NPC will ignore fading memories effect and not lose aggro.") RULE_BOOL(Spells, FixBeaconHeading, false, "Beacon spells use casters heading to fix live bug. False: Live like heading always 0.") RULE_BOOL(Spells, UseSpellImpliedTargeting, false, "Replicates EQ2-style targeting behavior for spells. Spells will 'pass through' inappropriate targets to target's target if it is appropriate.") +RULE_BOOL(Spells, IllusionsAlwaysPersist, false, "Allows Illusions to persist beyond death and zoning always.") RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b29d48206..2230a8147 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -10214,8 +10214,17 @@ void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, in } if (buffslot != -1) { - if (caster == this && spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && - (spellbonuses.IllusionPersistence || aabonuses.IllusionPersistence || itembonuses.IllusionPersistence)) { + if ( + caster == this && + spell_id != SPELL_MINOR_ILLUSION && + spell_id != SPELL_ILLUSION_TREE && + ( + spellbonuses.IllusionPersistence || + aabonuses.IllusionPersistence || + itembonuses.IllusionPersistence || + RuleB(Spells, IllusionsAlwaysPersist) + ) + ) { buffs[buffslot].persistant_buff = 1; } else { @@ -10225,9 +10234,18 @@ void Mob::ApplySpellEffectIllusion(int32 spell_id, Mob *caster, int buffslot, in } bool Mob::HasPersistDeathIllusion(int32 spell_id) { - - if (spellbonuses.IllusionPersistence > 1 || aabonuses.IllusionPersistence > 1 || itembonuses.IllusionPersistence > 1) { - if (spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && IsEffectInSpell(spell_id, SE_Illusion) && IsBeneficialSpell(spell_id)) { + if ( + spellbonuses.IllusionPersistence > 1 || + aabonuses.IllusionPersistence > 1 || + itembonuses.IllusionPersistence > 1 || + RuleB(Spells, IllusionsAlwaysPersist) + ) { + if ( + spell_id != SPELL_MINOR_ILLUSION && + spell_id != SPELL_ILLUSION_TREE && + IsEffectInSpell(spell_id, SE_Illusion) && + IsBeneficialSpell(spell_id) + ) { return true; } } diff --git a/zone/spells.cpp b/zone/spells.cpp index dfa5c7eb8..6a448679d 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2800,9 +2800,18 @@ int Mob::CalcBuffDuration(Mob *caster, Mob *target, uint16 spell_id, int32 caste castlevel = caster_level_override; int res = CalcBuffDuration_formula(castlevel, formula, duration); - if (caster == target && (target->aabonuses.IllusionPersistence || target->spellbonuses.IllusionPersistence || - target->itembonuses.IllusionPersistence) && - spell_id != SPELL_MINOR_ILLUSION && spell_id != SPELL_ILLUSION_TREE && IsEffectInSpell(spell_id, SE_Illusion)) { + if ( + caster == target && + ( + target->aabonuses.IllusionPersistence || + target->spellbonuses.IllusionPersistence || + target->itembonuses.IllusionPersistence || + RuleB(Spells, IllusionsAlwaysPersist) + ) && + spell_id != SPELL_MINOR_ILLUSION && + spell_id != SPELL_ILLUSION_TREE && + IsEffectInSpell(spell_id, SE_Illusion) + ) { res = 10000; // ~16h override } From 129a73807255ae64f4d272d6b33cfa8e0dc47576 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 14:45:26 -0400 Subject: [PATCH 024/552] [Commands] Cleanup #level Command. (#2203) - Cleanup messages and logic. - Breakout #level into its own command file. --- zone/command.cpp | 37 ++------------------------- zone/gm_commands/level.cpp | 51 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 35 deletions(-) create mode 100644 zone/gm_commands/level.cpp diff --git a/zone/command.cpp b/zone/command.cpp index 3a92f24d9..f35a3591c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -223,7 +223,7 @@ int command_init(void) command_add("kill", "- Kill your target", AccountStatus::GMAdmin, command_kill) || command_add("killallnpcs", " [npc_name] Kills all npcs by search name, leave blank for all attackable NPC's", AccountStatus::GMMgmt, command_killallnpcs) || command_add("lastname", "[Last Name] - Set you or your player target's lastname", AccountStatus::Guide, command_lastname) || - command_add("level", "[level] - Set your or your target's level", AccountStatus::Steward, command_level) || + command_add("level", "[Level] - Set your target's level", AccountStatus::Steward, command_level) || command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", AccountStatus::ApprenticeGuide, command_list) || command_add("listpetition", "- List petitions", AccountStatus::Guide, command_listpetition) || command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", AccountStatus::GMImpossible, command_load_shared_memory) || @@ -746,40 +746,6 @@ void command_zone_instance(Client *c, const Seperator *sep) } } -void command_level(Client *c, const Seperator *sep) -{ - uint16 level = atoi(sep->arg[1]); - - if ((level <= 0) || ((level > RuleI(Character, MaxLevel)) && (c->Admin() < commandLevelAboveCap))) { - c->Message(Chat::White, "Error: #Level: Invalid Level"); - } - else if (c->Admin() < RuleI(GM, MinStatusToLevelTarget)) { - c->SetLevel(level, true); -#ifdef BOTS - if(RuleB(Bots, BotLevelsWithOwner)) - Bot::LevelBotWithClient(c, level, true); -#endif - } - else if (!c->GetTarget()) { - c->Message(Chat::White, "Error: #Level: No target"); - } - else { - if (!c->GetTarget()->IsNPC() && ((c->Admin() < commandLevelNPCAboveCap) && (level > RuleI(Character, MaxLevel)))) { - c->Message(Chat::White, "Error: #Level: Invalid Level"); - } - else { - c->GetTarget()->SetLevel(level, true); - if(c->GetTarget()->IsClient()) { - c->GetTarget()->CastToClient()->SendLevelAppearance(); -#ifdef BOTS - if(RuleB(Bots, BotLevelsWithOwner)) - Bot::LevelBotWithClient(c->GetTarget()->CastToClient(), level, true); -#endif - } - } - } -} - void command_spawneditmass(Client *c, const Seperator *sep) { std::string query = fmt::format( @@ -1235,6 +1201,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/kill.cpp" #include "gm_commands/killallnpcs.cpp" #include "gm_commands/lastname.cpp" +#include "gm_commands/level.cpp" #include "gm_commands/list.cpp" #include "gm_commands/listpetition.cpp" #include "gm_commands/loc.cpp" diff --git a/zone/gm_commands/level.cpp b/zone/gm_commands/level.cpp new file mode 100644 index 000000000..77203e6a2 --- /dev/null +++ b/zone/gm_commands/level.cpp @@ -0,0 +1,51 @@ +#include "../client.h" + +void command_level(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #level [Level]"); + return; + } + + auto target = c->GetTarget(); + if (!target) { + c->Message(Chat::White, "You must have a target to use this command."); + return; + } + + auto level = static_cast(std::stoul(sep->arg[1])); + auto max_level = static_cast(RuleI(Character, MaxLevel)); + + if (c->Admin() < RuleI(GM, MinStatusToLevelTarget)) { + c->Message(Chat::White, "Your status is not high enough to change another person's level."); + return; + } + + if ( + level > max_level && + c->Admin() < commandLevelAboveCap + ) { + c->Message( + Chat::White, + fmt::format( + "Level {} is above the Maximum Level of {} and your status is not high enough to go beyond the cap.", + level, + max_level + ).c_str() + ); + return; + } + + target->SetLevel(level, true); + if (target->IsClient()) { + target->CastToClient()->SendLevelAppearance(); + +#ifdef BOTS + if (RuleB(Bots, BotLevelsWithOwner)) { + Bot::LevelBotWithClient(target->CastToClient(), level, true); + } +#endif + + } +} \ No newline at end of file From e6db71e31e07299c0930463ef6afd58f9f8af23c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 14:46:26 -0400 Subject: [PATCH 025/552] [Commands] Cleanup #corpsefix Command. (#2197) * [Commands] Cleanup #corpsefix Command. - Cleanup messages and logic. * Update entity.cpp --- zone/entity.cpp | 42 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index 1f786cb4c..918b1ec2a 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -3308,18 +3308,44 @@ uint32 EntityList::DeleteNPCCorpses() void EntityList::CorpseFix(Client* c) { + uint32 fixed_count = 0; + for (const auto& e : corpse_list) { + auto cur = e.second; + if (cur->IsNPCCorpse()) { + if (DistanceNoZ(c->GetPosition(), cur->GetPosition()) < 100) { + c->Message( + Chat::White, + fmt::format( + "Attempting to fix {}.", + cur->GetCleanName() + ).c_str() + ); - auto it = corpse_list.begin(); - while (it != corpse_list.end()) { - Corpse* corpse = it->second; - if (corpse->IsNPCCorpse()) { - if (DistanceNoZ(c->GetPosition(), corpse->GetPosition()) < 100) { - c->Message(Chat::Yellow, "Attempting to fix %s", it->second->GetCleanName()); - corpse->GMMove(corpse->GetX(), corpse->GetY(), c->GetZ() + 2, 0); + cur->GMMove( + cur->GetX(), + cur->GetY(), + cur->GetFixedZ(c->GetPosition()), + c->GetHeading() + ); + + fixed_count++; } } - ++it; } + + if (!fixed_count) { + c->Message(Chat::White, "There were no nearby NPC corpses to fix."); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Fixed {} nearby NPC corpse{}.", + fixed_count, + fixed_count != 1 ? "s" : "" + ).c_str() + ); } // returns the number of corpses deleted. A negative number indicates an error code. From 123bc5f19ab28426bb543e9df3b723688a2ec567 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 14:46:53 -0400 Subject: [PATCH 026/552] [Commands] Cleanup #findclass and #findrace Commands. (#2211) - Cleanup messages and logic. - Add bitmasks to player race messages. --- zone/gm_commands/findclass.cpp | 72 ++++++++++++++++------------- zone/gm_commands/findrace.cpp | 82 +++++++++++++++++++--------------- zone/mob.cpp | 10 +++++ zone/mob.h | 1 + 4 files changed, 97 insertions(+), 68 deletions(-) diff --git a/zone/gm_commands/findclass.cpp b/zone/gm_commands/findclass.cpp index a220f5ded..01456db57 100755 --- a/zone/gm_commands/findclass.cpp +++ b/zone/gm_commands/findclass.cpp @@ -3,9 +3,8 @@ void command_findclass(Client *c, const Seperator *sep) { int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findclass [search criteria]"); + if (!arguments) { + c->Message(Chat::White, "Command Syntax: #findclass [Search Criteria]"); return; } @@ -16,9 +15,17 @@ void command_findclass(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Class {}: {}", + "Class {} | {}{}", class_id, - class_name + class_name, + ( + c->IsPlayerClass(class_id) ? + fmt::format( + " ({})", + GetPlayerClassBit(class_id) + ) : + "" + ) ).c_str() ); } @@ -31,51 +38,52 @@ void command_findclass(Client *c, const Seperator *sep) ).c_str() ); } - } - else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { - std::string class_name = GetClassIDName(class_id); - std::string class_name_lower = str_tolower(class_name); - if (search_criteria.length() > 0 && class_name_lower.find(search_criteria) == std::string::npos) { + } else { + auto search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (uint16 class_id = WARRIOR; class_id <= MERCERNARY_MASTER; class_id++) { + std::string class_name = GetClassIDName(class_id); + auto class_name_lower = str_tolower(class_name); + if ( + search_criteria.length() && + class_name_lower.find(search_criteria) == std::string::npos + ) { continue; } c->Message( Chat::White, fmt::format( - "Class {}: {}", + "Class {} | {}{}", class_id, - class_name + class_name, + ( + c->IsPlayerClass(class_id) ? + fmt::format( + " ({})", + GetPlayerClassBit(class_id) + ) : + "" + ) ).c_str() ); + found_count++; - if (found_count == 20) { + if (found_count == 50) { break; } } - if (found_count == 20) { - c->Message(Chat::White, "20 Classes found... max reached."); - } - else { - auto class_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Class was" : - fmt::format("{} Classes were", found_count) - ) : - "No Classes were" - ); - + if (found_count == 50) { + c->Message(Chat::White, "50 Classes found, max reached."); + } else { c->Message( Chat::White, fmt::format( - "{} found.", - class_message + "{} Class{} found.", + found_count, + found_count != 1 ? "es" : "" ).c_str() ); } diff --git a/zone/gm_commands/findrace.cpp b/zone/gm_commands/findrace.cpp index 413687160..06b5ccc56 100755 --- a/zone/gm_commands/findrace.cpp +++ b/zone/gm_commands/findrace.cpp @@ -3,26 +3,35 @@ void command_findrace(Client *c, const Seperator *sep) { int arguments = sep->argnum; - - if (arguments == 0) { - c->Message(Chat::White, "Command Syntax: #findrace [search criteria]"); + if (!arguments) { + c->Message(Chat::White, "Command Syntax: #findrace [Search Criteria]"); return; } if (sep->IsNumber(1)) { - int race_id = std::stoi(sep->arg[1]); + auto race_id = static_cast(std::stoul(sep->arg[1])); std::string race_name = GetRaceIDName(race_id); - if (race_id >= RACE_HUMAN_1 && race_id <= RACE_PEGASUS_732) { + if ( + race_id >= RACE_HUMAN_1 && + race_id <= RACE_PEGASUS_732 + ) { c->Message( Chat::White, fmt::format( - "Race {}: {}", + "Race {} | {}{}", race_id, - race_name + race_name, + ( + c->IsPlayerRace(race_id) ? + fmt::format( + " ({})", + GetPlayerRaceBit(race_id) + ) : + "" + ) ).c_str() ); - } - else { + } else { c->Message( Chat::White, fmt::format( @@ -31,51 +40,52 @@ void command_findrace(Client *c, const Seperator *sep) ).c_str() ); } - } - else { - std::string search_criteria = str_tolower(sep->argplus[1]); - int found_count = 0; - for (int race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { - std::string race_name = GetRaceIDName(race_id); - std::string race_name_lower = str_tolower(race_name); - if (search_criteria.length() > 0 && race_name_lower.find(search_criteria) == std::string::npos) { + } else { + auto search_criteria = str_tolower(sep->argplus[1]); + int found_count = 0; + for (uint16 race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) { + std::string race_name = GetRaceIDName(race_id); + auto race_name_lower = str_tolower(race_name); + if ( + search_criteria.length() && + race_name_lower.find(search_criteria) == std::string::npos + ) { continue; } c->Message( Chat::White, fmt::format( - "Race {}: {}", + "Race {} | {}{}", race_id, - race_name + race_name, + ( + c->IsPlayerRace(race_id) ? + fmt::format( + " ({})", + GetPlayerRaceBit(race_id) + ) : + "" + ) ).c_str() ); + found_count++; - if (found_count == 20) { + if (found_count == 50) { break; } } - if (found_count == 20) { - c->Message(Chat::White, "20 Races found... max reached."); - } - else { - auto race_message = ( - found_count > 0 ? - ( - found_count == 1 ? - "A Race was" : - fmt::format("{} Races were", found_count) - ) : - "No Races were" - ); - + if (found_count == 50) { + c->Message(Chat::White, "50 Races found, max reached."); + } else { c->Message( Chat::White, fmt::format( - "{} found.", - race_message + "{} Race{} found.", + found_count, + found_count != 1 ? "s" : "" ).c_str() ); } diff --git a/zone/mob.cpp b/zone/mob.cpp index dc0032256..103989e4a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -2854,6 +2854,16 @@ bool Mob::RandomizeFeatures(bool send_illusion, bool set_variables) return false; } +bool Mob::IsPlayerClass(uint16 in_class) { + if ( + in_class >= WARRIOR && + in_class <= BERSERKER + ) { + return true; + } + + return false; +} bool Mob::IsPlayerRace(uint16 in_race) { diff --git a/zone/mob.h b/zone/mob.h index 67aaa734a..269572d03 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -741,6 +741,7 @@ public: //Util static uint32 RandomTimer(int min, int max); static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF); + static bool IsPlayerClass(uint16 in_class); static bool IsPlayerRace(uint16 in_race); EQ::skills::SkillType GetSkillByItemType(int ItemType); uint8 GetItemTypeBySkill(EQ::skills::SkillType skill); From 7072b5721b72d3ed58fe21b4fb9c6c03da640b98 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 15:00:59 -0400 Subject: [PATCH 027/552] [Rules] Add Spells:BuffsFadeOnDeath. (#2200) - Allows you to disable buffs fading on death. --- common/ruletypes.h | 1 + zone/attack.cpp | 5 ++++- zone/bot.cpp | 5 ++++- zone/client_process.cpp | 5 ++++- zone/mob.cpp | 7 +++++-- 5 files changed, 18 insertions(+), 5 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index badbdae5e..3e347a962 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -414,6 +414,7 @@ RULE_BOOL(Spells, CompoundLifetapHeals, true, "True: Lifetap heals calculate dam RULE_BOOL(Spells, UseFadingMemoriesMaxLevel, false, "Enables to limit field in spell data to set the max level that over which an NPC will ignore fading memories effect and not lose aggro.") RULE_BOOL(Spells, FixBeaconHeading, false, "Beacon spells use casters heading to fix live bug. False: Live like heading always 0.") RULE_BOOL(Spells, UseSpellImpliedTargeting, false, "Replicates EQ2-style targeting behavior for spells. Spells will 'pass through' inappropriate targets to target's target if it is appropriate.") +RULE_BOOL(Spells, BuffsFadeOnDeath, true, "Disable to keep buffs from fading on death") RULE_BOOL(Spells, IllusionsAlwaysPersist, false, "Allows Illusions to persist beyond death and zoning always.") RULE_CATEGORY_END() diff --git a/zone/attack.cpp b/zone/attack.cpp index ba7453836..854a4b6d4 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1894,7 +1894,10 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill int32 illusion_spell_id = spellbonuses.Illusion; //this generates a lot of 'updates' to the client that the client does not need - BuffFadeNonPersistDeath(); + if (RuleB(Spells, BuffsFadeOnDeath)) { + BuffFadeNonPersistDeath(); + } + if (RuleB(Character, UnmemSpellsOnDeath)) { if ((ClientVersionBit() & EQ::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover)) UnmemSpellAll(true); diff --git a/zone/bot.cpp b/zone/bot.cpp index 36e037b85..5e913e6ee 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -398,7 +398,10 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to current_hp = max_hp; if(current_hp <= 0) { - BuffFadeNonPersistDeath(); + if (RuleB(Spells, BuffsFadeOnDeath)) { + BuffFadeNonPersistDeath(); + } + if (RuleB(Bots, ResurrectionSickness)) { int resurrection_sickness_spell_id = ( RuleB(Bots, OldRaceRezEffects) && diff --git a/zone/client_process.cpp b/zone/client_process.cpp index b5c56d2e6..bcc3137a0 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -1020,7 +1020,10 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I name, (uint16)spells[SpellID].base_value[0], SpellID, ZoneID, InstanceID); - BuffFadeNonPersistDeath(); + if (RuleB(Spells, BuffsFadeOnDeath)) { + BuffFadeNonPersistDeath(); + } + int SpellEffectDescNum = GetSpellEffectDescNum(SpellID); // Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client) if(RuleB(Character, UseResurrectionSickness) && SpellEffectDescNum == 82 || SpellEffectDescNum == 39067) { diff --git a/zone/mob.cpp b/zone/mob.cpp index 103989e4a..07b00830a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5330,11 +5330,14 @@ bool Mob::TrySpellOnDeath() if(spellbonuses.SpellOnDeath[i] && IsValidSpell(spellbonuses.SpellOnDeath[i])) { if(zone->random.Roll(static_cast(spellbonuses.SpellOnDeath[i + 1]))) { SpellFinished(spellbonuses.SpellOnDeath[i], this, EQ::spells::CastingSlot::Item, 0, -1, spells[spellbonuses.SpellOnDeath[i]].resist_difficulty); - } } } + } + + if (RuleB(Spells, BuffsFadeOnDeath)) { + BuffFadeNonPersistDeath(); + } - BuffFadeNonPersistDeath(); return false; //You should not be able to use this effect and survive (ALWAYS return false), //attempting to place a heal in these effects will still result From 275995a37439c120af39379e2fb6622e3ebe7ead Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 15:26:30 -0400 Subject: [PATCH 028/552] [Commands] Cleanup #zone and #zoneinstance Commands. (#2202) * [Commands] Cleanup #zone and #zoneinstance Commands. - Cleanup messages and logic. - Broke these commands out in to their own files. * Update database.cpp * Update database.cpp * Update database.cpp --- common/database.cpp | 15 ++++ common/database.h | 1 + zone/command.cpp | 117 +---------------------------- zone/gm_commands/zone.cpp | 56 ++++++++++++++ zone/gm_commands/zone_instance.cpp | 70 +++++++++++++++++ 5 files changed, 146 insertions(+), 113 deletions(-) create mode 100644 zone/gm_commands/zone.cpp create mode 100644 zone/gm_commands/zone_instance.cpp diff --git a/common/database.cpp b/common/database.cpp index 93535e584..b14e8009b 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -50,6 +50,8 @@ #include "http/httplib.h" #include "http/uri.h" +#include "repositories/zone_repository.h" + extern Client client; Database::Database () { @@ -2496,3 +2498,16 @@ void Database::SourceDatabaseTableFromUrl(std::string table_name, std::string ur } } +uint8 Database::GetMinStatus(uint32 zone_id, uint32 instance_version) +{ + auto zones = ZoneRepository::GetWhere( + *this, + fmt::format( + "zoneidnumber = {} AND (version = {} OR version = 0) ORDER BY version DESC LIMIT 1", + zone_id, + instance_version + ) + ); + + return !zones.empty() ? zones[0].min_status : 0; +} diff --git a/common/database.h b/common/database.h index d70960e99..b11c07671 100644 --- a/common/database.h +++ b/common/database.h @@ -252,6 +252,7 @@ public: bool LoadPTimers(uint32 charid, PTimerList &into); uint8 GetPEQZone(uint32 zone_id, uint32 version); + uint8 GetMinStatus(uint32 zone_id, uint32 instance_version); uint8 GetRaceSkill(uint8 skillid, uint8 in_race); uint8 GetServerType(); uint8 GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 in_level); diff --git a/zone/command.cpp b/zone/command.cpp index f35a3591c..7a14de8ad 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -378,9 +378,9 @@ int command_init(void) command_add("zclip", "[Minimum Clip] [Maximum Clip] [Fog Minimum Clip] [Fog Maximum Clip] [Permanent (0 = False, 1 = True)] - Change zone clipping", AccountStatus::QuestTroupe, command_zclip) || command_add("zcolor", "[Red] [Green] [Blue] [Permanent (0 = False, 1 = True)] - Change sky color", AccountStatus::QuestTroupe, command_zcolor) || command_add("zheader", "[Zone ID|Zone Short Name] [Version] - Load a zone header from the database", AccountStatus::QuestTroupe, command_zheader) || - command_add("zone", "[zonename] [x] [y] [z] - Go to specified zone (coords optional)", AccountStatus::Guide, command_zone) || + command_add("zone", "[Zone ID|Zone Short Name] [X] [Y] [Z] - Teleport to specified Zone by ID or Short Name (coordinates are optional)", AccountStatus::Guide, command_zone) || command_add("zonebootup", "[ZoneServerID] [shortname] - Make a zone server boot a specific zone", AccountStatus::GMLeadAdmin, command_zonebootup) || - command_add("zoneinstance", "[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)", AccountStatus::Guide, command_zone_instance) || + command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) || command_add("zonelock", "[List|Lock|Unlock] [Zone ID|Zone Short Name] - Set or get lock status of a Zone by ID or Short Name", AccountStatus::GMAdmin, command_zonelock) || command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) || command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", AccountStatus::GMLeadAdmin, command_zonestatus) || @@ -635,117 +635,6 @@ void command_help(Client *c, const Seperator *sep) } -void command_zone(Client *c, const Seperator *sep) -{ - if(c->Admin() < commandZoneToCoords && - (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4))) { - c->Message(Chat::White, "Your status is not high enough to zone to specific coordinates."); - return; - } - - uint16 zoneid = 0; - - if (sep->IsNumber(1)) - { - if(atoi(sep->arg[1])==26 && (c->Admin() < commandZoneToSpecials)){ //cshome - c->Message(Chat::White, "Only Guides and above can goto that zone."); - return; - } - zoneid = atoi(sep->arg[1]); - } - else if (sep->arg[1][0] == 0) - { - c->Message(Chat::White, "Usage: #zone [zonename]"); - c->Message(Chat::White, "Optional Usage: #zone [zonename] y x z"); - return; - } - else if (zone->GetZoneID() == 184 && c->Admin() < commandZoneToSpecials) { // Zone: 'Load' - c->Message(Chat::White, "The Gods brought you here, only they can send you away."); - return; - } else { - if((strcasecmp(sep->arg[1], "cshome")==0) && (c->Admin() < commandZoneToSpecials)){ - c->Message(Chat::White, "Only Guides and above can goto that zone."); - return; - } - - zoneid = ZoneID(sep->arg[1]); - if(zoneid == 0) { - c->Message(Chat::White, "Unable to locate zone '%s'", sep->arg[1]); - return; - } - } - -#ifdef BOTS - // This block is necessary to clean up any bot objects owned by a Client - if(zoneid != c->GetZoneID()) - Bot::ProcessClientZoneChange(c); -#endif - - if (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4)){ - //zone to specific coords - c->MovePC(zoneid, (float)atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), 0.0f, 0); - } - else - //zone to safe coords - c->MovePC(zoneid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); -} - -//todo: fix this so it checks if you're in the instance set -void command_zone_instance(Client *c, const Seperator *sep) -{ - if(c->Admin() < commandZoneToCoords && - (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4))) { - c->Message(Chat::White, "Your status is not high enough to zone to specific coordinates."); - return; - } - - if (sep->arg[1][0] == 0) - { - c->Message(Chat::White, "Usage: #zoneinstance [instance id]"); - c->Message(Chat::White, "Optional Usage: #zoneinstance [instance id] y x z"); - return; - } - - uint16 zoneid = 0; - uint16 instanceid = 0; - - if(sep->IsNumber(1)) - { - instanceid = atoi(sep->arg[1]); - if(!instanceid) - { - c->Message(Chat::White, "Must enter a valid instance id."); - return; - } - - zoneid = database.ZoneIDFromInstanceID(instanceid); - if(!zoneid) - { - c->Message(Chat::White, "Instance not found or zone is set to null."); - return; - } - } - else - { - c->Message(Chat::White, "Must enter a valid instance id."); - return; - } - - if(!database.VerifyInstanceAlive(instanceid, c->CharacterID())) - { - c->Message(Chat::White, "Instance ID expiried or you are not apart of this instance."); - return; - } - - if (sep->IsNumber(2) || sep->IsNumber(3) || sep->IsNumber(4)){ - //zone to specific coords - c->MovePC(zoneid, instanceid, atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), 0.0f, 0); - } - else{ - c->MovePC(zoneid, instanceid, 0.0f, 0.0f, 0.0f, 0.0f, 0, ZoneToSafeCoords); - } -} - void command_spawneditmass(Client *c, const Seperator *sep) { std::string query = fmt::format( @@ -1355,10 +1244,12 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/zclip.cpp" #include "gm_commands/zcolor.cpp" #include "gm_commands/zheader.cpp" +#include "gm_commands/zone.cpp" #include "gm_commands/zonebootup.cpp" #include "gm_commands/zonelock.cpp" #include "gm_commands/zoneshutdown.cpp" #include "gm_commands/zonestatus.cpp" +#include "gm_commands/zone_instance.cpp" #include "gm_commands/zopp.cpp" #include "gm_commands/zsafecoords.cpp" #include "gm_commands/zsave.cpp" diff --git a/zone/gm_commands/zone.cpp b/zone/gm_commands/zone.cpp new file mode 100644 index 000000000..add9471da --- /dev/null +++ b/zone/gm_commands/zone.cpp @@ -0,0 +1,56 @@ +#include "../client.h" + +void command_zone(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #zone [Zone ID|Zone Short Name] [X] [Y] [Z]"); + return; + } + + auto zone_id = ( + sep->IsNumber(1) ? + std::stoul(sep->arg[1]) : + ZoneID(sep->arg[1]) + ); + auto zone_short_name = ZoneName(zone_id); + if ( + !zone_id || + !zone_short_name + ) { + c->Message( + Chat::White, + fmt::format( + "No zones were found matching '{}'.", + sep->arg[1] + ).c_str() + ); + return; + } + + auto min_status = database.GetMinStatus(zone_id, 0); + if (c->Admin() < min_status) { + c->Message(Chat::White, "Your status is not high enough to go to this zone."); + return; + } + +#ifdef BOTS + // This block is necessary to clean up any bot objects owned by a Client + if (zone_id != c->GetZoneID()) { + Bot::ProcessClientZoneChange(c); + } +#endif + + auto x = sep->IsNumber(2) ? std::stof(sep->arg[2]) : 0.0f; + auto y = sep->IsNumber(3) ? std::stof(sep->arg[3]) : 0.0f; + auto z = sep->IsNumber(4) ? std::stof(sep->arg[4]) : 0.0f; + + c->MovePC( + zone_id, + x, + y, + z, + 0.0f, + sep->IsNumber(2) ? 0 : ZoneToSafeCoords + ); +} \ No newline at end of file diff --git a/zone/gm_commands/zone_instance.cpp b/zone/gm_commands/zone_instance.cpp new file mode 100644 index 000000000..d54b3b251 --- /dev/null +++ b/zone/gm_commands/zone_instance.cpp @@ -0,0 +1,70 @@ +#include "../client.h" + +void command_zone_instance(Client *c, const Seperator *sep) +{ + int arguments = sep->argnum; + if (!arguments || !sep->IsNumber(1)) { + c->Message(Chat::White, "Usage: #zoneinstance [Instance ID] [X] [Y] [Z]"); + return; + } + + auto instance_id = std::stoul(sep->arg[1]); + if (!instance_id) { + c->Message(Chat::White, "You must enter a valid instance id."); + return; + } + + if (!database.CheckInstanceExists(instance_id)) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} does not exist.", + instance_id + ).c_str() + ); + return; + } + + auto zone_id = database.ZoneIDFromInstanceID(instance_id); + if (!zone_id) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} not found or zone is set to null.", + instance_id + ).c_str() + ); + return; + } + + if (database.CharacterInInstanceGroup(instance_id, c->CharacterID())) { + c->Message(Chat::White, "You are already a part of this instance, sending you there."); + c->MoveZoneInstance(instance_id); + return; + } + + if (!database.VerifyInstanceAlive(instance_id, c->CharacterID())) { + c->Message( + Chat::White, + fmt::format( + "Instance ID {} expired or you are not apart of this instance.", + instance_id + ).c_str() + ); + return; + } + + auto x = sep->IsNumber(2) ? std::stof(sep->arg[2]) : 0.0f; + auto y = sep->IsNumber(3) ? std::stof(sep->arg[3]) : 0.0f; + auto z = sep->IsNumber(4) ? std::stof(sep->arg[4]) : 0.0f; + + c->MovePC( + zone_id, + instance_id, + x, + y, + z, + 0.0f, + sep->IsNumber(2) ? 0 : ZoneToSafeCoords + ); +} From d7f38361f2b00294397d6d689f4cb9d461b56856 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 15:28:36 -0400 Subject: [PATCH 029/552] [Commands] Cleanup #findaliases and #help Commands. (#2204) - Cleanup messages and logic. - Add saylinks to messages for ease of use when searching for commands as most have help messages when using them incorrectly or require no arguments and can be used immediately from the say link. --- zone/command.cpp | 514 ++++++++++++++++++-------------- zone/command.h | 26 +- zone/gm_commands/logcommand.cpp | 46 +-- 3 files changed, 334 insertions(+), 252 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index 7a14de8ad..269d6d62e 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -44,18 +44,15 @@ extern FastMath g_Math; void CatchSignal(int sig_num); -int commandcount; // how many commands we have +int command_count; // how many commands we have // this is the pointer to the dispatch function, updated once // init has been performed to point at the real function -int (*command_dispatch)(Client *,char const *)=command_notavail; +int (*command_dispatch)(Client *,std::string) = command_notavail; std::map commandlist; std::map commandaliases; -// All allocated CommandRecords get put in here so they get deleted on shutdown -LinkedList cleanup_commandlist; - /* * command_notavail * This is the default dispatch function when commands aren't loaded. @@ -64,39 +61,12 @@ LinkedList cleanup_commandlist; * not used * */ -int command_notavail(Client *c, const char *message) +int command_notavail(Client *c, std::string message) { - c->Message(Chat::Red, "Commands not available."); + c->Message(Chat::White, "Commands not available."); return -1; } -/************************************************************************** -/* the rest below here could be in a dynamically loaded module eventually * -/*************************************************************************/ - -/* - -Access Levels: - -0 Normal -10 * Steward * -20 * Apprentice Guide * -50 * Guide * -80 * QuestTroupe * -81 * Senior Guide * -85 * GM-Tester * -90 * EQ Support * -95 * GM-Staff * -100 * GM-Admin * -150 * GM-Lead Admin * -160 * QuestMaster * -170 * GM-Areas * -180 * GM-Coder * -200 * GM-Mgmt * -250 * GM-Impossible * - -*/ - /* * command_init * initializes the command list, call at startup @@ -120,38 +90,38 @@ int command_init(void) command_add("aggrozone", "[aggro] - Aggro every mob in the zone with X aggro. Default is 0. Not recommend if you're not invulnerable.", AccountStatus::GMAdmin, command_aggrozone) || command_add("ai", "[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target", AccountStatus::GMAdmin, command_ai) || command_add("appearance", "[type] [value] - Send an appearance packet for you or your target", AccountStatus::GMLeadAdmin, command_appearance) || - command_add("appearanceeffects", "- [view] [set] [remove] appearance effects.", AccountStatus::GMAdmin, command_appearanceeffects) || + command_add("appearanceeffects", "[view] [set] [remove] appearance effects.", AccountStatus::GMAdmin, command_appearanceeffects) || command_add("apply_shared_memory", "[shared_memory_name] - Tells every zone and world to apply a specific shared memory segment by name.", AccountStatus::GMImpossible, command_apply_shared_memory) || command_add("attack", "[Entity Name] - Make your NPC target attack an entity by name", AccountStatus::GMLeadAdmin, command_attack) || command_add("augmentitem", "Force augments an item. Must have the augment item window open.", AccountStatus::GMImpossible, command_augmentitem) || - command_add("ban", "[Character Name] [Reason]- Ban by character name", AccountStatus::GMLeadAdmin, command_ban) || - command_add("bind", "- Sets your targets bind spot to their current location", AccountStatus::GMMgmt, command_bind) || + command_add("ban", "[Character Name] [Reason] - Ban by character name", AccountStatus::GMLeadAdmin, command_ban) || + command_add("bind", "Sets your targets bind spot to their current location", AccountStatus::GMMgmt, command_bind) || #ifdef BOTS - command_add("bot", "- Type \"#bot help\" or \"^help\" to the see the list of available commands for bots.", AccountStatus::Player, command_bot) || + command_add("bot", "Type \"#bot help\" or \"^help\" to the see the list of available commands for bots.", AccountStatus::Player, command_bot) || #endif command_add("camerashake", "[Duration (Milliseconds)] [Intensity (1-10)] - Shakes the camera on everyone's screen globally.", AccountStatus::QuestTroupe, command_camerashake) || command_add("castspell", "[Spell ID] [Instant (0 = False, 1 = True, Default is 1 if Unused)] - Cast a spell", AccountStatus::Guide, command_castspell) || command_add("chat", "[channel num] [message] - Send a channel message to all zones", AccountStatus::GMMgmt, command_chat) || - command_add("checklos", "- Check for line of sight to your target", AccountStatus::Guide, command_checklos) || - command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] Copies character to destination account", AccountStatus::GMImpossible, command_copycharacter) || - command_add("corpse", "- Manipulate corpses, use with no arguments for help", AccountStatus::Guide, command_corpse) || + command_add("checklos", "Check for line of sight to your target", AccountStatus::Guide, command_checklos) || + command_add("copycharacter", "[source_char_name] [dest_char_name] [dest_account_name] - Copies character to destination account", AccountStatus::GMImpossible, command_copycharacter) || + command_add("corpse", "Manipulate corpses, use with no arguments for help", AccountStatus::Guide, command_corpse) || command_add("corpsefix", "Attempts to bring corpses from underneath the ground within close proximity of the player", AccountStatus::Player, command_corpsefix) || command_add("countitem", "[Item ID] - Counts the specified Item ID in your or your target's inventory", AccountStatus::GMLeadAdmin, command_countitem) || - command_add("cvs", "- Summary of client versions currently online.", AccountStatus::GMMgmt, command_cvs) || + command_add("cvs", "Summary of client versions currently online.", AccountStatus::GMMgmt, command_cvs) || command_add("damage", "[Amount] - Damage yourself or your target", AccountStatus::GMAdmin, command_damage) || command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", AccountStatus::QuestTroupe, command_databuckets) || command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_date) || command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", AccountStatus::GMAdmin, command_dbspawn2) || command_add("delacct", "[accountname] - Delete an account", AccountStatus::GMLeadAdmin, command_delacct) || command_add("delpetition", "[petition number] - Delete a petition", AccountStatus::ApprenticeGuide, command_delpetition) || - command_add("depop", "- Depop your NPC target", AccountStatus::Guide, command_depop) || - command_add("depopzone", "- Depop the zone", AccountStatus::GMAdmin, command_depopzone) || - command_add("devtools", "- Manages devtools", AccountStatus::GMMgmt, command_devtools) || + command_add("depop", "Depop your NPC target", AccountStatus::Guide, command_depop) || + command_add("depopzone", "Depop the zone", AccountStatus::GMAdmin, command_depopzone) || + command_add("devtools", "Manages devtools", AccountStatus::GMMgmt, command_devtools) || command_add("disablerecipe", "[Recipe ID] - Disables a Recipe", AccountStatus::QuestTroupe, command_disablerecipe) || command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", AccountStatus::QuestTroupe, command_disarmtrap) || - command_add("distance", "- Reports the distance between you and your target.", AccountStatus::QuestTroupe, command_distance) || + command_add("distance", "Reports the distance between you and your target.", AccountStatus::QuestTroupe, command_distance) || command_add("door", "Door editing command", AccountStatus::QuestTroupe, command_door) || command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", AccountStatus::Guide, command_doanim) || command_add("dye", "[slot|'help'] [red] [green] [blue] [use_tint] - Dyes the specified armor slot to Red, Green, and Blue provided, allows you to bypass darkness limits.", AccountStatus::ApprenticeGuide, command_dye) || @@ -161,34 +131,34 @@ int command_init(void) command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", AccountStatus::QuestTroupe, command_emote) || command_add("emotesearch", "Searches NPC Emotes", AccountStatus::QuestTroupe, command_emotesearch) || command_add("emoteview", "Lists all NPC Emotes", AccountStatus::QuestTroupe, command_emoteview) || - command_add("emptyinventory", "- Clears your or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", AccountStatus::GMImpossible, command_emptyinventory) || + command_add("emptyinventory", "Clears your or your target's entire inventory (Equipment, General, Bank, and Shared Bank)", AccountStatus::GMImpossible, command_emptyinventory) || command_add("enablerecipe", "[Recipe ID] - Enables a Recipe", AccountStatus::QuestTroupe, command_enablerecipe) || command_add("endurance", "Restores your or your target's endurance.", AccountStatus::Guide, command_endurance) || command_add("equipitem", "[slotid(0-21)] - Equip the item on your cursor into the specified slot", AccountStatus::Guide, command_equipitem) || command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) || - command_add("feature", "- Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) || - command_add("findaliases", "[search criteria]- Searches for available command aliases, by alias or command", AccountStatus::Player, command_findaliases) || - command_add("findclass", "[search criteria] - Search for a class", AccountStatus::Guide, command_findclass) || - command_add("findfaction", "[search criteria] - Search for a faction", AccountStatus::Guide, command_findfaction) || - command_add("findnpctype", "[search criteria] - Search database NPC types", AccountStatus::GMAdmin, command_findnpctype) || - command_add("findrace", "[search criteria] - Search for a race", AccountStatus::Guide, command_findrace) || - command_add("findskill", "[search criteria] - Search for a skill", AccountStatus::Guide, command_findskill) || - command_add("findspell", "[search criteria] - Search for a spell", AccountStatus::Guide, command_findspell) || - command_add("findtask", "[search criteria] - Search for a task", AccountStatus::Guide, command_findtask) || - command_add("findzone", "[search criteria] - Search database zones", AccountStatus::GMAdmin, command_findzone) || + command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) || + command_add("findaliases", "[Search Criteria]- Searches for available command aliases, by alias or command", AccountStatus::Player, command_findaliases) || + command_add("findclass", "[Search Criteria] - Search for a class", AccountStatus::Guide, command_findclass) || + command_add("findfaction", "[Search Criteria] - Search for a faction", AccountStatus::Guide, command_findfaction) || + command_add("findnpctype", "[Search Criteria] - Search database NPC types", AccountStatus::GMAdmin, command_findnpctype) || + command_add("findrace", "[Search Criteria] - Search for a race", AccountStatus::Guide, command_findrace) || + command_add("findskill", "[Search Criteria] - Search for a skill", AccountStatus::Guide, command_findskill) || + command_add("findspell", "[Search Criteria] - Search for a spell", AccountStatus::Guide, command_findspell) || + command_add("findtask", "[Search Criteria] - Search for a task", AccountStatus::Guide, command_findtask) || + command_add("findzone", "[Search Criteria] - Search database zones", AccountStatus::GMAdmin, command_findzone) || command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) || command_add("flag", "[Status] [Account Name] - Refresh your admin status, or set an account's Admin status if arguments provided", AccountStatus::Player, command_flag) || - command_add("flagedit", "- Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) || - command_add("flags", "- displays the Zone Flags of you or your target", AccountStatus::Player, command_flags) || + command_add("flagedit", "Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) || + command_add("flags", "displays the Zone Flags of you or your target", AccountStatus::Player, command_flags) || command_add("flymode", "[0/1/2/3/4/5] - Set your or your player target's flymode to ground/flying/levitate/water/floating/levitate_running", AccountStatus::Guide, command_flymode) || - command_add("fov", "- Check wether you're behind or in your target's field of view", AccountStatus::QuestTroupe, command_fov) || - command_add("freeze", "- Freeze your target", AccountStatus::QuestTroupe, command_freeze) || + command_add("fov", "Check wether you're behind or in your target's field of view", AccountStatus::QuestTroupe, command_fov) || + command_add("freeze", "Freeze your target", AccountStatus::QuestTroupe, command_freeze) || command_add("gassign", "[Grid ID] - Assign targetted NPC to predefined wandering grid id", AccountStatus::GMAdmin, command_gassign) || command_add("gearup", "Developer tool to quickly equip a character", AccountStatus::GMMgmt, command_gearup) || command_add("gender", "[0/1/2] - Change your or your target's gender to male/female/neuter", AccountStatus::Guide, command_gender) || - command_add("getplayerburiedcorpsecount", "- Get your or your target's total number of buried player corpses.", AccountStatus::GMAdmin, command_getplayerburiedcorpsecount) || + command_add("getplayerburiedcorpsecount", "Get your or your target's total number of buried player corpses.", AccountStatus::GMAdmin, command_getplayerburiedcorpsecount) || command_add("getvariable", "[Variable Name] - Get the value of a variable from the database", AccountStatus::GMMgmt, command_getvariable) || - command_add("ginfo", "- get group info on target.", AccountStatus::ApprenticeGuide, command_ginfo) || + command_add("ginfo", "get group info on target.", AccountStatus::ApprenticeGuide, command_ginfo) || command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) || command_add("givemoney", "[Platinum] [Gold] [Silver] [Copper] - Gives specified amount of money to you or your player target", AccountStatus::GMMgmt, command_givemoney) || command_add("globalview", "Lists all qglobals in cache if you were to do a quest with this target.", AccountStatus::QuestTroupe, command_globalview) || @@ -197,40 +167,40 @@ int command_init(void) command_add("gmzone", "[Zone ID|Zone Short Name] [Version] [Instance Identifier] - Zones to a private GM instance (Version defaults to 0 and Instance Identifier defaults to 'gmzone' if not used)", AccountStatus::GMAdmin, command_gmzone) || command_add("goto", "[playername] or [x y z] [h] - Teleport to the provided coordinates or to your target", AccountStatus::Steward, command_goto) || command_add("grid", "[add/delete] [grid_num] [wandertype] [pausetype] - Create/delete a wandering grid", AccountStatus::GMAreas, command_grid) || - command_add("guild", "- Guild manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_guild) || + command_add("guild", "Guild manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_guild) || command_add("guildapprove", "[guildapproveid] - Approve a guild with specified ID (guild creator receives the id)", AccountStatus::Player, command_guildapprove) || command_add("guildcreate", "[guildname] - Creates an approval setup for guild name specified", AccountStatus::Player, command_guildcreate) || command_add("guildlist", "[guildapproveid] - Lists character names who have approved the guild specified by the approve id", AccountStatus::Player, command_guildlist) || command_add("haste", "[percentage] - Set your haste percentage", AccountStatus::GMAdmin, command_haste) || - command_add("hatelist", "- Display hate list for NPC.", AccountStatus::QuestTroupe, command_hatelist) || - command_add("heal", "- Completely heal your target", AccountStatus::Steward, command_heal) || - command_add("help", "[search term] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || + command_add("hatelist", "Display hate list for NPC.", AccountStatus::QuestTroupe, command_hatelist) || + command_add("heal", "Completely heal your target", AccountStatus::Steward, command_heal) || + command_add("help", "[Search Criteria] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || command_add("heromodel", "[hero model] [slot] - Full set of Hero's Forge Armor appearance. If slot is set, sends exact model just to slot.", AccountStatus::GMMgmt, command_heromodel) || command_add("hideme", "[on/off] - Hide yourself from spawn lists.", AccountStatus::QuestTroupe, command_hideme) || command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", AccountStatus::GMImpossible, command_hotfix) || - command_add("hp", "- Refresh your HP bar from the server.", AccountStatus::Player, command_hp) || - command_add("incstat", "- Increases or Decreases a client's stats permanently.", AccountStatus::GMMgmt, command_incstat) || - command_add("instance", "- Modify Instances", AccountStatus::GMMgmt, command_instance) || - command_add("interrogateinv", "- use [help] argument for available options", AccountStatus::Player, command_interrogateinv) || + command_add("hp", "Refresh your HP bar from the server.", AccountStatus::Player, command_hp) || + command_add("incstat", "Increases or Decreases a client's stats permanently.", AccountStatus::GMMgmt, command_incstat) || + command_add("instance", "Modify Instances", AccountStatus::GMMgmt, command_instance) || + command_add("interrogateinv", "use [help] argument for available options", AccountStatus::Player, command_interrogateinv) || command_add("interrupt", "[message id] [color] - Interrupt your casting. Arguments are optional.", AccountStatus::Guide, command_interrupt) || - command_add("invsnapshot", "- Manipulates inventory snapshots for your current target", AccountStatus::QuestTroupe, command_invsnapshot) || + command_add("invsnapshot", "Manipulates inventory snapshots for your current target", AccountStatus::QuestTroupe, command_invsnapshot) || command_add("invul", "[On|Off]] - Turn player target's or your invulnerable flag on or off", AccountStatus::QuestTroupe, command_invul) || command_add("ipban", "[IP] - Ban IP", AccountStatus::GMMgmt, command_ipban) || command_add("iplookup", "[charname] - Look up IP address of charname", AccountStatus::GMMgmt, command_iplookup) || - command_add("iteminfo", "- Get information about the item on your cursor", AccountStatus::Steward, command_iteminfo) || - command_add("itemsearch", "[search criteria] - Search for an item", AccountStatus::Steward, command_itemsearch) || + command_add("iteminfo", "Get information about the item on your cursor", AccountStatus::Steward, command_iteminfo) || + command_add("itemsearch", "[Search Criteria] - Search for an item", AccountStatus::Steward, command_itemsearch) || command_add("kick", "[Character Name] - Disconnect a player by name", AccountStatus::GMLeadAdmin, command_kick) || - command_add("kill", "- Kill your target", AccountStatus::GMAdmin, command_kill) || - command_add("killallnpcs", " [npc_name] Kills all npcs by search name, leave blank for all attackable NPC's", AccountStatus::GMMgmt, command_killallnpcs) || + command_add("kill", "Kill your target", AccountStatus::GMAdmin, command_kill) || + command_add("killallnpcs", "[npc_name] - Kills all npcs by search name, leave blank for all attackable NPC's", AccountStatus::GMMgmt, command_killallnpcs) || command_add("lastname", "[Last Name] - Set you or your player target's lastname", AccountStatus::Guide, command_lastname) || command_add("level", "[Level] - Set your target's level", AccountStatus::Steward, command_level) || command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", AccountStatus::ApprenticeGuide, command_list) || - command_add("listpetition", "- List petitions", AccountStatus::Guide, command_listpetition) || + command_add("listpetition", "List petitions", AccountStatus::Guide, command_listpetition) || command_add("load_shared_memory", "[shared_memory_name] - Reloads shared memory and uses the input as output", AccountStatus::GMImpossible, command_load_shared_memory) || - command_add("loc", "- Print out your or your target's current location and heading", AccountStatus::Player, command_loc) || + command_add("loc", "Print out your or your target's current location and heading", AccountStatus::Player, command_loc) || command_add("logs", "Manage anything to do with logs", AccountStatus::GMImpossible, command_logs) || command_add("makepet", "[Pet Name] - Make a pet", AccountStatus::Guide, command_makepet) || - command_add("mana", "- Fill your or your target's mana", AccountStatus::Guide, command_mana) || + command_add("mana", "Fill your or your target's mana", AccountStatus::Guide, command_mana) || command_add("maxskills", "Maxes skills for you.", AccountStatus::GMMgmt, command_max_all_skills) || command_add("memspell", "[Spell ID] [Spell Gem] - Memorize a Spell by ID to the specified Spell Gem for you or your target", AccountStatus::Guide, command_memspell) || command_add("merchant_close_shop", "Closes a merchant shop", AccountStatus::GMAdmin, command_merchantcloseshop) || @@ -239,23 +209,23 @@ int command_init(void) command_add("motd", "[Message of the Day] - Set Message of the Day (leave empty to have no Message of the Day)", AccountStatus::GMLeadAdmin, command_motd) || command_add("movechar", "[Character ID|Character Name] [Zone ID|Zone Short Name] - Move an offline character to the specified zone", AccountStatus::Guide, command_movechar) || command_add("movement", "Various movement commands", AccountStatus::GMMgmt, command_movement) || - command_add("myskills", "- Show details about your current skill levels", AccountStatus::Player, command_myskills) || + command_add("myskills", "Show details about your current skill levels", AccountStatus::Player, command_myskills) || command_add("mysql", "[Help|Query] [SQL Query] - Mysql CLI, see 'Help' for options.", AccountStatus::GMImpossible, command_mysql) || - command_add("mystats", "- Show details about you or your pet", AccountStatus::Guide, command_mystats) || + command_add("mystats", "Show details about you or your pet", AccountStatus::Guide, command_mystats) || command_add("name", "[New Name] - Rename your player target", AccountStatus::GMLeadAdmin, command_name) || - command_add("netstats", "- Gets the network stats for a stream.", AccountStatus::GMMgmt, command_netstats) || - command_add("network", "- Admin commands for the udp network interface.", AccountStatus::GMImpossible, command_network) || + command_add("netstats", "Gets the network stats for a stream.", AccountStatus::GMMgmt, command_netstats) || + command_add("network", "Admin commands for the udp network interface.", AccountStatus::GMImpossible, command_network) || command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", AccountStatus::QuestTroupe, command_npccast) || command_add("npcedit", "[column] [value] - Mega NPC editing command", AccountStatus::GMAdmin, command_npcedit) || command_add("npceditmass", "[name-search] [column] [value] - Mass (Zone wide) NPC data editing command", AccountStatus::GMAdmin, command_npceditmass) || command_add("npcemote", "[Message] - Make your NPC target emote a message.", AccountStatus::GMLeadAdmin, command_npcemote) || - command_add("npcloot", "- Manipulate the loot an NPC is carrying. Use #npcloot help for more information.", AccountStatus::QuestTroupe, command_npcloot) || + command_add("npcloot", "Manipulate the loot an NPC is carrying. Use #npcloot help for more information.", AccountStatus::QuestTroupe, command_npcloot) || command_add("npcsay", "[Message] - Make your NPC target say a message.", AccountStatus::GMLeadAdmin, command_npcsay) || command_add("npcshout", "[Message] - Make your NPC target shout a message.", AccountStatus::GMLeadAdmin, command_npcshout) || command_add("npcspawn", "[create/add/update/remove/delete] - Manipulate spawn DB", AccountStatus::GMAreas, command_npcspawn) || - command_add("npcstats", "- Show stats about target NPC", AccountStatus::QuestTroupe, command_npcstats) || + command_add("npcstats", "Show stats about target NPC", AccountStatus::QuestTroupe, command_npcstats) || command_add("npctypespawn", "[NPC ID] [Faction ID] - Spawn an NPC by ID from the database with an option of setting its Faction ID", AccountStatus::Steward, command_npctypespawn) || - command_add("nudge", "- Nudge your target's current position by specific values", AccountStatus::QuestTroupe, command_nudge) || + command_add("nudge", "Nudge your target's current position by specific values", AccountStatus::QuestTroupe, command_nudge) || command_add("nukebuffs", "[Beneficial|Detrimental|Help] - Strip all buffs by type on you or your target (no argument to remove all buffs)", AccountStatus::Guide, command_nukebuffs) || command_add("nukeitem", "[Item ID] - Removes the specified Item ID from you or your player target's inventory", AccountStatus::GMLeadAdmin, command_nukeitem) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || @@ -264,11 +234,11 @@ int command_init(void) command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, command_path) || command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || command_add("peqzone", "[Zone ID|Zone Short Name] - Teleports you to the specified zone if you meet the requirements.", AccountStatus::Player, command_peqzone) || - command_add("peqzone_flags", "- displays the PEQZone Flags of you or your target", AccountStatus::Player, command_peqzone_flags) || + command_add("peqzone_flags", "displays the PEQZone Flags of you or your target", AccountStatus::Player, command_peqzone_flags) || command_add("permaclass", "[Class ID] - Change your or your player target's class, changed client is disconnected", AccountStatus::QuestTroupe, command_permaclass) || command_add("permagender", "[Gender ID] - Change your or your player target's gender", AccountStatus::QuestTroupe, command_permagender) || command_add("permarace", "[Race ID] - Change your or your player target's race", AccountStatus::QuestTroupe, command_permarace) || - command_add("petitems", "- View your pet's items if you have one", AccountStatus::ApprenticeGuide, command_petitems) || + command_add("petitems", "View your pet's items if you have one", AccountStatus::ApprenticeGuide, command_petitems) || command_add("petitioninfo", "[petition number] - Get info about a petition", AccountStatus::ApprenticeGuide, command_petitioninfo) || command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", AccountStatus::Player, command_picklock) || command_add("profanity", "Manage censored language.", AccountStatus::GMLeadAdmin, command_profanity) || @@ -279,26 +249,26 @@ int command_init(void) command_add("questerrors", "Shows quest errors.", AccountStatus::GMAdmin, command_questerrors) || command_add("race", "[racenum] - Change your or your target's race. Use racenum 0 to return to normal", AccountStatus::Guide, command_race) || command_add("raidloot", "[All|GroupLeader|RaidLeader|Selected] - Sets your Raid Loot Type if you have permission to do so.", AccountStatus::Player, command_raidloot) || - command_add("randomfeatures", "- Temporarily randomizes the Facial Features of your target", AccountStatus::QuestTroupe, command_randomfeatures) || - command_add("refreshgroup", "- Refreshes Group for you or your player target.", AccountStatus::Player, command_refreshgroup) || + command_add("randomfeatures", "Temporarily randomizes the Facial Features of your target", AccountStatus::QuestTroupe, command_randomfeatures) || + command_add("refreshgroup", "Refreshes Group for you or your player target.", AccountStatus::Player, command_refreshgroup) || command_add("reload", "Reloads different types of server data globally, use no argument for help menu.", AccountStatus::GMMgmt, command_reload) || command_add("removeitem", "[Item ID] [Amount] - Removes the specified Item ID by Amount from you or your player target's inventory (Amount defaults to 1 if not used)", AccountStatus::GMAdmin, command_removeitem) || command_add("repop", "[Force] - Repop the zone with optional force repop", AccountStatus::GMAdmin, command_repop) || - command_add("resetaa", "- Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", AccountStatus::GMMgmt, command_resetaa) || + command_add("resetaa", "Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", AccountStatus::GMMgmt, command_resetaa) || command_add("resetaa_timer", "[All|Timer ID] - Command to reset AA cooldown timers for you or your player target.", AccountStatus::GMMgmt, command_resetaa_timer) || command_add("resetdisc_timer", "[All|Timer ID] - Command to reset discipline timers.", AccountStatus::GMMgmt, command_resetdisc_timer) || command_add("revoke", "[Character Name] [0|1] - Revokes or unrevokes a player's ability to talk in OOC by name (0 = Unrevoke, 1 = Revoke)", AccountStatus::GMMgmt, command_revoke) || command_add("roambox", "[Remove|Set] [Box Size] [Delay (Milliseconds)] - Remove or set an NPC's roambox size and delay", AccountStatus::GMMgmt, command_roambox) || command_add("rules", "(subcommand) - Manage server rules", AccountStatus::GMImpossible, command_rules) || - command_add("save", "- Force your player or player corpse target to be saved to the database", AccountStatus::Guide, command_save) || - command_add("scale", "- Handles npc scaling", AccountStatus::GMLeadAdmin, command_scale) || + command_add("save", "Force your player or player corpse target to be saved to the database", AccountStatus::Guide, command_save) || + command_add("scale", "Handles npc scaling", AccountStatus::GMLeadAdmin, command_scale) || command_add("scribespell", "[spellid] - Scribe specified spell in your target's spell book.", AccountStatus::GMCoder, command_scribespell) || command_add("scribespells", "[max level] [min level] - Scribe all spells for you or your player target that are usable by them, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_scribespells) || - command_add("sendzonespawns", "- Refresh spawn list for all clients in zone", AccountStatus::GMLeadAdmin, command_sendzonespawns) || + command_add("sendzonespawns", "Refresh spawn list for all clients in zone", AccountStatus::GMLeadAdmin, command_sendzonespawns) || command_add("sensetrap", "Analog for ldon sense trap for the newer clients since we still don't have it working.", AccountStatus::Player, command_sensetrap) || - command_add("serverinfo", "- Get CPU, Operating System, and Process Information about the server", AccountStatus::GMMgmt, command_serverinfo) || + command_add("serverinfo", "Get CPU, Operating System, and Process Information about the server", AccountStatus::GMMgmt, command_serverinfo) || command_add("serverlock", "[0|1] - Lock or Unlock the World Server (0 = Unlocked, 1 = Locked)", AccountStatus::GMLeadAdmin, command_serverlock) || - command_add("serverrules", "- Read this server's rules", AccountStatus::Player, command_serverrules) || + command_add("serverrules", "Read this server's rules", AccountStatus::Player, command_serverrules) || command_add("setaapts", "[AA|Group|Raid] [AA Amount] - Set your or your player target's Available AA Points by Type", AccountStatus::GMAdmin, command_setaapts) || command_add("setaaxp", "[AA|Group|Raid] [AA Experience] - Set your or your player target's AA Experience by Type", AccountStatus::GMAdmin, command_setaaxp) || command_add("setadventurepoints", "[Theme] [Points] - Set your or your player target's available Adventure Points by Theme", AccountStatus::GMLeadAdmin, command_set_adventure_points) || @@ -316,26 +286,26 @@ int command_init(void) command_add("setskill", "[skillnum] [value] - Set your target's skill skillnum to value", AccountStatus::Guide, command_setskill) || command_add("setskillall", "[Skill Level] - Set all of your or your target's skills to the specified skill level", AccountStatus::Guide, command_setskillall) || command_add("setstartzone", "[Zone ID|Zone Short Name] - Sets your or your target's starting zone (Use '0' or 'Reset' to allow the player use of /setstartcity)", AccountStatus::QuestTroupe, command_setstartzone) || - command_add("setstat", "- Sets the stats to a specific value.", AccountStatus::Max, command_setstat) || + command_add("setstat", "Sets the stats to a specific value.", AccountStatus::Max, command_setstat) || command_add("setxp", "[value] - Set your or your player target's experience", AccountStatus::GMAdmin, command_setxp) || command_add("showbonusstats", "[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.", AccountStatus::Guide, command_showbonusstats) || - command_add("showbuffs", "- List buffs active on your target or you if no target", AccountStatus::Guide, command_showbuffs) || + command_add("showbuffs", "List buffs active on your target or you if no target", AccountStatus::Guide, command_showbuffs) || command_add("shownumhits", "Shows buffs numhits for yourself.", AccountStatus::Player, command_shownumhits) || command_add("shownpcgloballoot", "Show global loot entries for your target NPC", AccountStatus::Guide, command_shownpcgloballoot) || command_add("showskills", "[Start Skill ID] [All] - Show the values of your or your player target's skills in a popup 50 at a time, use 'all' as second argument to show non-usable skill's values", AccountStatus::Guide, command_showskills) || command_add("showspellslist", "Shows spell list of targeted NPC", AccountStatus::GMAdmin, command_showspellslist) || - command_add("showstats", "- Show details about you or your target", AccountStatus::Guide, command_showstats) || + command_add("showstats", "Show details about you or your target", AccountStatus::Guide, command_showstats) || command_add("showzonegloballoot", "Show global loot entries for your current zone", AccountStatus::Guide, command_showzonegloballoot) || command_add("showzonepoints", "Show zone points for current zone", AccountStatus::Guide, command_showzonepoints) || - command_add("shutdown", "- Shut this zone process down", AccountStatus::GMLeadAdmin, command_shutdown) || + command_add("shutdown", "Shut this zone process down", AccountStatus::GMLeadAdmin, command_shutdown) || command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", AccountStatus::Steward, command_spawn) || command_add("spawneditmass", "Mass editing spawn command", AccountStatus::GMLeadAdmin, command_spawneditmass) || - command_add("spawnfix", "- Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || + command_add("spawnfix", "Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || command_add("spawnstatus", "[All|Disabled|Enabled|Spawn ID] - Show respawn timer status", AccountStatus::GMAdmin, command_spawnstatus) || command_add("spellinfo", "[spellid] - Get detailed info about a spell", AccountStatus::Steward, command_spellinfo) || command_add("stun", "[duration] - Stuns you or your target for duration", AccountStatus::GMAdmin, command_stun) || command_add("summon", "[Character Name] - Summons your corpse, NPC, or player target, or by character name if specified", AccountStatus::QuestTroupe, command_summon) || - command_add("summonburiedplayercorpse", "- Summons the target's oldest buried corpse, if any exist.", AccountStatus::GMAdmin, command_summonburiedplayercorpse) || + command_add("summonburiedplayercorpse", "Summons the target's oldest buried corpse, if any exist.", AccountStatus::GMAdmin, command_summonburiedplayercorpse) || command_add("summonitem", "[itemid] [charges] - Summon an item onto your cursor. Charges are optional.", AccountStatus::GMMgmt, command_summonitem) || command_add("suspend", "[name] [days] [reason] - Suspend by character name and for specificed number of days", AccountStatus::GMLeadAdmin, command_suspend) || command_add("task", "(subcommand) - Task system commands", AccountStatus::GMLeadAdmin, command_task) || @@ -343,36 +313,36 @@ int command_init(void) command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", AccountStatus::GMAdmin, command_petname) || command_add("texture", "[Texture] [Helmet Texture] - Change your or your target's texture (Helmet Texture defaults to 0 if not used)", AccountStatus::Steward, command_texture) || command_add("time", "[Hour] [Minute] - Set world time to specified time", AccountStatus::EQSupport, command_time) || - command_add("timers", "- Display persistent timers for target", AccountStatus::GMMgmt, command_timers) || + command_add("timers", "Display persistent timers for target", AccountStatus::GMMgmt, command_timers) || command_add("timezone", "[Hour] [Minutes] - Set timezone (Minutes are optional)", AccountStatus::EQSupport, command_timezone) || command_add("title", "[Remove|Title] [Save (0 = False, 1 = True)] - Set your or your player target's title (use remove to remove title, Save defaults to false if not used)", AccountStatus::Guide, command_title) || command_add("titlesuffix", "[Remove|Title Suffix] [Save (0 = False, 1 = True)] - Set your or your player target's title suffix (use remove to remove title suffix, Save defaults to false if not used)", AccountStatus::Guide, command_titlesuffix) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_traindisc) || - command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || + command_add("trapinfo", "Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || command_add("tune", "Calculate statistical values related to combat.", AccountStatus::GMAdmin, command_tune) || - command_add("undye", "- Remove dye from all of your or your target's armor slots", AccountStatus::GMAdmin, command_undye) || - command_add("undyeme", "- Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || - command_add("unfreeze", "- Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || + command_add("undye", "Remove dye from all of your or your target's armor slots", AccountStatus::GMAdmin, command_undye) || + command_add("undyeme", "Remove dye from all of your armor slots", AccountStatus::Player, command_undyeme) || + command_add("unfreeze", "Unfreeze your target", AccountStatus::QuestTroupe, command_unfreeze) || command_add("unmemspell", "[Spell ID] - Unmemorize a Spell by ID for you or your target", AccountStatus::Guide, command_unmemspell) || - command_add("unmemspells", " - Unmemorize all spells for you or your target", AccountStatus::Guide, command_unmemspells) || + command_add("unmemspells", " Unmemorize all spells for you or your target", AccountStatus::Guide, command_unmemspells) || command_add("unscribespell", "[Spell ID] - Unscribe a spell from your or your target's spell book by Spell ID", AccountStatus::GMCoder, command_unscribespell) || - command_add("unscribespells", "- Clear out your or your player target's spell book.", AccountStatus::GMCoder, command_unscribespells) || + command_add("unscribespells", "Clear out your or your player target's spell book.", AccountStatus::GMCoder, command_unscribespells) || command_add("untraindisc", "[Spell ID] - Untrain your or your target's discipline by Spell ID", AccountStatus::GMCoder, command_untraindisc) || - command_add("untraindiscs", "- Untrains all disciplines from your target.", AccountStatus::GMCoder, command_untraindiscs) || + command_add("untraindiscs", "Untrains all disciplines from your target.", AccountStatus::GMCoder, command_untraindiscs) || command_add("updatechecksum", "update client checksum", AccountStatus::GMImpossible, command_updatechecksum) || command_add("uptime", "[zone server id] - Get uptime of worldserver, or zone server if argument provided", AccountStatus::Steward, command_uptime) || - command_add("version", "- Display current version of EQEmu server", AccountStatus::Player, command_version) || - command_add("viewcurrencies", "- View your or your target's currencies", AccountStatus::GMAdmin, command_viewcurrencies) || + command_add("version", "Display current version of EQEmu server", AccountStatus::Player, command_version) || + command_add("viewcurrencies", "View your or your target's currencies", AccountStatus::GMAdmin, command_viewcurrencies) || command_add("viewnpctype", "[NPC ID] - Show stats for an NPC by NPC ID", AccountStatus::GMAdmin, command_viewnpctype) || command_add("viewpetition", "[petition number] - View a petition", AccountStatus::ApprenticeGuide, command_viewpetition) || command_add("viewzoneloot", "[item id] - Allows you to search a zone's loot for a specific item ID. (0 shows all loot in the zone)", AccountStatus::QuestTroupe, command_viewzoneloot) || command_add("wc", "[wear slot] [material] - Sends an OP_WearChange for your target", AccountStatus::GMMgmt, command_wc) || command_add("weather", "[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather", AccountStatus::QuestTroupe, command_weather) || command_add("who", "[search]", AccountStatus::ApprenticeGuide, command_who) || - command_add("worldshutdown", "- Shut down world and all zones", AccountStatus::GMMgmt, command_worldshutdown) || + command_add("worldshutdown", "Shut down world and all zones", AccountStatus::GMMgmt, command_worldshutdown) || command_add("wp", "[add|delete] [grid_id] [pause] [waypoint_id] [-h] - Add or delete a waypoint by grid ID. (-h to use current heading)", AccountStatus::GMAreas, command_wp) || command_add("wpadd", "[pause] [-h] - Add your current location as a waypoint to your NPC target's AI path. (-h to use current heading)", AccountStatus::GMAreas, command_wpadd) || - command_add("wpinfo", "- Show waypoint info about your NPC target", AccountStatus::GMAreas, command_wpinfo) || + command_add("wpinfo", "Show waypoint info about your NPC target", AccountStatus::GMAreas, command_wpinfo) || command_add("worldwide", "Performs world-wide GM functions such as cast (can be extended for other commands). Use caution", AccountStatus::GMImpossible, command_worldwide) || command_add("xtargets", "Show your targets Extended Targets and optionally set how many xtargets they can have.", AccountStatus::GMImpossible, command_xtargets) || command_add("zclip", "[Minimum Clip] [Maximum Clip] [Fog Minimum Clip] [Fog Maximum Clip] [Permanent (0 = False, 1 = True)] - Change zone clipping", AccountStatus::QuestTroupe, command_zclip) || @@ -383,12 +353,12 @@ int command_init(void) command_add("zoneinstance", "[Instance ID] [X] [Y] [Z] - Teleport to specified Instance by ID (coordinates are optional)", AccountStatus::Guide, command_zone_instance) || command_add("zonelock", "[List|Lock|Unlock] [Zone ID|Zone Short Name] - Set or get lock status of a Zone by ID or Short Name", AccountStatus::GMAdmin, command_zonelock) || command_add("zoneshutdown", "[shortname] - Shut down a zone server", AccountStatus::GMLeadAdmin, command_zoneshutdown) || - command_add("zonestatus", "- Show connected zoneservers, synonymous with /servers", AccountStatus::GMLeadAdmin, command_zonestatus) || + command_add("zonestatus", "Show connected zoneservers, synonymous with /servers", AccountStatus::GMLeadAdmin, command_zonestatus) || command_add("zopp", "Troubleshooting command - Sends a fake item packet to you. No server reference is created.", AccountStatus::GMImpossible, command_zopp) || command_add("zsafecoords", "[X] [Y] [Z] [Heading] [Permanent (0 = False, 1 = True)] - Set the current zone's safe coordinates", AccountStatus::QuestTroupe, command_zsafecoords) || - command_add("zsave", " - Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) || + command_add("zsave", " Saves zheader to the database", AccountStatus::QuestTroupe, command_zsave) || command_add("zsky", "[Sky Type] [Permanent (0 = False, 1 = True)] - Change zone sky type", AccountStatus::QuestTroupe, command_zsky) || - command_add("zstats", "- Show info about zone header", AccountStatus::QuestTroupe, command_zstats) || + command_add("zstats", "Show info about zone header", AccountStatus::QuestTroupe, command_zstats) || command_add("zunderworld", "[Z] [Permanent (0 = False, 1 = True)] - Change zone underworld Z", AccountStatus::QuestTroupe, command_zunderworld) ) { command_deinit(); @@ -401,15 +371,13 @@ int command_init(void) std::vector> injected_command_settings; std::vector orphaned_command_settings; - for (auto cs_iter : command_settings) { - - auto cl_iter = commandlist.find(cs_iter.first); - if (cl_iter == commandlist.end()) { - - orphaned_command_settings.push_back(cs_iter.first); + for (const auto& cs : command_settings) { + auto cl = commandlist.find(cs.first); + if (cl == commandlist.end()) { + orphaned_command_settings.push_back(cs.first); LogInfo( "Command [{}] no longer exists... Deleting orphaned entry from `command_settings` table...", - cs_iter.first.c_str() + cs.first ); } } @@ -421,60 +389,58 @@ int command_init(void) } auto working_cl = commandlist; - for (auto working_cl_iter : working_cl) { - - auto cs_iter = command_settings.find(working_cl_iter.first); - if (cs_iter == command_settings.end()) { - - injected_command_settings.push_back(std::pair(working_cl_iter.first, working_cl_iter.second->access)); + for (const auto& w : working_cl) { + auto cs = command_settings.find(w.first); + if (cs == command_settings.end()) { + injected_command_settings.push_back(std::pair(w.first, w.second->admin)); LogInfo( - "New Command [{}] found... Adding to `command_settings` table with access [{}]...", - working_cl_iter.first.c_str(), - working_cl_iter.second->access + "New Command [{}] found... Adding to `command_settings` table with admin [{}]...", + w.first, + w.second->admin ); - if (working_cl_iter.second->access == 0) { + if (w.second->admin == AccountStatus::Player) { LogCommands( - "command_init(): Warning: Command [{}] defaulting to access level 0!", - working_cl_iter.first.c_str() + "command_init(): Warning: Command [{}] defaulting to admin level 0!", + w.first ); } continue; } - working_cl_iter.second->access = cs_iter->second.first; + w.second->admin = cs->second.first; LogCommands( - "command_init(): - Command [{}] set to access level [{}]", - working_cl_iter.first.c_str(), - cs_iter->second.first + "command_init(): - Command [{}] set to admin level [{}]", + w.first, + cs->second.first ); - if (cs_iter->second.second.empty()) { + if (cs->second.second.empty()) { continue; } - for (auto alias_iter : cs_iter->second.second) { - if (alias_iter.empty()) { + for (const auto& a : cs->second.second) { + if (a.empty()) { continue; } - if (commandlist.find(alias_iter) != commandlist.end()) { + if (commandlist.find(a) != commandlist.end()) { LogCommands( "command_init(): Warning: Alias [{}] already exists as a command - skipping!", - alias_iter.c_str() + a ); continue; } - commandlist[alias_iter] = working_cl_iter.second; - commandaliases[alias_iter] = working_cl_iter.first; + commandlist[a] = w.second; + commandaliases[a] = w.first; LogCommands( "command_init(): - Alias [{}] added to command [{}]", - alias_iter.c_str(), - commandaliases[alias_iter].c_str() + a, + commandaliases[a] ); } } @@ -487,7 +453,7 @@ int command_init(void) command_dispatch = command_realdispatch; - return commandcount; + return command_count; } /* @@ -504,7 +470,7 @@ void command_deinit(void) commandaliases.clear(); command_dispatch = command_notavail; - commandcount = 0; + command_count = 0; } /* @@ -512,42 +478,46 @@ void command_deinit(void) * adds a command to the command list; used by command_init * * Parameters: - * command_name - the command ex: "spawn" - * desc - text description of command for #help - * access - default access level required to use command - * function - pointer to function that handles command + * command_name - the command ex: "spawn" + * description - text description of command for #help + * admin - default admin level required to use command + * function - pointer to function that handles command * */ -int command_add(std::string command_name, const char *desc, int access, CmdFuncPtr function) +int command_add(std::string command_name, std::string description, uint8 admin, CmdFuncPtr function) { if (command_name.empty()) { LogError("command_add() - Command added with empty name string - check command.cpp"); return -1; } - if (function == nullptr) { - LogError("command_add() - Command [{}] added without a valid function pointer - check command.cpp", command_name.c_str()); + + if (!function) { + LogError("command_add() - Command [{}] added without a valid function pointer - check command.cpp", command_name); return -1; } - if (commandlist.count(command_name) != 0) { - LogError("command_add() - Command [{}] is a duplicate command name - check command.cpp", command_name.c_str()); + + if (commandlist.count(command_name)) { + LogError("command_add() - Command [{}] is a duplicate command name - check command.cpp", command_name); return -1; } - for (auto iter = commandlist.begin(); iter != commandlist.end(); ++iter) { - if (iter->second->function != function) + + for (const auto& c : commandlist) { + if (c.second->function != function) { continue; - LogError("command_add() - Command [{}] equates to an alias of [{}] - check command.cpp", command_name.c_str(), iter->first.c_str()); + } + + LogError("command_add() - Command [{}] equates to an alias of [{}] - check command.cpp", command_name, c.first); return -1; } auto c = new CommandRecord; - c->access = access; - c->desc = desc; + c->admin = admin; + c->description = description; c->function = function; commandlist[command_name] = c; commandaliases[command_name] = command_name; - cleanup_commandlist.Append(c); - commandcount++; + command_count++; return 0; } @@ -564,75 +534,121 @@ int command_add(std::string command_name, const char *desc, int access, CmdFuncP * message - what the client typed * */ -int command_realdispatch(Client *c, const char *message) +int command_realdispatch(Client *c, std::string message) { - Seperator sep(message, ' ', 10, 100, true); // "three word argument" should be considered 1 arg + Seperator sep(message.c_str(), ' ', 10, 100, true); // "three word argument" should be considered 1 arg - command_logcommand(c, message); + command_logcommand(c, message.c_str()); - std::string cstr(sep.arg[0]+1); + std::string cstr(sep.arg[0] + 1); - if(commandlist.count(cstr) != 1) { - return(-2); + if (commandlist.count(cstr) != 1) { + return -2; } - CommandRecord *cur = commandlist[cstr]; - if(c->Admin() < cur->access){ - c->Message(Chat::Red,"Your access level is not high enough to use this command."); - return(-1); + auto cur = commandlist[cstr]; + if (c->Admin() < cur->admin) { + c->Message(Chat::White, "Your status is not high enough to use this command."); + return -1; } /* QS: Player_Log_Issued_Commands */ - if (RuleB(QueryServ, PlayerLogIssuedCommandes)){ - std::string event_desc = StringFormat("Issued command :: '%s' in zoneid:%i instid:%i", message, c->GetZoneID(), c->GetInstanceID()); + if (RuleB(QueryServ, PlayerLogIssuedCommandes)) { + auto event_desc = fmt::format( + "Issued command :: '{}' in Zone ID: {} Instance ID: {}", + message, + c->GetZoneID(), + c->GetInstanceID() + ); QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); } - if(cur->access >= COMMANDS_LOGGING_MIN_STATUS) { - LogCommands("[{}] ([{}]) used command: [{}] (target=[{}])", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); + if (cur->admin >= COMMANDS_LOGGING_MIN_STATUS) { + LogCommands( + "[{}] ([{}]) used command: [{}] (target=[{}])", + c->GetName(), + c->AccountName(), + message, + c->GetTarget() ? c->GetTarget()->GetName() : "NONE" + ); } - if(cur->function == nullptr) { - LogError("Command [{}] has a null function\n", cstr.c_str()); - return(-1); - } else { - //dispatch C++ command - cur->function(c, &sep); // dispatch command + if (!cur->function) { + LogError("Command [{}] has a null function", cstr); + return -1; } + + cur->function(c, &sep); // Dispatch C++ Command + return 0; - } void command_help(Client *c, const Seperator *sep) { - int commands_shown=0; + int found_count = 0; + std::string command_link; + std::string search_criteria = str_tolower(sep->argplus[1]); - c->Message(Chat::White, "Available EQEMu commands:"); - - std::map::iterator cur,end; - cur = commandlist.begin(); - end = commandlist.end(); - - for(; cur != end; ++cur) { - if(sep->arg[1][0]) { - if(cur->first.find(sep->arg[1]) == std::string::npos) { + for (const auto& cur : commandlist) { + if (!search_criteria.empty()) { + if (cur.first.find(search_criteria) == std::string::npos) { continue; } } - if(c->Admin() < cur->second->access) + if (c->Admin() < cur.second->admin) { continue; - commands_shown++; - c->Message(Chat::White, " %c%s %s", COMMAND_CHAR, cur->first.c_str(), cur->second->desc == nullptr?"":cur->second->desc); + } + + command_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "{}{}", + COMMAND_CHAR, + cur.first + ), + false, + fmt::format( + "{}{}", + COMMAND_CHAR, + cur.first + ) + ); + + c->Message( + Chat::White, + fmt::format( + "{} | {}", + command_link, + !cur.second->description.empty() ? cur.second->description : "" + ).c_str() + ); + + found_count++; } + if (parse->PlayerHasQuestSub(EVENT_COMMAND)) { - int i = parse->EventPlayer(EVENT_COMMAND, c, sep->msg, 0); - if (i >= 1) { - commands_shown += i; + auto event_parse = parse->EventPlayer(EVENT_COMMAND, c, sep->msg, 0); + if (event_parse >= 1) { + found_count += event_parse; } } - c->Message(Chat::White, "%d command%s listed.", commands_shown, commands_shown!=1?"s":""); + c->Message( + Chat::White, + fmt::format( + "{} Command{} listed{}.", + found_count, + found_count != 1 ? "s" : "", + ( + !search_criteria.empty() ? + fmt::format( + " matching '{}'", + search_criteria + ) : + "" + ) + ).c_str() + ); } void command_spawneditmass(Client *c, const Seperator *sep) @@ -767,34 +783,96 @@ void command_spawneditmass(Client *c, const Seperator *sep) void command_findaliases(Client *c, const Seperator *sep) { - if (!sep->arg[1][0]) { - c->Message(Chat::White, "Usage: #findaliases [alias | command]"); + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #findaliases [Search Critera]"); return; } - auto find_iter = commandaliases.find(sep->arg[1]); + std::string search_criteria = str_tolower(sep->argplus[1]); + + auto find_iter = commandaliases.find(search_criteria); if (find_iter == commandaliases.end()) { - c->Message(Chat::Yellow, "No commands or aliases match '%s'", sep->arg[1]); + c->Message( + Chat::White, + fmt::format( + "No commands or aliases found matching '{}'.", + search_criteria + ).c_str() + ); return; } auto command_iter = commandlist.find(find_iter->second); - if (find_iter->second.empty() || command_iter == commandlist.end()) { - c->Message(Chat::White, "An unknown condition occurred..."); + if ( + find_iter->second.empty() || + command_iter == commandlist.end() + ) { + c->Message(Chat::White, "An unknown condition occurred."); return; } - c->Message(Chat::White, "Available command aliases for '%s':", command_iter->first.c_str()); + auto current_commmand_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "{}{}", + COMMAND_CHAR, + command_iter->first + ), + false, + fmt::format( + "{}{}", + COMMAND_CHAR, + command_iter->first + ) + ); - int commandaliasesshown = 0; - for (auto alias_iter = commandaliases.begin(); alias_iter != commandaliases.end(); ++alias_iter) { - if (strcasecmp(find_iter->second.c_str(), alias_iter->second.c_str()) || c->Admin() < command_iter->second->access) + int alias_count = 0; + int alias_number = 1; + std::string alias_link; + for (const auto& a : commandaliases) { + if ( + find_iter->second != a.second || + c->Admin() < command_iter->second->admin + ) { continue; + } - c->Message(Chat::White, "%c%s", COMMAND_CHAR, alias_iter->first.c_str()); - ++commandaliasesshown; + alias_link = EQ::SayLinkEngine::GenerateQuestSaylink( + fmt::format( + "{}{}", + COMMAND_CHAR, + a.first + ), + false, + fmt::format( + "{}{}", + COMMAND_CHAR, + a.first + ) + ); + + c->Message( + Chat::White, + fmt::format( + "Alias {} | {}", + alias_number, + alias_link + ).c_str() + ); + + alias_count++; + alias_number++; } - c->Message(Chat::White, "%d command alias%s listed.", commandaliasesshown, commandaliasesshown != 1 ? "es" : ""); + + c->Message( + Chat::White, + fmt::format( + "{} Alias{} listed for {}.", + alias_count, + alias_count != 1 ? "es" : "", + current_commmand_link + ).c_str() + ); } void command_hotfix(Client *c, const Seperator *sep) diff --git a/zone/command.h b/zone/command.h index 884a85d65..1e3641096 100644 --- a/zone/command.h +++ b/zone/command.h @@ -5,29 +5,30 @@ class Client; class Seperator; #include "../common/types.h" +#include #define COMMAND_CHAR '#' typedef void (*CmdFuncPtr)(Client *, const Seperator *); typedef struct { - int access; - const char *desc; // description of command - CmdFuncPtr function; // null means perl function -} CommandRecord; + uint8 admin; + std::string description; + CmdFuncPtr function; // null means perl function +} CommandRecord; -extern int (*command_dispatch)(Client *, char const *); -extern int commandcount; // number of commands loaded +extern int (*command_dispatch)(Client *,std::string); +extern int command_count; // Commands Loaded Count -// the command system: +// Command Utilities int command_init(void); void command_deinit(void); -int command_add(std::string command_name, const char *desc, int access, CmdFuncPtr function); -int command_notavail(Client *c, const char *message); -int command_realdispatch(Client *c, char const *message); -void command_logcommand(Client *c, const char *message); +int command_add(std::string command_name, std::string description, uint8 admin, CmdFuncPtr function); +int command_notavail(Client *c, std::string message); +int command_realdispatch(Client *c, std::string message); +void command_logcommand(Client *c, std::string message); -//commands +// Commands void command_acceptrules(Client *c, const Seperator *sep); void command_advnpcspawn(Client *c, const Seperator *sep); void command_aggro(Client *c, const Seperator *sep); @@ -107,7 +108,6 @@ void command_gmzone(Client *c, const Seperator *sep); void command_goto(Client *c, const Seperator *sep); void command_grid(Client *c, const Seperator *sep); void command_guild(Client *c, const Seperator *sep); -bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char *what, const char *value); void command_guildapprove(Client *c, const Seperator *sep); void command_guildcreate(Client *c, const Seperator *sep); void command_guildlist(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/logcommand.cpp b/zone/gm_commands/logcommand.cpp index 3b85e2ff3..33fe83516 100755 --- a/zone/gm_commands/logcommand.cpp +++ b/zone/gm_commands/logcommand.cpp @@ -1,85 +1,89 @@ #include "../client.h" -void command_logcommand(Client *c, const char *message) +void command_logcommand(Client *c, std::string message) { int admin = c->Admin(); - bool continueevents = false; + bool log = false; switch (zone->loglevelvar) { //catch failsafe case 9: { // log only LeadGM if ( admin >= AccountStatus::GMLeadAdmin && admin < AccountStatus::GMMgmt - ) { - continueevents = true; + ) { + log = true; } + break; } case 8: { // log only GM if ( admin >= AccountStatus::GMAdmin && admin < AccountStatus::GMLeadAdmin - ) { - continueevents = true; + ) { + log = true; } + break; } case 1: { if (admin >= AccountStatus::GMMgmt) { - continueevents = true; + log = true; } + break; } case 2: { if (admin >= AccountStatus::GMLeadAdmin) { - continueevents = true; + log = true; } + break; } case 3: { if (admin >= AccountStatus::GMAdmin) { - continueevents = true; + log = true; } + break; } case 4: { if (admin >= AccountStatus::QuestTroupe) { - continueevents = true; + log = true; } + break; } case 5: { if (admin >= AccountStatus::ApprenticeGuide) { - continueevents = true; + log = true; } + break; } case 6: { if (admin >= AccountStatus::Steward) { - continueevents = true; + log = true; } + break; } case 7: { - continueevents = true; + log = true; break; } } - if (continueevents) { + if (log) { database.logevents( c->AccountName(), c->AccountID(), - admin, c->GetName(), + admin, + c->GetName(), c->GetTarget() ? c->GetTarget()->GetName() : "None", "Command", - message, + message.c_str(), 1 ); } } - - -/* - * commands go below here - */ From aaaee6c6a4baec132e7b37f89c3658bab7b519aa Mon Sep 17 00:00:00 2001 From: Paul Coene Date: Fri, 27 May 2022 15:30:38 -0400 Subject: [PATCH 030/552] [Bug Fix] IsDamage test for lifetap was not complete. (#2213) * [Bug Fix] IsDamage test for lifetap was not complete. * Fix magic # and formatting as per Kingly * Added #define --- common/spdat.cpp | 20 ++++++++++++-------- common/spdat.h | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 2c22bcd33..b3bb811d1 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -101,14 +101,18 @@ bool IsSacrificeSpell(uint16 spell_id) bool IsLifetapSpell(uint16 spell_id) { - // Ancient Lifebane: 2115 - if (IsValidSpell(spell_id) && - (spells[spell_id].target_type == ST_Tap || - spells[spell_id].target_type == ST_TargetAETap || - spell_id == 2115)) - return true; + if ( + IsValidSpell(spell_id) && + ( + spells[spell_id].target_type == ST_Tap || + spells[spell_id].target_type == ST_TargetAETap || + spell_id == SPELL_ANCIENT_LIFEBANE + ) + ) { + return true; + } - return false; + return false; } bool IsMezSpell(uint16 spell_id) @@ -139,7 +143,7 @@ bool IsEvacSpell(uint16 spellid) bool IsDamageSpell(uint16 spellid) { - if (spells[spellid].target_type == ST_Tap) + if (IsLifetapSpell(spellid)) return false; for (int o = 0; o < EFFECT_COUNT; o++) { diff --git a/common/spdat.h b/common/spdat.h index 3a0d1636a..0159fe52c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -169,6 +169,7 @@ #define SPELL_ILLUSION_FEMALE 1731 #define SPELL_ILLUSION_MALE 1732 #define SPELL_UNSUMMON_SELF 892 +#define SPELL_ANCIENT_LIFEBANE 2115 //spellgroup ids #define SPELLGROUP_FRENZIED_BURNOUT 2754 From 7de50d0e6022190dd4e0bbd522a54b35097e76b0 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Fri, 27 May 2022 23:57:55 -0400 Subject: [PATCH 031/552] [Bug Fix] Fix IP Exemptions. (#2189) * [Bug Fix] Fix IP Exemptions. - IP Exemptions were broken due to GetAccountID() returning 0 in logic somehow, resolved this by setting a variable to GetAccountID() at the beginning of the method. - Fixed weird IP messages where the long form of IP was displayed instead of the string form. - Fixes edge case where IP rule may be set to -1 and this will make anyone get instantly kicked if IP Exemptions were enabled as their IP Count would always be greater than -1. * Update client.cpp * Update client.cpp * Update clientlist.cpp * Update clientlist.cpp --- common/database.cpp | 112 +++++++++++----------- common/database.h | 6 +- world/client.cpp | 219 ++++++++++++++++++++++--------------------- world/clientlist.cpp | 146 +++++++++++++++++++---------- world/clientlist.h | 4 +- world/main.cpp | 3 +- zone/main.cpp | 3 +- 7 files changed, 267 insertions(+), 226 deletions(-) diff --git a/common/database.cpp b/common/database.cpp index b14e8009b..c492f975a 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -1469,41 +1469,25 @@ uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 return base_cap; } -uint32 Database::GetCharacterInfo( - const char *iName, - uint32 *oAccID, - uint32 *oZoneID, - uint32 *oInstanceID, - float *oX, - float *oY, - float *oZ -) +uint32 Database::GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id) { - std::string query = StringFormat( - "SELECT `id`, `account_id`, `zone_id`, `zone_instance`, `x`, `y`, `z` FROM `character_data` WHERE `name` = '%s'", - EscapeString(iName).c_str() + auto query = fmt::format( + "SELECT `id`, `account_id`, `zone_id`, `zone_instance` FROM `character_data` WHERE `name` = '{}'", + EscapeString(character_name) ); auto results = QueryDatabase(query); - - if (!results.Success()) { + if (!results.Success() || !results.RowCount()) { return 0; } - if (results.RowCount() != 1) { - return 0; - } + auto row = results.begin(); + auto character_id = std::stoul(row[0]); + *account_id = std::stoul(row[1]); + *zone_id = std::stoul(row[2]); + *instance_id = std::stoul(row[3]); - auto row = results.begin(); - uint32 charid = atoi(row[0]); - if (oAccID) { *oAccID = atoi(row[1]); } - if (oZoneID) { *oZoneID = atoi(row[2]); } - if (oInstanceID) { *oInstanceID = atoi(row[3]); } - if (oX) { *oX = atof(row[4]); } - if (oY) { *oY = atof(row[5]); } - if (oZ) { *oZ = atof(row[6]); } - - return charid; + return character_id; } bool Database::UpdateLiveChar(char* charname, uint32 account_id) { @@ -1627,29 +1611,36 @@ uint32 Database::GetGroupID(const char* name){ return atoi(row[0]); } -/* Is this really getting used properly... A half implementation ? Akkadius */ -char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf) { - strcpy(leaderbuf, ""); +std::string Database::GetGroupLeaderForLogin(std::string character_name) { uint32 group_id = 0; - std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name` = '%s'", name); + auto query = fmt::format( + "SELECT `groupid` FROM `group_id` WHERE `name` = '{}'", + character_name + ); auto results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) - if (row[0]) - group_id = atoi(row[0]); + if (results.Success() && results.RowCount()) { + auto row = results.begin(); + group_id = std::stoul(row[0]); + } - if (group_id == 0) - return leaderbuf; + if (!group_id) { + return std::string(); + } - query = StringFormat("SELECT `leadername` FROM `group_leaders` WHERE `gid` = '%u' LIMIT 1", group_id); + query = fmt::format( + "SELECT `leadername` FROM `group_leaders` WHERE `gid` = {} LIMIT 1", + group_id + ); results = QueryDatabase(query); - for (auto row = results.begin(); row != results.end(); ++row) - if (row[0]) - strcpy(leaderbuf, row[0]); + if (results.Success() && results.RowCount()) { + auto row = results.begin(); + return row[0]; + } - return leaderbuf; + return std::string(); } void Database::SetGroupLeaderName(uint32 gid, const char* name) { @@ -2244,28 +2235,32 @@ bool Database::SaveTime(int8 minute, int8 hour, int8 day, int8 month, int16 year } int Database::GetIPExemption(std::string account_ip) { - std::string query = StringFormat("SELECT `exemption_amount` FROM `ip_exemptions` WHERE `exemption_ip` = '%s'", account_ip.c_str()); - auto results = QueryDatabase(query); - - if (results.Success() && results.RowCount() > 0) { - auto row = results.begin(); - return atoi(row[0]); - } - - return RuleI(World, MaxClientsPerIP); -} - -void Database::SetIPExemption(std::string account_ip, int exemption_amount) { - std::string query = fmt::format( - "SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'", + auto query = fmt::format( + "SELECT `exemption_amount` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'", account_ip ); auto results = QueryDatabase(query); + if (!results.Success() || !results.RowCount()) { + return RuleI(World, MaxClientsPerIP); + } + + auto row = results.begin(); + return std::stoi(row[0]); +} + +void Database::SetIPExemption(std::string account_ip, int exemption_amount) { + auto query = fmt::format( + "SELECT `exemption_id` FROM `ip_exemptions` WHERE `exemption_ip` = '{}'", + account_ip + ); + uint32 exemption_id = 0; - if (results.Success() && results.RowCount() > 0) { + + auto results = QueryDatabase(query); + if (results.Success() && results.RowCount()) { auto row = results.begin(); - exemption_id = atoi(row[0]); + exemption_id = std::stoul(row[0]); } query = fmt::format( @@ -2274,13 +2269,14 @@ void Database::SetIPExemption(std::string account_ip, int exemption_amount) { exemption_amount ); - if (exemption_id != 0) { + if (exemption_id) { query = fmt::format( "UPDATE `ip_exemptions` SET `exemption_amount` = {} WHERE `exemption_ip` = '{}'", exemption_amount, account_ip ); } + QueryDatabase(query); } diff --git a/common/database.h b/common/database.h index b11c07671..9a1933b33 100644 --- a/common/database.h +++ b/common/database.h @@ -131,7 +131,7 @@ public: uint32 GetAccountIDByChar(uint32 char_id); uint32 GetAccountIDByName(std::string account_name, std::string loginserver, int16* status = 0, uint32* lsid = 0); uint32 GetCharacterID(const char *name); - uint32 GetCharacterInfo(const char* iName, uint32* oAccID = 0, uint32* oZoneID = 0, uint32* oInstanceID = 0, float* oX = 0, float* oY = 0, float* oZ = 0); + uint32 GetCharacterInfo(std::string character_name, uint32 *account_id, uint32 *zone_id, uint32 *instance_id); uint32 GetGuildIDByCharID(uint32 char_id); uint32 GetGroupIDByCharID(uint32 char_id); uint32 GetRaidIDByCharID(uint32 char_id); @@ -207,8 +207,8 @@ public: /* Groups */ - char* GetGroupLeaderForLogin(const char* name,char* leaderbuf); - char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); + std::string GetGroupLeaderForLogin(std::string character_name); + char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); uint32 GetGroupID(const char* name); diff --git a/world/client.cpp b/world/client.cpp index 50689520c..555b0f24c 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -721,38 +721,40 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { } bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { - if (GetAccountID() == 0) { - LogInfo("Enter world with no logged in account"); + auto account_id = GetAccountID(); + if (!account_id) { + LogInfo("Enter world with no logged in account."); eqs->Close(); return true; } - if(GetAdmin() < 0) - { - LogInfo("Account banned or suspended"); + if (GetAdmin() < 0) { + LogInfo("Account [{}] is banned or suspended.", account_id); eqs->Close(); return true; } - if (RuleB(World, EnableIPExemptions) || RuleI(World, MaxClientsPerIP) >= 0) { + if ( + RuleB(World, EnableIPExemptions) || + RuleI(World, MaxClientsPerIP) > 0 + ) { client_list.GetCLEIP(GetIP()); //Check current CLE Entry IPs against incoming connection } - EnterWorld_Struct *ew=(EnterWorld_Struct *)app->pBuffer; - strn0cpy(char_name, ew->name, 64); + auto ew = (EnterWorld_Struct *) app->pBuffer; + strn0cpy(char_name, ew->name, sizeof(char_name)); - EQApplicationPacket *outapp; - uint32 tmpaccid = 0; - charid = database.GetCharacterInfo(char_name, &tmpaccid, &zone_id, &instance_id); - if (charid == 0) { + uint32 temporary_account_id = 0; + charid = database.GetCharacterInfo(char_name, &temporary_account_id, &zone_id, &instance_id); + if (!charid) { LogInfo("Could not get CharInfo for [{}]", char_name); eqs->Close(); return true; } // Make sure this account owns this character - if (tmpaccid != GetAccountID()) { - LogInfo("This account does not own the character named [{}]", char_name); + if (temporary_account_id != account_id) { + LogInfo("Account [{}] does not own the character named [{}] from account [{}]", account_id, char_name, temporary_account_id); eqs->Close(); return true; } @@ -761,25 +763,26 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { // (This is a literal translation of the original process..I don't see why it can't be changed to a single-target query over account iteration) if (!is_player_zoning) { size_t character_limit = EQ::constants::StaticLookup(eqs->ClientVersion())->CharacterCreationLimit; - if (character_limit > EQ::constants::CHARACTER_CREATION_LIMIT) { character_limit = EQ::constants::CHARACTER_CREATION_LIMIT; } - if (eqs->ClientVersion() == EQ::versions::ClientVersion::Titanium) { character_limit = Titanium::constants::CHARACTER_CREATION_LIMIT; } + if (character_limit > EQ::constants::CHARACTER_CREATION_LIMIT) { + character_limit = EQ::constants::CHARACTER_CREATION_LIMIT; + } - std::string tgh_query = StringFormat( - "SELECT " - "`id`, " - "name, " - "`level`, " - "last_login " - "FROM " - "character_data " - "WHERE `account_id` = %i ORDER BY `name` LIMIT %u", GetAccountID(), character_limit); - auto tgh_results = database.QueryDatabase(tgh_query); + if (eqs->ClientVersion() == EQ::versions::ClientVersion::Titanium) { + character_limit = Titanium::constants::CHARACTER_CREATION_LIMIT; + } + + auto query = fmt::format( + "SELECT `id`, `name`, `level`, `last_login` FROM character_data WHERE `account_id` = {} ORDER BY `name` LIMIT {}", + account_id, + character_limit + ); + auto results = database.QueryDatabase(query); /* Check GoHome */ if (ew->return_home && !ew->tutorial) { bool home_enabled = false; - for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) { - if (strcasecmp(row[1], char_name) == 0) { + for (auto row : results) { + if (!strcasecmp(row[1], char_name)) { if (RuleB(World, EnableReturnHomeButton)) { int now = time(nullptr); if ((now - atoi(row[3])) >= RuleI(World, MinOfflineTimeToReturnHome)) { @@ -792,9 +795,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if (home_enabled) { zone_id = database.MoveCharacterToBind(charid, 4); - } - else { - LogInfo("[{}] is trying to go home before they're able", char_name); + } else { + LogInfo("[{}] is trying to go home before they're able.", char_name); database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); eqs->Close(); return true; @@ -804,9 +806,12 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { /* Check Tutorial*/ if (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial)) { bool tutorial_enabled = false; - for (auto row = tgh_results.begin(); row != tgh_results.end(); ++row) { - if (strcasecmp(row[1], char_name) == 0) { - if (RuleB(World, EnableTutorialButton) && ((uint8)atoi(row[2]) <= RuleI(World, MaxLevelForTutorial))) { + for (auto row : results) { + if (!strcasecmp(row[1], char_name)) { + if ( + RuleB(World, EnableTutorialButton) && + std::stoi(row[2]) <= RuleI(World, MaxLevelForTutorial) + ) { tutorial_enabled = true; break; } @@ -816,9 +821,8 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if (tutorial_enabled) { zone_id = RuleI(World, TutorialZoneID); database.MoveCharacterToZone(charid, zone_id); - } - else { - LogInfo("[{}] is trying to go to tutorial but are not allowed", char_name); + } else { + LogInfo("[{}] is trying to go to the Tutorial but they are not allowed.", char_name); database.SetHackerFlag(GetAccountName(), char_name, "MQTutorial: player tried to enter the tutorial without having tutorial enabled for this character."); eqs->Close(); return true; @@ -826,17 +830,17 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { } } - if (zone_id == 0 || !ZoneName(zone_id)) { + if (!zone_id || !ZoneName(zone_id)) { // This is to save people in an invalid zone, once it's removed from the DB database.MoveCharacterToZone(charid, ZoneID("arena")); - LogInfo("Zone not found in database zone_id=[{}], moveing char to arena character:[{}]", zone_id, char_name); + LogInfo("Zone [{}] not found, moving [{}] to Arena.", zone_id, char_name); } - if(instance_id > 0) - { - if (!database.VerifyInstanceAlive(instance_id, GetCharID()) || - !database.VerifyZoneInstance(zone_id, instance_id)) - { + if (instance_id) { + if ( + !database.VerifyInstanceAlive(instance_id, GetCharID()) || + !database.VerifyZoneInstance(zone_id, instance_id) + ) { zone_id = database.MoveCharacterToInstanceSafeReturn(charid, zone_id, instance_id); instance_id = 0; } @@ -845,83 +849,80 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if(!is_player_zoning) { database.SetGroupID(char_name, 0, charid); database.SetLoginFlags(charid, false, false, 1); - } - else{ - uint32 groupid = database.GetGroupID(char_name); - if(groupid > 0){ - char* leader = 0; - char leaderbuf[64] = {0}; - if((leader = database.GetGroupLeaderForLogin(char_name, leaderbuf)) && strlen(leader)>1){ - auto outapp3 = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); - GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer; + } else { + auto group_id = database.GetGroupID(char_name); + if (group_id) { + auto leader_name = database.GetGroupLeaderForLogin(char_name); + if (!leader_name.empty()) { + auto pack = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + auto gj = (GroupJoin_Struct*) pack->pBuffer; gj->action=8; - strcpy(gj->yourname, char_name); - strcpy(gj->membername, leader); - QueuePacket(outapp3); - safe_delete(outapp3); + strn0cpy(gj->yourname, char_name, sizeof(gj->yourname)); + strn0cpy(gj->membername, leader_name.c_str(), sizeof(gj->membername)); + QueuePacket(pack); + safe_delete(pack); } } } - outapp = new EQApplicationPacket(OP_MOTD); - std::string tmp; - if (database.GetVariable("MOTD", tmp)) { - outapp->size = tmp.length() + 1; + auto outapp = new EQApplicationPacket(OP_MOTD); + std::string motd_message; + if (database.GetVariable("MOTD", motd_message)) { + outapp->size = motd_message.length() + 1; outapp->pBuffer = new uchar[outapp->size]; - memset(outapp->pBuffer,0,outapp->size); - strcpy((char*)outapp->pBuffer, tmp.c_str()); - - } else { - // Null Message of the Day. :) + memset(outapp->pBuffer, 0, outapp->size); + strcpy((char*)outapp->pBuffer, motd_message.c_str()); + } else { // Null Message of the Day. :) outapp->size = 1; outapp->pBuffer = new uchar[outapp->size]; outapp->pBuffer[0] = 0; } + QueuePacket(outapp); safe_delete(outapp); // set mailkey - used for duration of character session - int MailKey = emu_random.Int(1, INT_MAX); + int mail_key = emu_random.Int(1, INT_MAX); - database.SetMailKey(charid, GetIP(), MailKey); + database.SetMailKey(charid, GetIP(), mail_key); if (UCSServerAvailable_) { - const WorldConfig *Config = WorldConfig::get(); + auto config = WorldConfig::get(); std::string buffer; - EQ::versions::UCSVersion ConnectionType = EQ::versions::ucsUnknown; + auto connection_type = EQ::versions::ucsUnknown; // chat server packet switch (GetClientVersion()) { - case EQ::versions::ClientVersion::Titanium: - ConnectionType = EQ::versions::ucsTitaniumChat; - break; - case EQ::versions::ClientVersion::SoF: - ConnectionType = EQ::versions::ucsSoFCombined; - break; - case EQ::versions::ClientVersion::SoD: - ConnectionType = EQ::versions::ucsSoDCombined; - break; - case EQ::versions::ClientVersion::UF: - ConnectionType = EQ::versions::ucsUFCombined; - break; - case EQ::versions::ClientVersion::RoF: - ConnectionType = EQ::versions::ucsRoFCombined; - break; - case EQ::versions::ClientVersion::RoF2: - ConnectionType = EQ::versions::ucsRoF2Combined; - break; - default: - ConnectionType = EQ::versions::ucsUnknown; - break; + case EQ::versions::ClientVersion::Titanium: + connection_type = EQ::versions::ucsTitaniumChat; + break; + case EQ::versions::ClientVersion::SoF: + connection_type = EQ::versions::ucsSoFCombined; + break; + case EQ::versions::ClientVersion::SoD: + connection_type = EQ::versions::ucsSoDCombined; + break; + case EQ::versions::ClientVersion::UF: + connection_type = EQ::versions::ucsUFCombined; + break; + case EQ::versions::ClientVersion::RoF: + connection_type = EQ::versions::ucsRoFCombined; + break; + case EQ::versions::ClientVersion::RoF2: + connection_type = EQ::versions::ucsRoF2Combined; + break; + default: + connection_type = EQ::versions::ucsUnknown; + break; } - buffer = StringFormat("%s,%i,%s.%s,%c%08X", - Config->ChatHost.c_str(), - Config->ChatPort, - Config->ShortName.c_str(), + buffer = fmt::format("{},{},{}.{},{}{:08X}", + config->ChatHost, + config->ChatPort, + config->ShortName, GetCharName(), - ConnectionType, - MailKey + static_cast(connection_type), + mail_key ); outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1)); @@ -933,21 +934,21 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { // mail server packet switch (GetClientVersion()) { - case EQ::versions::ClientVersion::Titanium: - ConnectionType = EQ::versions::ucsTitaniumMail; - break; - default: - // retain value from previous switch - break; + case EQ::versions::ClientVersion::Titanium: + connection_type = EQ::versions::ucsTitaniumMail; + break; + default: + // retain value from previous switch + break; } - buffer = StringFormat("%s,%i,%s.%s,%c%08X", - Config->MailHost.c_str(), - Config->MailPort, - Config->ShortName.c_str(), + buffer = fmt::format("{},{},{}.{},{}{:08X}", + config->MailHost, + config->MailPort, + config->ShortName, GetCharName(), - ConnectionType, - MailKey + static_cast(connection_type), + mail_key ); outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1)); diff --git a/world/clientlist.cpp b/world/clientlist.cpp index d2b016af4..8fb35964b 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -100,67 +100,101 @@ ClientListEntry* ClientList::GetCLE(uint32 iID) { //Check current CLE Entry IPs against incoming connection -void ClientList::GetCLEIP(uint32 iIP) { - ClientListEntry* countCLEIPs = 0; +void ClientList::GetCLEIP(uint32 in_ip) { + ClientListEntry* cle = nullptr; LinkedListIterator iterator(clientlist); - int IPInstances = 0; + int count = 0; iterator.Reset(); - while(iterator.MoreElements()) { - countCLEIPs = iterator.GetData(); - if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0))) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) - IPInstances++; // Increment the occurences of this IP address - LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); + while (iterator.MoreElements()) { + cle = iterator.GetData(); + if ( + cle->GetIP() == in_ip && + ( + cle->Admin() < RuleI(World, ExemptMaxClientsStatus) || + RuleI(World, ExemptMaxClientsStatus) < 0 + ) + ) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) + auto ip_string = long2ip(cle->GetIP()); + count++; // Increment the occurences of this IP address + LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}]", cle->LSID(), cle->LSName(), ip_string); + if (RuleB(World, EnableIPExemptions)) { - LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())); - if (IPInstances > database.GetIPExemption(long2ip(countCLEIPs->GetIP()).c_str())) { - if(RuleB(World, IPLimitDisconnectAll)) { - LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); - DisconnectByIP(iIP); + LogClientLogin( + "Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", + cle->LSID(), + cle->LSName(), + ip_string, + count, + database.GetIPExemption(ip_string) + ); + + auto exemption_amount = database.GetIPExemption(ip_string); + if (exemption_amount > 0 && count > exemption_amount) { + if (RuleB(World, IPLimitDisconnectAll)) { + LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string); + DisconnectByIP(in_ip); return; } else { - LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status::Offline); + LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string); + cle->SetOnline(CLE_Status::Offline); iterator.RemoveCurrent(); continue; } } } else { - if (IPInstances > (RuleI(World, MaxClientsPerIP))) { // If the number of connections exceeds the lower limit + if ( + RuleI(World, MaxClientsPerIP) > 0 && + count > RuleI(World, MaxClientsPerIP) + ) { // If the number of connections exceeds the lower limit if (RuleB(World, MaxClientsSetByStatus)) { // If MaxClientsSetByStatus is set to True, override other IP Limit Rules - LogClientLogin("Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", countCLEIPs->LSID(), countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str(), IPInstances, countCLEIPs->Admin()); - if (IPInstances > countCLEIPs->Admin()) { // The IP Limit is set by the status of the account if status > MaxClientsPerIP - if(RuleB(World, IPLimitDisconnectAll)) { - LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); - DisconnectByIP(iIP); + LogClientLogin( + "Account ID: [{}] Account Name: [{}] IP: [{}] IP Instances: [{}] Max IP Instances: [{}]", + cle->LSID(), + cle->LSName(), + ip_string, + count, + cle->Admin() + ); + + if (count > cle->Admin()) { // The IP Limit is set by the status of the account if status > MaxClientsPerIP + if (RuleB(World, IPLimitDisconnectAll)) { + LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string); + DisconnectByIP(in_ip); return; } else { - LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection + LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string); + cle->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; } } - } else if ((countCLEIPs->Admin() < RuleI(World, AddMaxClientsStatus)) || (RuleI(World, AddMaxClientsStatus) < 0)) { // Else if the Admin status of the connection is not eligible for the higher limit, or there is no higher limit (AddMaxClientStatus < 0) - if(RuleB(World, IPLimitDisconnectAll)) { - LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); - DisconnectByIP(iIP); + } else if ( + cle->Admin() < RuleI(World, AddMaxClientsStatus) || + RuleI(World, AddMaxClientsStatus) < 0 + ) { // Else if the Admin status of the connection is not eligible for the higher limit, or there is no higher limit (AddMaxClientStatus < 0) + if (RuleB(World, IPLimitDisconnectAll)) { + LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string); + DisconnectByIP(in_ip); return; } else { - LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection + LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string); + cle->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; } - } else if (IPInstances > RuleI(World, AddMaxClientsPerIP)) { // else they are eligible for the higher limit, but if they exceed that - if(RuleB(World, IPLimitDisconnectAll)) { - LogClientLogin("Disconnect: All accounts on IP [{}]", long2ip(countCLEIPs->GetIP()).c_str()); - DisconnectByIP(iIP); + } else if ( + RuleI(World, AddMaxClientsPerIP) > 0 && + count > RuleI(World, AddMaxClientsPerIP) + ) { // else they are eligible for the higher limit, but if they exceed that + if (RuleB(World, IPLimitDisconnectAll)) { + LogClientLogin("Disconnect: All Accounts on IP [{}]", ip_string); + DisconnectByIP(in_ip); return; } else { - LogClientLogin("Disconnect: Account [{}] on IP [{}]", countCLEIPs->LSName(), long2ip(countCLEIPs->GetIP()).c_str()); - countCLEIPs->SetOnline(CLE_Status::Offline); // Remove the connection + LogClientLogin("Disconnect: Account [{}] on IP [{}]", cle->LSName(), ip_string); + cle->SetOnline(CLE_Status::Offline); // Remove the connection iterator.RemoveCurrent(); continue; } @@ -168,46 +202,54 @@ void ClientList::GetCLEIP(uint32 iIP) { } } } + iterator.Advance(); } } -uint32 ClientList::GetCLEIPCount(uint32 iIP) { - ClientListEntry* countCLEIPs = 0; +uint32 ClientList::GetCLEIPCount(uint32 in_ip) { + ClientListEntry* cle = nullptr; LinkedListIterator iterator(clientlist); - int IPInstances = 0; + int count = 0; iterator.Reset(); while (iterator.MoreElements()) { - countCLEIPs = iterator.GetData(); - if ((countCLEIPs->GetIP() == iIP) && ((countCLEIPs->Admin() < (RuleI(World, ExemptMaxClientsStatus))) || (RuleI(World, ExemptMaxClientsStatus) < 0)) && countCLEIPs->Online() >= CLE_Status::Online) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) - IPInstances++; // Increment the occurences of this IP address + cle = iterator.GetData(); + if ( + cle->GetIP() == in_ip && + ( + cle->Admin() < RuleI(World, ExemptMaxClientsStatus) || + RuleI(World, ExemptMaxClientsStatus) < 0 + ) && + cle->Online() >= CLE_Status::Online + ) { // If the IP matches, and the connection admin status is below the exempt status, or exempt status is less than 0 (no-one is exempt) + count++; // Increment the occurences of this IP address } iterator.Advance(); } - return IPInstances; + return count; } -void ClientList::DisconnectByIP(uint32 iIP) { - ClientListEntry* countCLEIPs = 0; +void ClientList::DisconnectByIP(uint32 in_ip) { + ClientListEntry* cle = nullptr; LinkedListIterator iterator(clientlist); iterator.Reset(); - while(iterator.MoreElements()) { - countCLEIPs = iterator.GetData(); - if ((countCLEIPs->GetIP() == iIP)) { - if(strlen(countCLEIPs->name())) { + while (iterator.MoreElements()) { + cle = iterator.GetData(); + if (cle->GetIP() == in_ip) { + if (strlen(cle->name())) { auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, "SessionLimit"); - strcpy(skp->name, countCLEIPs->name()); + auto skp = (ServerKickPlayer_Struct*) pack->pBuffer; + strn0cpy(skp->adminname, "SessionLimit", sizeof(skp->adminname)); + strn0cpy(skp->name, cle->name(), sizeof(skp->name)); skp->adminrank = 255; zoneserver_list.SendPacket(pack); safe_delete(pack); } - countCLEIPs->SetOnline(CLE_Status::Offline); + cle->SetOnline(CLE_Status::Offline); iterator.RemoveCurrent(); } iterator.Advance(); diff --git a/world/clientlist.h b/world/clientlist.h index 2f9583784..2ab6be60d 100644 --- a/world/clientlist.h +++ b/world/clientlist.h @@ -57,9 +57,9 @@ public: ClientListEntry* FindCLEByCharacterID(uint32 iCharID); ClientListEntry* FindCLEByLSID(uint32 iLSID); ClientListEntry* GetCLE(uint32 iID); - void GetCLEIP(uint32 iIP); + void GetCLEIP(uint32 in_ip); uint32 GetCLEIPCount(uint32 iLSAccountID); - void DisconnectByIP(uint32 iIP); + void DisconnectByIP(uint32 in_ip); void CLCheckStale(); void CLEKeepAlive(uint32 numupdates, uint32* wid); void CLEAdd(uint32 iLSID, const char* iLoginServerName, const char* iLoginName, const char* iLoginKey, int16 iWorldAdmin = AccountStatus::Player, uint32 ip = 0, uint8 local=0); diff --git a/world/main.cpp b/world/main.cpp index ee9f12705..17552952f 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -44,6 +44,7 @@ #include "../common/rulesys.h" #include "../common/platform.h" #include "../common/crash.h" +#include "../common/misc.h" #include "client.h" #include "worlddb.h" @@ -658,7 +659,7 @@ int main(int argc, char **argv) eqsm.OnNewConnection( [&stream_identifier](std::shared_ptr stream) { stream_identifier.AddStream(stream); - LogInfo("New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); + LogInfo("New connection from IP {}:{}", long2ip(stream->GetRemoteIP()), ntohs(stream->GetRemotePort())); } ); diff --git a/zone/main.cpp b/zone/main.cpp index f92ea9ea8..15be8f77a 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -36,6 +36,7 @@ #include "../common/memory_mapped_file.h" #include "../common/spdat.h" #include "../common/eqemu_logsys.h" +#include "../common/misc.h" #include "api_service.h" #include "zone_config.h" @@ -514,7 +515,7 @@ int main(int argc, char** argv) { eqsm->OnNewConnection([&stream_identifier](std::shared_ptr stream) { stream_identifier.AddStream(stream); - LogF(Logs::Detail, Logs::WorldServer, "New connection from IP {0}:{1}", stream->GetRemoteIP(), ntohs(stream->GetRemotePort())); + LogInfo("New connection from IP {}:{}", long2ip(stream->GetRemoteIP()), ntohs(stream->GetRemotePort())); }); } From c8f6dbb86d1c67ffe9a9ea1a1c3d0be3ebe23d47 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 28 May 2022 14:35:05 -0400 Subject: [PATCH 032/552] [Commands] Cleanup #npcedit, #lastname, #title, and #titlesuffix Commands. (#2215) * [Commands] Cleanup #lastname, #npcedit, #title, and #titlesuffix Commands. - Cleanup messages and logic. * Update emu_constants.h * Update command.cpp * Update command.cpp * Cleanup of GetXName methods to not define map unnecessarily. * Update emu_constants.cpp * Update npcedit.cpp --- common/emu_constants.cpp | 98 +- common/emu_constants.h | 27 +- zone/command.cpp | 12 +- zone/gm_commands/lastname.cpp | 23 +- zone/gm_commands/npcedit.cpp | 3746 +++++++++++++++++++----------- zone/gm_commands/title.cpp | 16 +- zone/gm_commands/titlesuffix.cpp | 13 +- 7 files changed, 2577 insertions(+), 1358 deletions(-) diff --git a/common/emu_constants.cpp b/common/emu_constants.cpp index 4d5c40b64..ad720123c 100644 --- a/common/emu_constants.cpp +++ b/common/emu_constants.cpp @@ -22,7 +22,6 @@ #include "data_verification.h" #include "bodytypes.h" - int16 EQ::invtype::GetInvTypeSize(int16 inv_type) { static const int16 local_array[] = { POSSESSIONS_SIZE, @@ -189,15 +188,16 @@ const std::map& EQ::constants::GetLanguageMap() { LANG_HADAL, "Hadal" }, { LANG_UNKNOWN, "Unknown" } }; + return language_map; } std::string EQ::constants::GetLanguageName(int language_id) { if (EQ::ValueWithin(language_id, LANG_COMMON_TONGUE, LANG_UNKNOWN)) { - auto languages = EQ::constants::GetLanguageMap(); - return languages[language_id]; + return EQ::constants::GetLanguageMap().find(language_id)->second; } + return std::string(); } @@ -211,21 +211,22 @@ const std::map& EQ::constants::GetLDoNThemeMap() { LDoNThemes::RUJ, "Rujarkian Hills" }, { LDoNThemes::TAK, "Takish-Hiz" }, }; + return ldon_theme_map; } std::string EQ::constants::GetLDoNThemeName(uint32 theme_id) { if (EQ::ValueWithin(theme_id, LDoNThemes::Unused, LDoNThemes::TAK)) { - auto ldon_themes = EQ::constants::GetLDoNThemeMap(); - return ldon_themes[theme_id]; + return EQ::constants::GetLDoNThemeMap().find(theme_id)->second; } + return std::string(); } -const std::map& EQ::constants::GetFlyModeMap() +const std::map& EQ::constants::GetFlyModeMap() { - static const std::map flymode_map = { + static const std::map flymode_map = { { GravityBehavior::Ground, "Ground" }, { GravityBehavior::Flying, "Flying" }, { GravityBehavior::Levitating, "Levitating" }, @@ -233,15 +234,16 @@ const std::map& EQ::constants::GetFlyModeMap() { GravityBehavior::Floating, "Floating" }, { GravityBehavior::LevitateWhileRunning, "Levitating While Running" }, }; + return flymode_map; } -std::string EQ::constants::GetFlyModeName(uint8 flymode_id) +std::string EQ::constants::GetFlyModeName(int8 flymode_id) { if (EQ::ValueWithin(flymode_id, GravityBehavior::Ground, GravityBehavior::LevitateWhileRunning)) { - auto flymodes = EQ::constants::GetFlyModeMap(); - return flymodes[flymode_id]; + return EQ::constants::GetFlyModeMap().find(flymode_id)->second; } + return std::string(); } @@ -288,15 +290,16 @@ const std::map& EQ::constants::GetBodyTypeMap() { BT_InvisMan, "Invisible Man" }, { BT_Special, "Special" }, }; + return bodytype_map; } std::string EQ::constants::GetBodyTypeName(bodyType bodytype_id) { - auto bodytypes = EQ::constants::GetBodyTypeMap(); - if (!bodytypes[bodytype_id].empty()) { - return bodytypes[bodytype_id]; + if (EQ::constants::GetBodyTypeMap().find(bodytype_id) != EQ::constants::GetBodyTypeMap().end()) { + return EQ::constants::GetBodyTypeMap().find(bodytype_id)->second; } + return std::string(); } @@ -321,21 +324,23 @@ const std::map& EQ::constants::GetAccountStatusMap() { AccountStatus::GMImpossible, "GM Impossible" }, { AccountStatus::Max, "GM Max" } }; + return account_status_map; } std::string EQ::constants::GetAccountStatusName(uint8 account_status) { - auto account_statuses = EQ::constants::GetAccountStatusMap(); - std::string status_name; - for (auto status_level = account_statuses.rbegin(); status_level != account_statuses.rend(); ++status_level) { + for ( + auto status_level = EQ::constants::GetAccountStatusMap().rbegin(); + status_level != EQ::constants::GetAccountStatusMap().rend(); + ++status_level + ) { if (account_status >= status_level->first) { - status_name = status_level->second; - break; + return status_level->second; } } - return status_name; + return std::string(); } const std::map& EQ::constants::GetConsiderLevelMap() @@ -351,15 +356,16 @@ const std::map& EQ::constants::GetConsiderLevelMap() { ConsiderLevel::Threateningly, "Threateningly" }, { ConsiderLevel::Scowls, "Scowls" } }; + return consider_level_map; } std::string EQ::constants::GetConsiderLevelName(uint8 faction_consider_level) { - auto consider_levels = EQ::constants::GetConsiderLevelMap(); - if (!consider_levels[faction_consider_level].empty()) { - return consider_levels[faction_consider_level]; + if (EQ::constants::GetConsiderLevelMap().find(faction_consider_level) != EQ::constants::GetConsiderLevelMap().end()) { + return EQ::constants::GetConsiderLevelMap().find(faction_consider_level)->second; } + return std::string(); } @@ -371,14 +377,58 @@ const std::map& EQ::constants::GetEnvironmentalDamageMap() { EnvironmentalDamage::Falling, "Falling" }, { EnvironmentalDamage::Trap, "Trap" } }; + return damage_type_map; } std::string EQ::constants::GetEnvironmentalDamageName(uint8 damage_type) { if (EQ::ValueWithin(damage_type, EnvironmentalDamage::Lava, EnvironmentalDamage::Trap)) { - auto damage_types = EQ::constants::GetEnvironmentalDamageMap(); - return damage_types[damage_type]; + return EQ::constants::GetEnvironmentalDamageMap().find(damage_type)->second; } + + return std::string(); +} + +const std::map& EQ::constants::GetStuckBehaviorMap() +{ + static const std::map stuck_behavior_map = { + { StuckBehavior::RunToTarget, "Run To Target" }, + { StuckBehavior::WarpToTarget, "Warp To Target" }, + { StuckBehavior::TakeNoAction, "Take No Action" }, + { StuckBehavior::EvadeCombat, "Evade Combat" } + }; + + return stuck_behavior_map; +} + +std::string EQ::constants::GetStuckBehaviorName(uint8 behavior_id) +{ + if (EQ::ValueWithin(behavior_id, StuckBehavior::RunToTarget, StuckBehavior::EvadeCombat)) { + return EQ::constants::GetStuckBehaviorMap().find(behavior_id)->second; + } + + return std::string(); +} + +const std::map& EQ::constants::GetSpawnAnimationMap() +{ + static const std::map spawn_animation_map = { + { SpawnAnimations::Standing, "Standing" }, + { SpawnAnimations::Sitting, "Sitting" }, + { SpawnAnimations::Crouching, "Crouching" }, + { SpawnAnimations::Laying, "Laying" }, + { SpawnAnimations::Looting, "Looting" } + }; + + return spawn_animation_map; +} + +std::string EQ::constants::GetSpawnAnimationName(uint8 animation_id) +{ + if (EQ::ValueWithin(animation_id, SpawnAnimations::Standing, SpawnAnimations::Looting)) { + return EQ::constants::GetSpawnAnimationMap().find(animation_id)->second; + } + return std::string(); } diff --git a/common/emu_constants.h b/common/emu_constants.h index 80e7a0b05..941393316 100644 --- a/common/emu_constants.h +++ b/common/emu_constants.h @@ -221,7 +221,7 @@ namespace EQ stanceBurnAE }; - enum GravityBehavior : uint8 { + enum GravityBehavior : int8 { Ground, Flying, Levitating, @@ -237,6 +237,21 @@ namespace EQ Trap }; + enum StuckBehavior : uint8 { + RunToTarget, + WarpToTarget, + TakeNoAction, + EvadeCombat + }; + + enum SpawnAnimations : uint8 { + Standing, + Sitting, + Crouching, + Laying, + Looting + }; + const char *GetStanceName(StanceType stance_type); int ConvertStanceTypeToIndex(StanceType stance_type); @@ -246,8 +261,8 @@ namespace EQ extern const std::map& GetLDoNThemeMap(); std::string GetLDoNThemeName(uint32 theme_id); - extern const std::map& GetFlyModeMap(); - std::string GetFlyModeName(uint8 flymode_id); + extern const std::map& GetFlyModeMap(); + std::string GetFlyModeName(int8 flymode_id); extern const std::map& GetBodyTypeMap(); std::string GetBodyTypeName(bodyType bodytype_id); @@ -261,6 +276,12 @@ namespace EQ extern const std::map& GetEnvironmentalDamageMap(); std::string GetEnvironmentalDamageName(uint8 damage_type); + extern const std::map& GetStuckBehaviorMap(); + std::string GetStuckBehaviorName(uint8 behavior_id); + + extern const std::map& GetSpawnAnimationMap(); + std::string GetSpawnAnimationName(uint8 animation_id); + const int STANCE_TYPE_FIRST = stancePassive; const int STANCE_TYPE_LAST = stanceBurnAE; const int STANCE_TYPE_COUNT = stanceBurnAE; diff --git a/zone/command.cpp b/zone/command.cpp index 269d6d62e..822d3c98e 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -192,7 +192,7 @@ int command_init(void) command_add("kick", "[Character Name] - Disconnect a player by name", AccountStatus::GMLeadAdmin, command_kick) || command_add("kill", "Kill your target", AccountStatus::GMAdmin, command_kill) || command_add("killallnpcs", "[npc_name] - Kills all npcs by search name, leave blank for all attackable NPC's", AccountStatus::GMMgmt, command_killallnpcs) || - command_add("lastname", "[Last Name] - Set you or your player target's lastname", AccountStatus::Guide, command_lastname) || + command_add("lastname", "[Last Name] - Set your or your player target's last name (use \"-1\" to remove last name)", AccountStatus::Guide, command_lastname) || command_add("level", "[Level] - Set your target's level", AccountStatus::Steward, command_level) || command_add("list", "[npcs|players|corpses|doors|objects] [search] - Search entities", AccountStatus::ApprenticeGuide, command_list) || command_add("listpetition", "List petitions", AccountStatus::Guide, command_listpetition) || @@ -205,7 +205,7 @@ int command_init(void) command_add("memspell", "[Spell ID] [Spell Gem] - Memorize a Spell by ID to the specified Spell Gem for you or your target", AccountStatus::Guide, command_memspell) || command_add("merchant_close_shop", "Closes a merchant shop", AccountStatus::GMAdmin, command_merchantcloseshop) || command_add("merchant_open_shop", "Opens a merchants shop", AccountStatus::GMAdmin, command_merchantopenshop) || - command_add("modifynpcstat", "- Modifys a NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || + command_add("modifynpcstat", "Modifies an NPC's stats", AccountStatus::GMLeadAdmin, command_modifynpcstat) || command_add("motd", "[Message of the Day] - Set Message of the Day (leave empty to have no Message of the Day)", AccountStatus::GMLeadAdmin, command_motd) || command_add("movechar", "[Character ID|Character Name] [Zone ID|Zone Short Name] - Move an offline character to the specified zone", AccountStatus::Guide, command_movechar) || command_add("movement", "Various movement commands", AccountStatus::GMMgmt, command_movement) || @@ -230,8 +230,8 @@ int command_init(void) command_add("nukeitem", "[Item ID] - Removes the specified Item ID from you or your player target's inventory", AccountStatus::GMLeadAdmin, command_nukeitem) || command_add("object", "List|Add|Edit|Move|Rotate|Copy|Save|Undo|Delete - Manipulate static and tradeskill objects within the zone", AccountStatus::GMAdmin, command_object) || command_add("oocmute", "[0|1] - Enable or Disable Server OOC", AccountStatus::GMMgmt, command_oocmute) || - command_add("opcode", "- opcode management", AccountStatus::GMImpossible, command_opcode) || - command_add("path", "- view and edit pathing", AccountStatus::GMMgmt, command_path) || + command_add("opcode", "opcode management", AccountStatus::GMImpossible, command_opcode) || + command_add("path", "view and edit pathing", AccountStatus::GMMgmt, command_path) || command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", AccountStatus::GMAdmin, command_peekinv) || command_add("peqzone", "[Zone ID|Zone Short Name] - Teleports you to the specified zone if you meet the requirements.", AccountStatus::Player, command_peqzone) || command_add("peqzone_flags", "displays the PEQZone Flags of you or your target", AccountStatus::Player, command_peqzone_flags) || @@ -315,8 +315,8 @@ int command_init(void) command_add("time", "[Hour] [Minute] - Set world time to specified time", AccountStatus::EQSupport, command_time) || command_add("timers", "Display persistent timers for target", AccountStatus::GMMgmt, command_timers) || command_add("timezone", "[Hour] [Minutes] - Set timezone (Minutes are optional)", AccountStatus::EQSupport, command_timezone) || - command_add("title", "[Remove|Title] [Save (0 = False, 1 = True)] - Set your or your player target's title (use remove to remove title, Save defaults to false if not used)", AccountStatus::Guide, command_title) || - command_add("titlesuffix", "[Remove|Title Suffix] [Save (0 = False, 1 = True)] - Set your or your player target's title suffix (use remove to remove title suffix, Save defaults to false if not used)", AccountStatus::Guide, command_titlesuffix) || + command_add("title", "[Title] - Set your or your player target's title (use \"-1\" to remove title)", AccountStatus::Guide, command_title) || + command_add("titlesuffix", "[Title Suffix] - Set your or your player target's title suffix (use \"-1\" to remove title suffix)", AccountStatus::Guide, command_titlesuffix) || command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", AccountStatus::GMLeadAdmin, command_traindisc) || command_add("trapinfo", "Gets infomation about the traps currently spawned in the zone.", AccountStatus::QuestTroupe, command_trapinfo) || command_add("tune", "Calculate statistical values related to combat.", AccountStatus::GMAdmin, command_tune) || diff --git a/zone/gm_commands/lastname.cpp b/zone/gm_commands/lastname.cpp index 81b3c2b85..070f5023a 100755 --- a/zone/gm_commands/lastname.cpp +++ b/zone/gm_commands/lastname.cpp @@ -9,20 +9,31 @@ void command_lastname(Client *c, const Seperator *sep) LogInfo("#lastname request from [{}] for [{}]", c->GetCleanName(), target->GetCleanName()); - std::string last_name = sep->arg[1]; + bool is_remove = !strcasecmp(sep->argplus[1], "-1"); + std::string last_name = is_remove ? "" : sep->argplus[1]; + if (last_name.size() > 64) { - c->Message(Chat::White, "Usage: #lastname [Last Name] (Last Name must be 64 characters or less)"); + c->Message(Chat::White, "Last name must be 64 characters or less."); return; } target->ChangeLastName(last_name); + c->Message( Chat::White, fmt::format( - "{} now {} a last name of '{}'.", - c->GetTargetDescription(target, TargetDescriptionType::UCYou), - c == target ? "have" : "has", - last_name + "Last name has been {}{} for {}{}", + is_remove ? "removed" : "changed", + !is_remove ? " and saved" : "", + c->GetTargetDescription(target), + ( + is_remove ? + "." : + fmt::format( + " to '{}'.", + last_name + ) + ) ).c_str() ); } diff --git a/zone/gm_commands/npcedit.cpp b/zone/gm_commands/npcedit.cpp index 1332a61e6..435155831 100755 --- a/zone/gm_commands/npcedit.cpp +++ b/zone/gm_commands/npcedit.cpp @@ -1,552 +1,847 @@ #include "../client.h" #include "../groups.h" -#include "../mob_movement_manager.h" #include "../raids.h" #include "../raids.h" void command_npcedit(Client *c, const Seperator *sep) { if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { - c->Message(Chat::White, "Error: Must have NPC targeted"); + c->Message(Chat::White, "You must target an NPC to use this command."); return; } - if (strcasecmp(sep->arg[1], "help") == 0) { + int arguments = sep->argnum; - c->Message(Chat::White, "Help File for #npcedit. Syntax for commands are:"); - c->Message(Chat::White, "#npcedit name - Sets an NPC's Name"); - c->Message(Chat::White, "#npcedit lastname - Sets an NPC's Lastname"); - c->Message(Chat::White, "#npcedit level - Sets an NPC's Level"); - c->Message(Chat::White, "#npcedit race - Sets an NPC's Race"); - c->Message(Chat::White, "#npcedit class - Sets an NPC's Class"); - c->Message(Chat::White, "#npcedit bodytype - Sets an NPC's Bodytype"); - c->Message(Chat::White, "#npcedit hp - Sets an NPC's Hitpoints"); - c->Message(Chat::White, "#npcedit mana - Sets an NPC's Mana"); - c->Message(Chat::White, "#npcedit gender - Sets an NPC's Gender"); - c->Message(Chat::White, "#npcedit texture - Sets an NPC's Texture"); - c->Message(Chat::White, "#npcedit helmtexture - Sets an NPC's Helmet Texture"); - c->Message(Chat::White, "#npcedit herosforgemodel - Sets an NPC's Hero's Forge Model"); - c->Message(Chat::White, "#npcedit size - Sets an NPC's Size"); - c->Message(Chat::White, "#npcedit hpregen - Sets an NPC's Hitpoints Regeneration Rate Per Tick"); - c->Message(Chat::White, "#npcedit hp_regen_per_second - Sets an NPC's HP regeneration per second"); - c->Message(Chat::White, "#npcedit manaregen - Sets an NPC's Mana Regeneration Rate Per Tick"); - c->Message(Chat::White, "#npcedit loottable - Sets an NPC's Loottable ID"); - c->Message(Chat::White, "#npcedit merchantid - Sets an NPC's Merchant ID"); - c->Message(Chat::White, "#npcedit alt_currency_id - Sets an NPC's Alternate Currency ID"); - c->Message(Chat::White, "#npcedit spell - Sets an NPC's Spells List ID"); - c->Message(Chat::White, "#npcedit npc_spells_effects_id - Sets an NPC's Spell Effects ID"); - c->Message(Chat::White, "#npcedit faction - Sets an NPC's Faction ID"); - c->Message(Chat::White, "#npcedit adventure_template_id - Sets an NPC's Adventure Template ID"); - c->Message(Chat::White, "#npcedit trap_template - Sets an NPC's Trap Template ID"); - c->Message(Chat::White, "#npcedit damage [minimum] [maximum] - Sets an NPC's Damage"); - c->Message(Chat::White, "#npcedit attackcount - Sets an NPC's Attack Count"); - c->Message(Chat::White, "#npcedit special_attacks - Sets an NPC's Special Attacks"); - c->Message(Chat::White, "#npcedit special_abilities - Sets an NPC's Special Abilities"); - c->Message(Chat::White, "#npcedit aggroradius - Sets an NPC's Aggro Radius"); - c->Message(Chat::White, "#npcedit assistradius - Sets an NPC's Assist Radius"); - c->Message(Chat::White, "#npcedit featuresave - Saves an NPC's current facial features to the database"); - c->Message(Chat::White, "#npcedit armortint_id - Sets an NPC's Armor Tint ID"); - c->Message(Chat::White, "#npcedit color [red] [green] [blue] - Sets an NPC's Red, Green, and Blue armor tint"); - c->Message(Chat::White, "#npcedit ammoidfile - Sets an NPC's Ammo ID File"); + bool is_help = (arguments == 0 || !strcasecmp(sep->arg[1], "help")); + + if (is_help) { + c->Message(Chat::White, "Usage: #npcedit name [Name] - Sets an NPC's Name"); + c->Message(Chat::White, "Usage: #npcedit lastname [Last Name] - Sets an NPC's Last Name"); + c->Message(Chat::White, "Usage: #npcedit level [Level] - Sets an NPC's Level"); + c->Message(Chat::White, "Usage: #npcedit race [Race ID] - Sets an NPC's Race"); + c->Message(Chat::White, "Usage: #npcedit class [Class ID] - Sets an NPC's Class"); + c->Message(Chat::White, "Usage: #npcedit bodytype [Body Type ID] - Sets an NPC's Bodytype"); + c->Message(Chat::White, "Usage: #npcedit hp [HP] - Sets an NPC's HP"); + c->Message(Chat::White, "Usage: #npcedit mana [Mana] - Sets an NPC's Mana"); + c->Message(Chat::White, "Usage: #npcedit gender [Gender ID] - Sets an NPC's Gender"); + c->Message(Chat::White, "Usage: #npcedit texture [Texture] - Sets an NPC's Texture"); + c->Message(Chat::White, "Usage: #npcedit helmtexture [Helmet Texture] - Sets an NPC's Helmet Texture"); + c->Message(Chat::White, "Usage: #npcedit herosforgemodel [Model Number] - Sets an NPC's Hero's Forge Model"); + c->Message(Chat::White, "Usage: #npcedit size [Size] - Sets an NPC's Size"); + c->Message(Chat::White, "Usage: #npcedit hpregen [HP Regen] - Sets an NPC's HP Regen Rate Per Tick"); + c->Message(Chat::White, "Usage: #npcedit hp_regen_per_second [HP Regen] - Sets an NPC's HP Regen Rate Per Second"); + c->Message(Chat::White, "Usage: #npcedit manaregen [Mana Regen] - Sets an NPC's Mana Regen Rate Per Tick"); + c->Message(Chat::White, "Usage: #npcedit loottable [Loottable ID] - Sets an NPC's Loottable ID"); + c->Message(Chat::White, "Usage: #npcedit merchantid [Merchant ID] - Sets an NPC's Merchant ID"); + c->Message(Chat::White, "Usage: #npcedit alt_currency_id [Alternate Currency ID] - Sets an NPC's Alternate Currency ID"); + c->Message(Chat::White, "Usage: #npcedit spell [Spell List ID] - Sets an NPC's Spells List ID"); + c->Message(Chat::White, "Usage: #npcedit npc_spells_effects_id [Spell Effects ID] - Sets an NPC's Spell Effects ID"); + c->Message(Chat::White, "Usage: #npcedit faction [Faction ID] - Sets an NPC's Faction ID"); + c->Message(Chat::White, "Usage: #npcedit adventure_template_id [Template ID] - Sets an NPC's Adventure Template ID"); + c->Message(Chat::White, "Usage: #npcedit trap_template [Template ID] - Sets an NPC's Trap Template ID"); + c->Message(Chat::White, "Usage: #npcedit damage [Minimum] [Maximum] - Sets an NPC's Damage"); + c->Message(Chat::White, "Usage: #npcedit attackcount [Attack Count] - Sets an NPC's Attack Count"); + c->Message(Chat::White, "Usage: #npcedit special_attacks [Special Attacks] - Sets an NPC's Special Attacks"); + c->Message(Chat::White, "Usage: #npcedit special_abilities [Special Abilities] - Sets an NPC's Special Abilities"); + c->Message(Chat::White, "Usage: #npcedit aggroradius [Radius] - Sets an NPC's Aggro Radius"); + c->Message(Chat::White, "Usage: #npcedit assistradius [Radius] - Sets an NPC's Assist Radius"); + c->Message(Chat::White, "Usage: #npcedit featuresave - Saves an NPC's current facial features to the database"); + c->Message(Chat::White, "Usage: #npcedit armortint_id [Armor Tint ID] - Sets an NPC's Armor Tint ID"); + c->Message(Chat::White, "Usage: #npcedit color [Red] [Green] [Blue] - Sets an NPC's Red, Green, and Blue armor tint"); + c->Message(Chat::White, "Usage: #npcedit ammoidfile [ID File] - Sets an NPC's Ammo ID File"); c->Message( Chat::White, - "#npcedit weapon [primary_model] [secondary_model] - Sets an NPC's Primary and Secondary Weapon Model" + "Usage: #npcedit weapon [Primary Model] [Secondary Model] - Sets an NPC's Primary and Secondary Weapon Model" ); - c->Message(Chat::White, "#npcedit meleetype [primary_type] [secondary_type] - Sets an NPC's Melee Types"); - c->Message(Chat::White, "#npcedit rangedtype - Sets an NPC's Ranged Type"); - c->Message(Chat::White, "#npcedit runspeed - Sets an NPC's Run Speed"); - c->Message(Chat::White, "#npcedit mr - Sets an NPC's Magic Resistance"); - c->Message(Chat::White, "#npcedit pr - Sets an NPC's Poison Resistance"); - c->Message(Chat::White, "#npcedit dr - Sets an NPC's Disease Resistance"); - c->Message(Chat::White, "#npcedit fr - Sets an NPC's Fire Resistance"); - c->Message(Chat::White, "#npcedit cr - Sets an NPC's Cold Resistance"); - c->Message(Chat::White, "#npcedit corrup - Sets an NPC's Corruption Resistance"); - c->Message(Chat::White, "#npcedit phr - Sets and NPC's Physical Resistance"); + c->Message(Chat::White, "Usage: #npcedit meleetype [Primary Type] [Secondary Type] - Sets an NPC's Melee Skill Types"); + c->Message(Chat::White, "Usage: #npcedit rangedtype [Type] - Sets an NPC's Ranged Skill Type"); + c->Message(Chat::White, "Usage: #npcedit runspeed [Run Speed] - Sets an NPC's Run Speed"); + c->Message(Chat::White, "Usage: #npcedit mr [Resistance] - Sets an NPC's Magic Resistance"); + c->Message(Chat::White, "Usage: #npcedit pr [Resistance] - Sets an NPC's Poison Resistance"); + c->Message(Chat::White, "Usage: #npcedit dr [Resistance] - Sets an NPC's Disease Resistance"); + c->Message(Chat::White, "Usage: #npcedit fr [Resistance] - Sets an NPC's Fire Resistance"); + c->Message(Chat::White, "Usage: #npcedit cr [Resistance] - Sets an NPC's Cold Resistance"); + c->Message(Chat::White, "Usage: #npcedit corrup [Resistance] - Sets an NPC's Corruption Resistance"); + c->Message(Chat::White, "Usage: #npcedit phr [Resistance] - Sets and NPC's Physical Resistance"); c->Message( Chat::White, - "#npcedit seeinvis - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]" + "Usage: #npcedit seeinvis [Flag] - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]" ); c->Message( Chat::White, - "#npcedit seeinvisundead - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]" + "Usage: #npcedit seeinvisundead [Flag] - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]" ); c->Message( Chat::White, - "#npcedit qglobal - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]" + "Usage: #npcedit qglobal [Flag] - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]" ); - c->Message(Chat::White, "#npcedit ac - Sets an NPC's Armor Class"); + c->Message(Chat::White, "Usage: #npcedit ac [Armor Class] - Sets an NPC's Armor Class"); c->Message( Chat::White, - "#npcedit npcaggro - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]" + "Usage: #npcedit npcaggro [Flag] - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]" ); - c->Message(Chat::White, "#npcedit spawn_limit - Sets an NPC's Spawn Limit Counter"); - c->Message(Chat::White, "#npcedit attackspeed - Sets an NPC's Attack Speed Modifier"); - c->Message(Chat::White, "#npcedit attackdelay - Sets an NPC's Attack Delay"); - c->Message(Chat::White, "#npcedit findable - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); - c->Message(Chat::White, "#npcedit str - Sets an NPC's Strength"); - c->Message(Chat::White, "#npcedit sta - Sets an NPC's Stamina"); - c->Message(Chat::White, "#npcedit dex - Sets an NPC's Dexterity"); - c->Message(Chat::White, "#npcedit agi - Sets an NPC's Agility"); - c->Message(Chat::White, "#npcedit int - Sets an NPC's Intelligence"); - c->Message(Chat::White, "#npcedit wis - Sets an NPC's Wisdom"); - c->Message(Chat::White, "#npcedit cha - Sets an NPC's Charisma"); + c->Message(Chat::White, "Usage: #npcedit spawn_limit [Limit] - Sets an NPC's Spawn Limit Counter"); + c->Message(Chat::White, "Usage: #npcedit attackspeed [Attack Speed] - Sets an NPC's Attack Speed Modifier"); + c->Message(Chat::White, "Usage: #npcedit attackdelay [Attack Delay] - Sets an NPC's Attack Delay"); + c->Message(Chat::White, "Usage: #npcedit findable [Flag] - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); + c->Message(Chat::White, "Usage: #npcedit str [Strength] - Sets an NPC's Strength"); + c->Message(Chat::White, "Usage: #npcedit sta [Stamina] - Sets an NPC's Stamina"); + c->Message(Chat::White, "Usage: #npcedit agi [Agility] - Sets an NPC's Agility"); + c->Message(Chat::White, "Usage: #npcedit dex [Dexterity] - Sets an NPC's Dexterity"); + c->Message(Chat::White, "Usage: #npcedit int [Intelligence] - Sets an NPC's Intelligence"); + c->Message(Chat::White, "Usage: #npcedit wis [Wisdom] - Sets an NPC's Wisdom"); + c->Message(Chat::White, "Usage: #npcedit cha [Charisma] - Sets an NPC's Charisma"); c->Message( Chat::White, - "#npcedit seehide - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]" + "Usage: #npcedit seehide [Flag] - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]" ); c->Message( Chat::White, - "#npcedit seeimprovedhide - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]" + "Usage: #npcedit seeimprovedhide [Flag] - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]" ); - c->Message(Chat::White, "#npcedit trackable - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); - c->Message(Chat::White, "#npcedit atk - Sets an NPC's Attack"); - c->Message(Chat::White, "#npcedit accuracy - Sets an NPC's Accuracy"); - c->Message(Chat::White, "#npcedit avoidance - Sets an NPC's Avoidance"); - c->Message(Chat::White, "#npcedit slow_mitigation - Sets an NPC's Slow Mitigation"); - c->Message(Chat::White, "#npcedit version - Sets an NPC's Version"); - c->Message(Chat::White, "#npcedit maxlevel - Sets an NPC's Maximum Level"); - c->Message(Chat::White, "#npcedit scalerate - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message(Chat::White, "Usage: #npcedit trackable [Flag] - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); + c->Message(Chat::White, "Usage: #npcedit atk [Attack] - Sets an NPC's Attack"); + c->Message(Chat::White, "Usage: #npcedit accuracy [Accuracy] - Sets an NPC's Accuracy"); + c->Message(Chat::White, "Usage: #npcedit avoidance [Avoidance] - Sets an NPC's Avoidance"); + c->Message(Chat::White, "Usage: #npcedit slow_mitigation [Slow Mitigation] - Sets an NPC's Slow Mitigation"); + c->Message(Chat::White, "Usage: #npcedit version [Version] - Sets an NPC's Version"); + c->Message(Chat::White, "Usage: #npcedit maxlevel [Max Level] - Sets an NPC's Maximum Level"); + c->Message(Chat::White, "Usage: #npcedit scalerate [Scale Rate] - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); c->Message( Chat::White, - "#npcedit spellscale - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + "Usage: #npcedit spellscale [Scale Rate] - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" ); c->Message( Chat::White, - "#npcedit healscale - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + "Usage: #npcedit healscale [Scale Rate] - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" ); c->Message( Chat::White, - "#npcedit no_target - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]" + "Usage: #npcedit no_target [Flag] - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]" ); c->Message( Chat::White, - "#npcedit raidtarget - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]" + "Usage: #npcedit raidtarget [Flag] - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]" ); - c->Message(Chat::White, "#npcedit armtexture - Sets an NPC's Arm Texture"); - c->Message(Chat::White, "#npcedit bracertexture - Sets an NPC's Bracer Texture"); - c->Message(Chat::White, "#npcedit handtexture - Sets an NPC's Hand Texture"); - c->Message(Chat::White, "#npcedit legtexture - Sets an NPC's Leg Texture"); - c->Message(Chat::White, "#npcedit feettexture - Sets an NPC's Feet Texture"); - c->Message(Chat::White, "#npcedit walkspeed - Sets an NPC's Walk Speed"); - c->Message(Chat::White, "#npcedit show_name - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); + c->Message(Chat::White, "Usage: #npcedit armtexture [Texture] - Sets an NPC's Arm Texture"); + c->Message(Chat::White, "Usage: #npcedit bracertexture [Texture] - Sets an NPC's Bracer Texture"); + c->Message(Chat::White, "Usage: #npcedit handtexture [Texture] - Sets an NPC's Hand Texture"); + c->Message(Chat::White, "Usage: #npcedit legtexture [Texture] - Sets an NPC's Leg Texture"); + c->Message(Chat::White, "Usage: #npcedit feettexture [Texture] - Sets an NPC's Feet Texture"); + c->Message(Chat::White, "Usage: #npcedit walkspeed [Walk Speed] - Sets an NPC's Walk Speed"); + c->Message(Chat::White, "Usage: #npcedit show_name [Flag] - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); c->Message( Chat::White, - "#npcedit untargetable - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]" + "Usage: #npcedit untargetable [Flag] - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]" ); - c->Message(Chat::White, "#npcedit charm_ac - Sets an NPC's Armor Class while Charmed"); - c->Message(Chat::White, "#npcedit charm_min_dmg - Sets an NPC's Minimum Damage while Charmed"); - c->Message(Chat::White, "#npcedit charm_max_dmg - Sets an NPC's Max Damage while Charmed"); - c->Message(Chat::White, "#npcedit charm_attack_delay - Sets an NPC's Attack Delay while Charmed"); - c->Message(Chat::White, "#npcedit charm_accuracy_rating - Sets an NPC's Accuracy Rating while Charmed"); - c->Message(Chat::White, "#npcedit charm_avoidance_rating - Sets an NPC's Avoidance Rating while Charmed"); - c->Message(Chat::White, "#npcedit charm_atk - Sets an NPC's Attack while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_ac [Armor Class] - Sets an NPC's Armor Class while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_min_dmg [Damage] - Sets an NPC's Minimum Damage while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_max_dmg [Damage] - Sets an NPC's Maximum Damage while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_attack_delay [Attack Delay] - Sets an NPC's Attack Delay while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_accuracy_rating [Accuracy] - Sets an NPC's Accuracy Rating while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_avoidance_rating [Avoidance] - Sets an NPC's Avoidance Rating while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_atk [Attack] - Sets an NPC's Attack while Charmed"); c->Message( Chat::White, - "#npcedit skip_global_loot - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip" + "Usage: #npcedit skip_global_loot [Flag] - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip" ); c->Message( Chat::White, - "#npcedit rarespawn - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]" + "Usage: #npcedit rarespawn [Flag] - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]" ); c->Message( Chat::White, - "#npcedit stuck_behavior - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]" + "Usage: #npcedit stuck_behavior [Stuck Behavior] - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]" ); c->Message( Chat::White, - "#npcedit flymode - Sets an NPC's Fly Mode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" + "Usage: #npcedit flymode [Fly Mode] - Sets an NPC's Fly Mode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" ); c->Message( Chat::White, - "#npcedit always_aggro - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]" + "Usage: #npcedit always_aggro [Flag] - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]" ); c->Message( Chat::White, - "#npcedit exp_mod - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]" + "Usage: #npcedit exp_mod [Modifier] - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]" ); - c->Message(Chat::White, "#npcedit setanimation - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); + c->Message(Chat::White, "Usage: #npcedit setanimation [Animation ID] - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); c->Message( Chat::White, - "#npcedit respawntime - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)" + "Usage: #npcedit respawntime [Respawn Time] - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)" ); + return; } + std::string sub_command = sep->arg[1]; + uint32 npc_id = c->GetTarget()->CastToNPC()->GetNPCTypeID(); - if (strcasecmp(sep->arg[1], "name") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the name '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET name = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); - return; - } + auto npc_id_string = fmt::format( + "NPC ID {}", + commify(std::to_string(npc_id)) + ); - if (strcasecmp(sep->arg[1], "lastname") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has the lastname '{}'.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format("UPDATE npc_types SET lastname = '{}' WHERE id = {}", sep->arg[2], npc_id); - content_db.QueryDatabase(query); + if (!strcasecmp(sep->arg[1], "name")) { + std::string name = sep->argplus[2]; + if (!name.empty()) { + c->Message( + Chat::Yellow, + fmt::format( + "{} is now named '{}'.", + npc_id_string, + sep->argplus[2] + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET name = '{}' WHERE id = {}", + sep->argplus[2], + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit name [Name] - Sets an NPC's Name"); + } return; - } - - if (strcasecmp(sep->arg[1], "level") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now level {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET level = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "lastname")) { + std::string last_name = sep->argplus[2]; + if (!last_name.empty()) { + c->Message( + Chat::Yellow, + fmt::format( + "{} now has the lastname '{}'.", + npc_id_string, + sep->argplus[2] + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET lastname = '{}' WHERE id = {}", + sep->argplus[2], + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit lastname [Last Name] - Sets an NPC's Last Name"); + } return; - } - - if (strcasecmp(sep->arg[1], "race") == 0) { - auto race_id = atoi(sep->arg[2]); - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetRaceIDName(race_id), race_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET race = {} WHERE id = {}", race_id, npc_id); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "level")) { + if (sep->IsNumber(2)) { + auto level = static_cast(std::stoul(sep->arg[2])); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now level {}.", + npc_id_string, + level + ).c_str() + ); + auto query = fmt::format("UPDATE npc_types SET level = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit level [Level] - Sets an NPC's Level"); + } return; - } - - if (strcasecmp(sep->arg[1], "class") == 0) { - auto class_id = atoi(sep->arg[2]); - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now a {} ({}).", npc_id, GetClassIDName(class_id), class_id).c_str()); - std::string query = fmt::format("UPDATE npc_types SET class = {} WHERE id = {}", class_id, npc_id); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "race")) { + if (sep->IsNumber(2)) { + auto race_id = static_cast(std::stoul(sep->arg[2])); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now a {} ({}).", + npc_id_string, + GetRaceIDName(race_id), + race_id + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET race = {} WHERE id = {}", + race_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit race [Race ID] - Sets an NPC's Race"); + } return; - } - - if (strcasecmp(sep->arg[1], "bodytype") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is now using Bodytype {} ({}).", - npc_id, - EQ::constants::GetBodyTypeName(static_cast(std::stoul(sep->arg[2]))), - std::stoul(sep->arg[2]) - ).c_str() - ); - std::string query = fmt::format( - "UPDATE npc_types SET bodytype = {} WHERE id = {}", - std::stoul(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "class")) { + if (sep->IsNumber(2)) { + auto class_id = static_cast(std::stoul(sep->arg[2])); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now a {} ({}).", + npc_id_string, + GetClassIDName(class_id), + class_id + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET class = {} WHERE id = {}", + class_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit class [Class ID] - Sets an NPC's Class"); + } return; - } - - if (strcasecmp(sep->arg[1], "hp") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has {} Health.", - npc_id, - std::strtoll(sep->arg[2], nullptr, 10) - ).c_str() - ); - std::string query = fmt::format("UPDATE npc_types SET hp = {} WHERE id = {}", strtoull(sep->arg[2], nullptr, 10), npc_id); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "bodytype")) { + if (sep->IsNumber(2)) { + auto body_type_id = static_cast(std::stoul(sep->arg[2])); + auto body_type_name = EQ::constants::GetBodyTypeName(static_cast(body_type_id)); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Body Type {}.", + npc_id_string, + ( + !body_type_name.empty() ? + fmt::format( + "{} ({})", + body_type_name, + body_type_id + ) : + std::to_string(body_type_id) + ) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET bodytype = {} WHERE id = {}", + body_type_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit bodytype [Body Type ID] - Sets an NPC's Bodytype"); + } return; - } - - if (strcasecmp(sep->arg[1], "mana") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Mana.", npc_id, std::strtoll(sep->arg[2], nullptr, 10)).c_str()); - std::string query = fmt::format("UPDATE npc_types SET mana = {} WHERE id = {}", std::strtoll(sep->arg[2], nullptr, 10), npc_id); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "hp")) { + if (sep->IsNumber(2)) { + auto hp = std::stoll(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Health.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET hp = {} WHERE id = {}", + hp, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit hp [HP] - Sets an NPC's HP"); + } return; - } - - if (strcasecmp(sep->arg[1], "gender") == 0) { - auto gender_id = atoi(sep->arg[2]); - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now a {} ({}).", npc_id, gender_id, GetGenderName(gender_id)).c_str()); - std::string query = fmt::format("UPDATE npc_types SET gender = {} WHERE id = {}", gender_id, npc_id); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "mana")) { + if (sep->IsNumber(2)) { + auto mana = std::stoll(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Mana.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET mana = {} WHERE id = {}", + mana, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit mana [Mana] - Sets an NPC's Mana"); + } return; - } - - if (strcasecmp(sep->arg[1], "texture") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET texture = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "gender")) { + if (sep->IsNumber(2)) { + auto gender_id = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now a {} ({}).", + npc_id_string, + gender_id, + GetGenderName(gender_id) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET gender = {} WHERE id = {}", + gender_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit gender [Gender ID] - Sets an NPC's Gender"); + } return; - } - - if (strcasecmp(sep->arg[1], "helmtexture") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Helmet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET helmtexture = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "texture")) { + if (sep->IsNumber(2)) { + auto texture = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Texture {}.", + npc_id_string, + texture + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET texture = {} WHERE id = {}", + texture, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit texture [Texture] - Sets an NPC's Texture"); + } return; - } - - if (strcasecmp(sep->arg[1], "herosforgemodel") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Hero's Forge Model {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET herosforgemodel = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "size") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now Size {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET size = {:.2f} WHERE id = {}", atof(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hpregen") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now regenerates {} Health per Tick.", npc_id, std::strtoll(sep->arg[2], nullptr, 10)).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET hp_regen_rate = {} WHERE id = {}", - std::strtoll(sep->arg[2], nullptr, 10), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "hp_regen_per_second") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now regenerates {} HP per second.", - npc_id, - std::strtoll(sep->arg[2], nullptr, 10)).c_str() - ); - std::string query = fmt::format( - "UPDATE npc_types SET hp_regen_per_second = {} WHERE id = {}", - std::strtoll(sep->arg[2], nullptr, 10), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "manaregen") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now regenerates {} Mana per Tick.", npc_id, std::strtoll(sep->arg[2], nullptr, 10)).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET mana_regen_rate = {} WHERE id = {}", - std::strtoll(sep->arg[2], nullptr, 10), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "loottable") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using loottable ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET loottable_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "merchantid") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using merchant ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET merchant_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "alt_currency_id") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Alternate Currency ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET alt_currency_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spell") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Spell List ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET npc_spells_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "npc_spells_effects_id") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using NPC Spells Effects ID {}.", npc_id, sep->arg[2]).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET npc_spells_effects_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "faction") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Faction ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "adventure_template_id") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Adventure Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET adventure_template_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "trap_template") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Trap Template ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET trap_template = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "damage") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now hits from {} to {} damage.", - npc_id, + } else if (!strcasecmp(sep->arg[1], "helmtexture")) { + if (sep->IsNumber(2)) { + auto helmet_texture = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Helmet Texture {}.", + npc_id_string, + helmet_texture + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET helmtexture = {} WHERE id = {}", atoi(sep->arg[2]), - atoi(sep->arg[3])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET mindmg = {}, maxdmg = {} WHERE id = {}", - atoi(sep->arg[2]), - atoi(sep->arg[3]), - npc_id - ); - content_db.QueryDatabase(query); + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit helmtexture [Helmet Texture] - Sets an NPC's Helmet Texture"); + } return; - } - - if (strcasecmp(sep->arg[1], "attackcount") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has an Attack Count of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET attack_count = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "herosforgemodel")) { + if (sep->IsNumber(2)) { + auto heros_forge_model = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Hero's Forge Model {}.", + npc_id_string, + heros_forge_model + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET herosforgemodel = {} WHERE id = {}", + heros_forge_model, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit herosforgemodel [Model Number] - Sets an NPC's Hero's Forge Model"); + } return; - } - - if (strcasecmp(sep->arg[1], "special_attacks") == 0) { + } else if (!strcasecmp(sep->arg[1], "size")) { + if (sep->IsNumber(2)) { + auto size = std::stof(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now Size {:.2f}.", + npc_id_string, + size + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET size = {:.2f} WHERE id = {}", + size, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit size [Size] - Sets an NPC's Size"); + } + return; + } else if (!strcasecmp(sep->arg[1], "hpregen")) { + if (sep->IsNumber(2)) { + auto hp_regen = std::stoll(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now regenerates {} Health per Tick.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET hp_regen_rate = {} WHERE id = {}", + hp_regen, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit hpregen [HP Regen] - Sets an NPC's HP Regen Rate Per Tick"); + } + return; + } else if (!strcasecmp(sep->arg[1], "hp_regen_per_second")) { + if (sep->IsNumber(2)) { + auto hp_regen_per_second = std::stoll(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now regenerates {} HP per Second.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET hp_regen_per_second = {} WHERE id = {}", + hp_regen_per_second, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit hp_regen_per_second [HP Regen] - Sets an NPC's HP Regen Rate Per Second"); + } + return; + } else if (!strcasecmp(sep->arg[1], "manaregen")) { + if (sep->IsNumber(2)) { + auto mana_regen = std::stoll(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now regenerates {} Mana per Tick.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET mana_regen_rate = {} WHERE id = {}", + mana_regen, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit manaregen [Mana Regen] - Sets an NPC's Mana Regen Rate Per Tick"); + } + return; + } else if (!strcasecmp(sep->arg[1], "loottable")) { + if (sep->IsNumber(2)) { + auto loottable_id = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Loottable ID {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET loottable_id = {} WHERE id = {}", + loottable_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit loottable [Loottable ID] - Sets an NPC's Loottable ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "merchantid")) { + if (sep->IsNumber(2)) { + auto merchant_id = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Merchant ID {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET merchant_id = {} WHERE id = {}", + merchant_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit merchantid [Merchant ID] - Sets an NPC's Merchant ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "alt_currency_id")) { + if (sep->IsNumber(2)) { + auto alternate_currency_id = std::stoul(sep->arg[2]); + auto alternate_currency_item_id = zone->GetCurrencyItemID(alternate_currency_id); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Alternate Currency {}.", + npc_id_string, + ( + alternate_currency_item_id ? + fmt::format( + "{} ({})", + database.CreateItemLink(alternate_currency_item_id), + alternate_currency_id + ) : + std::to_string(alternate_currency_id) + ) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET alt_currency_id = {} WHERE id = {}", + alternate_currency_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit alt_currency_id [Alternate Currency ID] - Sets an NPC's Alternate Currency ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "spell")) { + if (sep->IsNumber(2)) { + auto spell_list_id = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Spell List ID {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET npc_spells_id = {} WHERE id = {}", + spell_list_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit spell [Spell List ID] - Sets an NPC's Spells List ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "npc_spells_effects_id")) { + if (sep->IsNumber(2)) { + auto spell_effects_id = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Spells Effects ID {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET npc_spells_effects_id = {} WHERE id = {}", + spell_effects_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit npc_spells_effects_id [Spell Effects ID] - Sets an NPC's Spell Effects ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "faction")) { + if (sep->IsNumber(2)) { + auto faction_id = std::stoi(sep->arg[2]); + auto faction_name = content_db.GetFactionName(faction_id); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Faction {}.", + npc_id_string, + ( + !faction_name.empty() ? + fmt::format( + "{} ({})", + faction_name, + faction_id + ) : + commify(sep->arg[2]) + ) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET npc_faction_id = {} WHERE id = {}", + faction_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit faction [Faction ID] - Sets an NPC's Faction ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "adventure_template_id")) { + if (sep->IsNumber(2)) { + auto adventure_template_id = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Adventure Template ID {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET adventure_template_id = {} WHERE id = {}", + adventure_template_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit adventure_template_id [Template ID] - Sets an NPC's Adventure Template ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "trap_template")) { + if (sep->IsNumber(2)) { + auto trap_template = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Trap Template ID {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET trap_template = {} WHERE id = {}", + trap_template, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit trap_template [Template ID] - Sets an NPC's Trap Template ID"); + } + return; + } else if (!strcasecmp(sep->arg[1], "damage")) { + if (sep->IsNumber(2) && sep->IsNumber(3)) { + auto minimum_damage = std::stoul(sep->arg[2]); + auto maximum_damage = std::stoul(sep->arg[3]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now hits from {} to {} damage.", + npc_id_string, + commify(sep->arg[2]), + commify(sep->arg[3]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET mindmg = {}, maxdmg = {} WHERE id = {}", + minimum_damage, + maximum_damage, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit damage [Minimum] [Maximum] - Sets an NPC's Damage"); + } + return; + } else if (!strcasecmp(sep->arg[1], "attackcount")) { + if (sep->IsNumber(2)) { + auto attack_count = std::stoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has an Attack Count of {}.", + npc_id_string, + attack_count + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET attack_count = {} WHERE id = {}", + attack_count, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit attackcount [Attack Count] - Sets an NPC's Attack Count"); + } + return; + } else if (!strcasecmp(sep->arg[1], "special_attacks")) { + std::string special_attacks = sep->argplus[2]; c->Message( Chat::Yellow, fmt::format( - "NPC ID {} is now using the following Special Attacks '{}'.", - npc_id, - sep->arg[2] - ).c_str()); - std::string query = fmt::format( + "{} is now using the following Special Attacks '{}'.", + npc_id_string, + special_attacks + ).c_str() + ); + auto query = fmt::format( "UPDATE npc_types SET npcspecialattks = '{}' WHERE id = {}", - sep->arg[2], + special_attacks, npc_id ); content_db.QueryDatabase(query); return; - } - - if (strcasecmp(sep->arg[1], "special_abilities") == 0) { + } else if (!strcasecmp(sep->arg[1], "special_abilities")) { + std::string special_abilities = sep->argplus[2]; c->Message( Chat::Yellow, fmt::format( - "NPC ID {} is now using the following Special Abilities '{}'.", - npc_id, - sep->arg[2] - ).c_str()); - std::string query = fmt::format( + "{} is now using the following Special Abilities '{}'.", + npc_id_string, + special_abilities + ).c_str() + ); + auto query = fmt::format( "UPDATE npc_types SET special_abilities = '{}' WHERE id = {}", - sep->arg[2], + special_abilities, npc_id ); content_db.QueryDatabase(query); return; - } - - if (strcasecmp(sep->arg[1], "aggroradius") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has an Aggro Radius of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET aggroradius = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "aggroradius")) { + if (sep->IsNumber(2)) { + auto aggro_radius = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has an Aggro Radius of {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET aggroradius = {} WHERE id = {}", + aggro_radius, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit aggroradius [Radius] - Sets an NPC's Aggro Radius"); + } return; - } - - if (strcasecmp(sep->arg[1], "assistradius") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has an Assist Radius of {}", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET assistradius = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); + } else if (!strcasecmp(sep->arg[1], "assistradius")) { + if (sep->IsNumber(2)) { + auto assist_radius = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has an Assist Radius of {}", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET assistradius = {} WHERE id = {}", + assist_radius, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit assistradius [Radius] - Sets an NPC's Assist Radius"); + } return; - } - - if (strcasecmp(sep->arg[1], "featuresave") == 0) { + } else if (!strcasecmp(sep->arg[1], "featuresave")) { c->Message( Chat::Yellow, - fmt::format("NPC ID {} saved with all current body and facial feature settings.", npc_id).c_str()); - Mob *target = c->GetTarget(); - std::string query = fmt::format( + fmt::format( + "{} saved with all current body and facial feature settings.", + npc_id + ).c_str() + ); + auto target = c->GetTarget(); + auto query = fmt::format( "UPDATE npc_types " "SET luclin_haircolor = {}, luclin_beardcolor = {}, " "luclin_eyecolor = {}, luclin_eyecolor2 = {}, " @@ -574,871 +869,1718 @@ void command_npcedit(Client *c, const Seperator *sep) ); content_db.QueryDatabase(query); return; - } - - if (strcasecmp(sep->arg[1], "armortint_id") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Armor Tint ID {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET armortint_id = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "color") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has {} Red, {} Green, and {} Blue tinting on their armor.", - npc_id, - atoi(sep->arg[2]), - atoi(sep->arg[3]), - atoi(sep->arg[4])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET armortint_red = {}, armortint_green = {}, armortint_blue = {} WHERE id = {}", - atoi(sep->arg[2]), - atoi(sep->arg[3]), - atoi(sep->arg[4]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ammoidfile") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Ammo ID File {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "weapon") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", - npc_id, - atoi(sep->arg[2]), - atoi(sep->arg[3])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET d_melee_texture1 = {}, d_melee_texture2 = {} WHERE id = {}", - atoi(sep->arg[2]), - atoi(sep->arg[3]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "meleetype") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has a Primary Melee Type of {} and a Secondary Melee Type of {}.", - npc_id, - atoi(sep->arg[2]), - atoi(sep->arg[3])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET prim_melee_type = {}, sec_melee_type = {} WHERE id = {}", - atoi(sep->arg[2]), - atoi(sep->arg[3]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "rangedtype") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Ranged Type of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET ranged_type = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "runspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now runs at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET runspeed = {:.2f} WHERE id = {}", - atof(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "mr") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET MR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "pr") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Poison Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET PR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "dr") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Disease Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET DR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "fr") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Fire Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET FR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "cr") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Cold Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET CR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "corrup") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Corruption Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET corrup = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "phr") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Physical Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET PhR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvis") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} can {} See Invisible.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_invis = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeinvisundead") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} can {} See Invisible vs. Undead.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET see_invis_undead = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "qglobal") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} can {} use Quest Globals.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET qglobal = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "ac") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Armor Class.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "npcaggro") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} will {} aggro other NPCs that have a hostile faction.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET npc_aggro = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spawn_limit") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Spawn Limit of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET spawn_limit = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackspeed") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has an Attack Speed of {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET attack_speed = {:.2f} WHERE id = {}", - atof(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "attackdelay") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has an Attack Delay of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET attack_delay = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "findable") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is {} Findable.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET findable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "str") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Strength.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET STR = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "sta") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Stamina.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET STA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "agi") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Agility.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET AGI = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "dex") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Dexterity.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET DEX = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "int") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Intelligence.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET _INT = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "wis") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Magic Resistance of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET WIS = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "cha") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Charisma.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET CHA = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seehide") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} can {} See Hide.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET see_hide = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "seeimprovedhide") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} can {} See Improved Hide.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET see_improved_hide = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "trackable") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is {} Trackable.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET trackable = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "atk") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Attack.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "accuracy") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Accuracy.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET accuracy = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "avoidance") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now has {} Avoidance.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET avoidance = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "slow_mitigation") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has {} Slow Mitigation.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET slow_mitigation = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "version") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} is now using Version {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET version = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "maxlevel") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Maximum Level of {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET maxlevel = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "scalerate") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET scalerate = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "spellscale") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Spell Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET spellscale = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "healscale") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has a Heal Scaling Rate of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET healscale = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "no_target") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is {} Targetable with Target Hotkey.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET no_target_hotkey = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "raidtarget") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is {} designated as a Raid Target.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET raid_target = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "armtexture") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Arm Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET armtexture = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "bracertexture") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Bracer Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET bracertexture = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "handtexture") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Hand Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET handtexture = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "legtexture") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Leg Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET legtexture = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "feettexture") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} is now using Feet Texture {}.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET feettexture = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "walkspeed") == 0) { - c->Message(Chat::Yellow, fmt::format("NPC ID {} now walks at {:.2f}.", npc_id, atof(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET walkspeed = {:.2f} WHERE id = {}", - atof(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "show_name") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} will {} show their name.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format("UPDATE npc_types SET show_name = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "untargetable") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} will {} be untargetable.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET untargetable = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_ac") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has {} Armor Class while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_ac = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_min_dmg") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now does {} Minimum Damage while Charmed.", - npc_id, - atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET charm_min_dmg = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_max_dmg") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now does {} Maximum Damage while Charmed.", - npc_id, - atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET charm_max_dmg = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_attack_delay") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has {} Attack Delay while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET charm_attack_delay = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_accuracy_rating") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has {} Accuracy Rating while Charmed.", - npc_id, - atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET charm_accuracy_rating = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_avoidance_rating") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has {} Avoidance Rating while Charmed.", - npc_id, - atoi(sep->arg[2])).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET charm_avoidance_rating = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "charm_atk") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has {} Attack while Charmed.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET charm_atk = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "skip_global_loot") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} will {} skip Global Loot.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET skip_global_loot = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "rarespawn") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is {} designated as a Rare Spawn.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET rare_spawn = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "stuck_behavior") == 0) { - auto behavior_id = atoi(sep->arg[2]); - std::string behavior_name = "Unknown"; - if (behavior_id == MobStuckBehavior::RunToTarget) { - behavior_name = "Run To Target"; - } - else if (behavior_id == MobStuckBehavior::WarpToTarget) { - behavior_name = "Warp To Target"; - } - else if (behavior_id == MobStuckBehavior::TakeNoAction) { - behavior_name = "Take No Action"; - } - else if (behavior_id == MobStuckBehavior::EvadeCombat) { - behavior_name = "Evade Combat"; - } - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is now using Stuck Behavior {} ({}).", - npc_id, - behavior_name, - behavior_id - ).c_str()); - std::string query = fmt::format("UPDATE npc_types SET stuck_behavior = {} WHERE id = {}", behavior_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "flymode") == 0) { - auto flymode_id = static_cast(std::stoul(sep->arg[2])); - std::string flymode_name = EQ::constants::GetFlyModeName(flymode_id); - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} is now using Fly Mode {} ({}).", - npc_id, - flymode_name, - flymode_id - ).c_str() - ); - std::string query = fmt::format("UPDATE npc_types SET flymode = {} WHERE id = {}", flymode_id, npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "always_aggro") == 0) { - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} will {} Always Aggro.", - npc_id, - (atoi(sep->arg[2]) == 1 ? "now" : "no longer")).c_str()); - std::string query = fmt::format( - "UPDATE npc_types SET always_aggro = {} WHERE id = {}", - atoi(sep->arg[2]), - npc_id - ); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "exp_mod") == 0) { - c->Message( - Chat::Yellow, - fmt::format("NPC ID {} now has an Experience Modifier of {}%%.", npc_id, atoi(sep->arg[2])).c_str()); - std::string query = fmt::format("UPDATE npc_types SET exp_mod = {} WHERE id = {}", atoi(sep->arg[2]), npc_id); - content_db.QueryDatabase(query); - return; - } - - if (strcasecmp(sep->arg[1], "setanimation") == 0) { - int animation = 0; - std::string animation_name = "Unknown"; - if (sep->arg[2] && atoi(sep->arg[2]) <= 4) { - if (strcasecmp(sep->arg[2], "stand") == 0 || atoi(sep->arg[2]) == 0) { // Stand - animation = 0; - animation_name = "Standing"; - } - else if (strcasecmp(sep->arg[2], "sit") == 0 || atoi(sep->arg[2]) == 1) { // Sit - animation = 1; - animation_name = "Sitting"; - } - else if (strcasecmp(sep->arg[2], "crouch") == 0 || atoi(sep->arg[2]) == 2) { // Crouch - animation = 2; - animation_name = "Crouching"; - } - else if (strcasecmp(sep->arg[2], "dead") == 0 || atoi(sep->arg[2]) == 3) { // Dead - animation = 3; - animation_name = "Dead"; - } - else if (strcasecmp(sep->arg[2], "loot") == 0 || atoi(sep->arg[2]) == 4) { // Looting Animation - animation = 4; - animation_name = "Looting"; - } - } - else { - c->Message( - Chat::White, - "You must specify an Animation (0 = Stand, 1 = Sit, 2 = Crouch, 3 = Dead, 4 = Loot)" - ); - c->Message(Chat::White, "Example: #npcedit setanimation sit"); - c->Message(Chat::White, "Example: #npcedit setanimation 0"); - return; - } - - c->Message( - Chat::Yellow, - fmt::format( - "NPC ID {} now has their Spawn Animation set to {} ({}) on Spawn Group ID {}.", - npc_id, - animation_name, - animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId() - ).c_str() - ); - std::string query = fmt::format( - "UPDATE spawn2 SET animation = {} WHERE spawngroupID = {}", - animation, - c->GetTarget()->CastToNPC()->GetSpawnGroupId() - ); - content_db.QueryDatabase(query); - - c->GetTarget()->SetAppearance(EmuAppearance(animation)); - return; - } - - if (strcasecmp(sep->arg[1], "respawntime") == 0) { - if (sep->arg[2][0] && sep->IsNumber(sep->arg[2]) && atoi(sep->arg[2]) > 0) { + } else if (!strcasecmp(sep->arg[1], "armortint_id")) { + if (sep->IsNumber(2)) { + auto armor_tint_id = std::stoul(sep->arg[2]); c->Message( Chat::Yellow, fmt::format( - "NPC ID {} now has a Respawn Timer of {} Seconds on Spawn Group ID {}.", - npc_id, - atoi(sep->arg[2]), - c->GetTarget()->CastToNPC()->GetSpawnGroupId()).c_str()); - std::string query = fmt::format( - "UPDATE spawn2 SET respawntime = {} WHERE spawngroupID = {} AND version = {}", - atoi(sep->arg[2]), - c->GetTarget()->CastToNPC()->GetSpawnGroupId(), - zone->GetInstanceVersion()); + "{} is now using Armor Tint ID {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET armortint_id = {} WHERE id = {}", + armor_tint_id, + npc_id + ); content_db.QueryDatabase(query); - return; + } else { + c->Message(Chat::White, "Usage: #npcedit armortint_id [Armor Tint ID] - Sets an NPC's Armor Tint ID"); } - } + return; + } else if (!strcasecmp(sep->arg[1], "color")) { + if (sep->IsNumber(2)) { + auto red = static_cast(std::stoul(sep->arg[2])); + uint8 green = sep->IsNumber(3) ? std::stoul(sep->arg[3]) : 0; + uint8 blue = sep->IsNumber(4) ? std::stoul(sep->arg[4]) : 0; + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Red, {} Green, and {} Blue tinting on their armor.", + npc_id_string, + red, + green, + blue + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET armortint_red = {}, armortint_green = {}, armortint_blue = {} WHERE id = {}", + red, + green, + blue, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit color [Red] [Green] [Blue] - Sets an NPC's Red, Green, and Blue armor tint"); + } + return; + } else if (!strcasecmp(sep->arg[1], "ammoidfile")) { + if (sep->IsNumber(2)) { + auto ammo_id_file = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Ammo ID File {}.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET ammo_idfile = {} WHERE id = {}", + ammo_id_file, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit ammoidfile [ID File] - Sets an NPC's Ammo ID File"); + } + return; + } else if (!strcasecmp(sep->arg[1], "weapon")) { + if (sep->IsNumber(2)) { + auto primary_model = std::stoul(sep->arg[2]); + uint32 secondary_model = sep->IsNumber(3) ? std::stoul(sep->arg[3]) : 0; + c->Message( + Chat::Yellow, + fmt::format( + "{} will have Model {} set to their Primary and Model {} set to their Secondary on repop.", + npc_id_string, + commify(sep->arg[2]), + sep->IsNumber(3) ? commify(sep->arg[3]) : 0 + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET d_melee_texture1 = {}, d_melee_texture2 = {} WHERE id = {}", + primary_model, + secondary_model, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit weapon [Primary Model] [Secondary Model] - Sets an NPC's Primary and Secondary Weapon Model" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "meleetype")) { + if (sep->IsNumber(2)) { + auto primary_type = std::stoul(sep->arg[2]); + uint32 secondary_type = sep->IsNumber(3) ? std::stoul(sep->arg[3]) : 0; - if ((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1], "*") == 0) || - ((c->GetTarget() == 0) || (c->GetTarget()->IsClient()))) { - c->Message(Chat::White, "Type #npcedit help for more info"); - } + auto primary_skill = EQ::skills::GetSkillName(static_cast(primary_type)); + auto secondary_skill = EQ::skills::GetSkillName(static_cast(secondary_type)); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Primary Melee Type of {} and a Secondary Melee Type of {}.", + npc_id_string, + ( + !primary_skill.empty() ? + fmt::format( + "{} ({})", + primary_skill, + primary_type + ) : + std::to_string(primary_type) + ), + ( + !secondary_skill.empty() ? + fmt::format( + "{} ({})", + secondary_skill, + secondary_type + ) : + std::to_string(secondary_type) + ) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET prim_melee_type = {}, sec_melee_type = {} WHERE id = {}", + primary_type, + secondary_type, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit meleetype [Primary Type] [Secondary Type] - Sets an NPC's Melee Skill Types"); + } + return; + } else if (!strcasecmp(sep->arg[1], "rangedtype")) { + if (sep->IsNumber(2)) { + auto ranged_type = std::stoul(sep->arg[2]); + + auto ranged_skill = EQ::skills::GetSkillName(static_cast(ranged_type)); + + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Ranged Type of {}.", + npc_id_string, + ( + !ranged_skill.empty() ? + fmt::format( + "{} ({})", + ranged_skill, + ranged_type + ) : + std::to_string(ranged_type) + ) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET ranged_type = {} WHERE id = {}", + ranged_type, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit rangedtype [Type] - Sets an NPC's Ranged Skill Type"); + } + return; + } else if (!strcasecmp(sep->arg[1], "runspeed")) { + if (sep->IsNumber(2)) { + auto run_speed = std::stof(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now runs at a Run Speed of {:.2f}.", + npc_id_string, + run_speed + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET runspeed = {:.2f} WHERE id = {}", + run_speed, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit runspeed [Run Speed] - Sets an NPC's Run Speed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "mr")) { + if (sep->IsNumber(2)) { + auto magic_resist = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Magic Resistance of {}.", + npc_id_string, + magic_resist + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET MR = {} WHERE id = {}", + magic_resist, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit mr [Resistance] - Sets an NPC's Magic Resistance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "pr")) { + if (sep->IsNumber(2)) { + auto poison_resist = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Poison Resistance of {}.", + npc_id_string, + poison_resist + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET PR = {} WHERE id = {}", + poison_resist, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit pr [Resistance] - Sets an NPC's Poison Resistance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "dr")) { + if (sep->IsNumber(2)) { + auto disease_resist = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Disease Resistance of {}.", + npc_id_string, + disease_resist + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET DR = {} WHERE id = {}", + disease_resist, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit dr [Resistance] - Sets an NPC's Disease Resistance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "fr")) { + if (sep->IsNumber(2)) { + auto fire_resist = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Fire Resistance of {}.", + npc_id_string, + fire_resist + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET FR = {} WHERE id = {}", + fire_resist, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit fr [Resistance] - Sets an NPC's Fire Resistance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "cr")) { + if (sep->IsNumber(2)) { + auto cold_resist = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Cold Resistance of {}.", + npc_id_string, + cold_resist + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET CR = {} WHERE id = {}", + cold_resist, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit cr [Resistance] - Sets an NPC's Cold Resistance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "corrup")) { + if (sep->IsNumber(2)) { + auto corruption_resist = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Corruption Resistance of {}.", + npc_id_string, + corruption_resist + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET corrup = {} WHERE id = {}", + corruption_resist, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit corrup [Resistance] - Sets an NPC's Corruption Resistance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "phr")) { + if (sep->IsNumber(2)) { + auto physical_resist = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Physical Resistance of {}.", + npc_id_string, + physical_resist + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET PhR = {} WHERE id = {}", + physical_resist, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit phr [Resistance] - Sets and NPC's Physical Resistance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "seeinvis")) { + if (sep->IsNumber(2)) { + auto see_invisible = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} can {} See Invisible.", + npc_id_string, + see_invisible ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET see_invis = {} WHERE id = {}", + see_invisible, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit seeinvis [Flag] - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "seeinvisundead")) { + if (sep->IsNumber(2)) { + auto see_invisible_undead = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} can {} See Invisible vs. Undead.", + npc_id_string, + see_invisible_undead ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET see_invis_undead = {} WHERE id = {}", + see_invisible_undead, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit seeinvisundead [Flag] - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "qglobal")) { + if (sep->IsNumber(2)) { + auto use_qglobals = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} can {} use Quest Globals.", + npc_id_string, + use_qglobals ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET qglobal = {} WHERE id = {}", + use_qglobals, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit qglobal [Flag] - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "ac")) { + if (sep->IsNumber(2)) { + auto armor_class = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Armor Class.", + npc_id_string, + armor_class + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET ac = {} WHERE id = {}", + armor_class, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit ac [Armor Class] - Sets an NPC's Armor Class"); + } + return; + } else if (!strcasecmp(sep->arg[1], "npcaggro")) { + if (sep->IsNumber(2)) { + auto aggro_npcs = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} will {} aggro other NPCs that have a hostile faction.", + npc_id_string, + aggro_npcs ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET npc_aggro = {} WHERE id = {}", + aggro_npcs, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit npcaggro [Flag] - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "spawn_limit")) { + if (sep->IsNumber(2)) { + auto spawn_limit = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Spawn Limit of {}.", + npc_id_string, + spawn_limit + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET spawn_limit = {} WHERE id = {}", + spawn_limit, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit spawn_limit [Limit] - Sets an NPC's Spawn Limit Counter"); + } + return; + } else if (!strcasecmp(sep->arg[1], "attackspeed")) { + if (sep->IsNumber(2)) { + auto attack_speed = std::stof(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has an Attack Speed of {:.2f}.", + npc_id_string, + attack_speed + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET attack_speed = {:.2f} WHERE id = {}", + attack_speed, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit attackspeed [Attack Speed] - Sets an NPC's Attack Speed Modifier"); + } + return; + } else if (!strcasecmp(sep->arg[1], "attackdelay")) { + if (sep->IsNumber(2)) { + auto attack_delay = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has an Attack Delay of {}.", + npc_id_string, + attack_delay + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET attack_delay = {} WHERE id = {}", + attack_delay, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit attackdelay [Attack Delay] - Sets an NPC's Attack Delay"); + } + return; + } else if (!strcasecmp(sep->arg[1], "findable")) { + if (sep->IsNumber(2)) { + auto is_findable = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is {} Findable.", + npc_id_string, + is_findable ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET findable = {} WHERE id = {}", + is_findable, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit findable [Flag] - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); + } + return; + } else if (!strcasecmp(sep->arg[1], "str")) { + if (sep->IsNumber(2)) { + auto strength = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Strength.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET STR = {} WHERE id = {}", + strength, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit str [Strength] - Sets an NPC's Strength"); + } + return; + } else if (!strcasecmp(sep->arg[1], "sta")) { + if (sep->IsNumber(2)) { + auto stamina = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Stamina.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET STA = {} WHERE id = {}", + stamina, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit sta [Stamina] - Sets an NPC's Stamina"); + } + return; + } else if (!strcasecmp(sep->arg[1], "agi")) { + if (sep->IsNumber(2)) { + auto agility = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Agility.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET AGI = {} WHERE id = {}", + agility, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit agi [Agility] - Sets an NPC's Agility"); + } + return; + } else if (!strcasecmp(sep->arg[1], "dex")) { + if (sep->IsNumber(2)) { + auto dexterity = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Dexterity.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET DEX = {} WHERE id = {}", + dexterity, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit dex [Dexterity] - Sets an NPC's Dexterity"); + } + return; + } else if (!strcasecmp(sep->arg[1], "int")) { + if (sep->IsNumber(2)) { + auto intelligence = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Intelligence.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET _INT = {} WHERE id = {}", + intelligence, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit int [Intelligence] - Sets an NPC's Intelligence"); + } + return; + } else if (!strcasecmp(sep->arg[1], "wis")) { + if (sep->IsNumber(2)) { + auto wisdom = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Wisdom.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET WIS = {} WHERE id = {}", + wisdom, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit wis [Wisdom] - Sets an NPC's Wisdom"); + } + return; + } else if (!strcasecmp(sep->arg[1], "cha")) { + if (sep->IsNumber(2)) { + auto charisma = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Charisma.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET CHA = {} WHERE id = {}", + charisma, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit cha [Charisma] - Sets an NPC's Charisma"); + } + return; + } else if (!strcasecmp(sep->arg[1], "seehide")) { + if (sep->IsNumber(2)) { + auto see_hide = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} can {} See Hide.", + npc_id_string, + see_hide ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET see_hide = {} WHERE id = {}", + see_hide, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit seehide [Flag] - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "seeimprovedhide")) { + if (sep->IsNumber(2)) { + auto see_improved_hide = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} can {} See Improved Hide.", + npc_id_string, + see_improved_hide ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET see_improved_hide = {} WHERE id = {}", + see_improved_hide, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit seeimprovedhide [Flag] - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "trackable")) { + if (sep->IsNumber(2)) { + auto is_trackable = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is {} Trackable.", + npc_id_string, + is_trackable ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET trackable = {} WHERE id = {}", + is_trackable, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit trackable [Flag] - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); + } + return; + } else if (!strcasecmp(sep->arg[1], "atk")) { + if (sep->IsNumber(2)) { + auto attack = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Attack.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET atk = {} WHERE id = {}", + attack, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit atk [Attack] - Sets an NPC's Attack"); + } + return; + } else if (!strcasecmp(sep->arg[1], "accuracy")) { + if (sep->IsNumber(2)) { + auto accuracy = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Accuracy.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET accuracy = {} WHERE id = {}", + accuracy, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit accuracy [Accuracy] - Sets an NPC's Accuracy"); + } + return; + } else if (!strcasecmp(sep->arg[1], "avoidance")) { + if (sep->IsNumber(2)) { + auto avoidance = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Avoidance.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET avoidance = {} WHERE id = {}", + avoidance, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit avoidance [Avoidance] - Sets an NPC's Avoidance"); + } + return; + } else if (!strcasecmp(sep->arg[1], "slow_mitigation")) { + if (sep->IsNumber(2)) { + auto slow_mitigation = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Slow Mitigation.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET slow_mitigation = {} WHERE id = {}", + slow_mitigation, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit slow_mitigation [Slow Mitigation] - Sets an NPC's Slow Mitigation"); + } + return; + } else if (!strcasecmp(sep->arg[1], "version")) { + if (sep->IsNumber(2)) { + auto version = std::stoi(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Version {}.", + npc_id_string, + version + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET version = {} WHERE id = {}", + version, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit version [Version] - Sets an NPC's Version"); + } + return; + } else if (!strcasecmp(sep->arg[1], "maxlevel")) { + if (sep->IsNumber(2)) { + auto max_level = static_cast(std::stoul(sep->arg[2])); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Maximum Level of {}.", + npc_id_string, + max_level + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET maxlevel = {} WHERE id = {}", + max_level, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit maxlevel [Max Level] - Sets an NPC's Maximum Level"); + } + return; + } else if (!strcasecmp(sep->arg[1], "scalerate")) { + if (sep->IsNumber(2)) { + auto scale_rate = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Scaling Rate of {}%%.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET scalerate = {} WHERE id = {}", + scale_rate, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit scalerate [Scale Rate] - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + } + return; + } else if (!strcasecmp(sep->arg[1], "spellscale")) { + if (sep->IsNumber(2)) { + auto spell_scale = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Spell Scaling Rate of {}%%.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET spellscale = {} WHERE id = {}", + spell_scale, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit spellscale [Scale Rate] - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "healscale")) { + if (sep->IsNumber(2)) { + auto heal_scale = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Heal Scaling Rate of {}%%.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET healscale = {} WHERE id = {}", + heal_scale, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit healscale [Scale Rate] - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "no_target")) { + if (sep->IsNumber(2)) { + auto is_no_target = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is {} Targetable with Target Hotkey.", + npc_id_string, + is_no_target ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET no_target_hotkey = {} WHERE id = {}", + is_no_target, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit no_target [Flag] - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "raidtarget")) { + if (sep->IsNumber(2)) { + auto is_raid_target = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is {} designated as a Raid Target.", + npc_id_string, + is_raid_target ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET raid_target = {} WHERE id = {}", + is_raid_target, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit raidtarget [Flag] - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "armtexture")) { + if (sep->IsNumber(2)) { + auto arm_texture = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Arm Texture {}.", + npc_id_string, + arm_texture + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET armtexture = {} WHERE id = {}", + arm_texture, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit armtexture [Texture] - Sets an NPC's Arm Texture"); + } + return; + } else if (!strcasecmp(sep->arg[1], "bracertexture")) { + if (sep->IsNumber(2)) { + auto bracer_texture = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Bracer Texture {}.", + npc_id_string, + bracer_texture + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET bracertexture = {} WHERE id = {}", + bracer_texture, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit bracertexture [Texture] - Sets an NPC's Bracer Texture"); + } + return; + } else if (!strcasecmp(sep->arg[1], "handtexture")) { + if (sep->IsNumber(2)) { + auto hand_texture = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Hand Texture {}.", + npc_id_string, + hand_texture + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET handtexture = {} WHERE id = {}", + hand_texture, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit handtexture [Texture] - Sets an NPC's Hand Texture"); + } + return; + } else if (!strcasecmp(sep->arg[1], "legtexture")) { + if (sep->IsNumber(2)) { + auto leg_texture = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Leg Texture {}.", + npc_id_string, + leg_texture + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET legtexture = {} WHERE id = {}", + leg_texture, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit legtexture [Texture] - Sets an NPC's Leg Texture"); + } + return; + } else if (!strcasecmp(sep->arg[1], "feettexture")) { + if (sep->IsNumber(2)) { + auto feet_texture = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Feet Texture {}.", + npc_id_string, + feet_texture + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET feettexture = {} WHERE id = {}", + feet_texture, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit feettexture [Texture] - Sets an NPC's Feet Texture"); + } + return; + } else if (!strcasecmp(sep->arg[1], "walkspeed")) { + if (sep->IsNumber(2)) { + auto walk_speed = std::stof(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now walks at a Walk Speed of {:.2f}.", + npc_id_string, + walk_speed + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET walkspeed = {:.2f} WHERE id = {}", + walk_speed, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit walkspeed [Walk Speed] - Sets an NPC's Walk Speed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "show_name")) { + if (sep->IsNumber(2)) { + auto show_name = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} will {} show their name.", + npc_id_string, + show_name ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET show_name = {} WHERE id = {}", + show_name, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit show_name [Flag] - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); + } + return; + } else if (!strcasecmp(sep->arg[1], "untargetable")) { + if (sep->IsNumber(2)) { + auto is_untargetable = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} will {} be untargetable.", + npc_id_string, + is_untargetable ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET untargetable = {} WHERE id = {}", + is_untargetable, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit untargetable [Flag] - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "charm_ac")) { + if (sep->IsNumber(2)) { + auto charm_armor_class = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Armor Class while Charmed.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET charm_ac = {} WHERE id = {}", + charm_armor_class, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit charm_ac [Armor Class] - Sets an NPC's Armor Class while Charmed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "charm_min_dmg")) { + if (sep->IsNumber(2)) { + auto charm_minimum_damage = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now does {} Minimum Damage while Charmed.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET charm_min_dmg = {} WHERE id = {}", + charm_minimum_damage, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit charm_min_dmg [Damage] - Sets an NPC's Minimum Damage while Charmed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "charm_max_dmg")) { + if (sep->IsNumber(2)) { + auto charm_maximum_damage = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now does {} Maximum Damage while Charmed.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET charm_max_dmg = {} WHERE id = {}", + charm_maximum_damage, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit charm_max_dmg [Damage] - Sets an NPC's Maximum Damage while Charmed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "charm_attack_delay")) { + if (sep->IsNumber(2)) { + auto charm_attack_delay = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Attack Delay while Charmed.", + npc_id_string, + charm_attack_delay + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET charm_attack_delay = {} WHERE id = {}", + charm_attack_delay, + npc_id + ); + content_db.QueryDatabase(query); + } + return; + } else if (!strcasecmp(sep->arg[1], "charm_accuracy_rating")) { + if (sep->IsNumber(2)) { + auto charm_accuracy_rating = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Accuracy Rating while Charmed.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET charm_accuracy_rating = {} WHERE id = {}", + charm_accuracy_rating, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit charm_accuracy_rating [Accuracy] - Sets an NPC's Accuracy Rating while Charmed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "charm_avoidance_rating")) { + if (sep->IsNumber(2)) { + auto charm_avoidance_rating = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Avoidance Rating while Charmed.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET charm_avoidance_rating = {} WHERE id = {}", + charm_avoidance_rating, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit charm_avoidance_rating [Avoidance] - Sets an NPC's Avoidance Rating while Charmed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "charm_atk")) { + if (sep->IsNumber(2)) { + auto charm_attack = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has {} Attack while Charmed.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET charm_atk = {} WHERE id = {}", + charm_attack, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Usage: #npcedit charm_atk [Attack] - Sets an NPC's Attack while Charmed"); + } + return; + } else if (!strcasecmp(sep->arg[1], "skip_global_loot")) { + if (sep->IsNumber(2)) { + auto skip_global_loot = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} will {} skip Global Loot.", + npc_id_string, + skip_global_loot ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET skip_global_loot = {} WHERE id = {}", + skip_global_loot, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit skip_global_loot [Flag] - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "rarespawn")) { + if (sep->IsNumber(2)) { + auto is_rare_spawn = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} is {} designated as a Rare Spawn.", + npc_id_string, + is_rare_spawn ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET rare_spawn = {} WHERE id = {}", + is_rare_spawn, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit rarespawn [Flag] - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "stuck_behavior")) { + if (sep->IsNumber(2)) { + auto behavior_id = ( + static_cast(std::stoul(sep->arg[2])) > EQ::constants::StuckBehavior::EvadeCombat ? + EQ::constants::StuckBehavior::EvadeCombat : + static_cast(std::stoul(sep->arg[2])) + ); + auto behavior_name = EQ::constants::GetStuckBehaviorName(behavior_id); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Stuck Behavior {}.", + npc_id_string, + ( + !behavior_name.empty() ? + fmt::format( + "{} ({})", + behavior_name, + behavior_id + ) : + std::to_string(behavior_id) + ) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET stuck_behavior = {} WHERE id = {}", + behavior_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit stuck_behavior [Stuck Behavior] - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "flymode")) { + if (sep->IsNumber(2)) { + auto flymode_id = ( + static_cast(std::stoul(sep->arg[2])) > GravityBehavior::LevitateWhileRunning ? + GravityBehavior::LevitateWhileRunning : + static_cast(std::stoul(sep->arg[2])) + ); + auto flymode_name = EQ::constants::GetFlyModeName(flymode_id); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Fly Mode {}.", + npc_id_string, + ( + !flymode_name.empty() ? + fmt::format( + "{} ({})", + flymode_name, + flymode_id + ) : + std::to_string(flymode_id) + ) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET flymode = {} WHERE id = {}", + flymode_id, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit flymode [Fly Mode] - Sets an NPC's Fly Mode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "always_aggro")) { + if (sep->IsNumber(2)) { + auto always_aggro = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} will {} Always Aggro.", + npc_id_string, + always_aggro ? "now" : "no longer" + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET always_aggro = {} WHERE id = {}", + always_aggro, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit always_aggro [Flag] - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "exp_mod")) { + if (sep->IsNumber(2)) { + auto experience_modifier = std::stoul(sep->arg[2]); + c->Message( + Chat::Yellow, + fmt::format( + "{} now has an Experience Modifier of {}%%.", + npc_id_string, + commify(sep->arg[2]) + ).c_str() + ); + auto query = fmt::format( + "UPDATE npc_types SET exp_mod = {} WHERE id = {}", + experience_modifier, + npc_id + ); + content_db.QueryDatabase(query); + } else { + c->Message( + Chat::White, + "Usage: #npcedit exp_mod [Modifier] - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]" + ); + } + return; + } else if (!strcasecmp(sep->arg[1], "setanimation")) { + if (sep->IsNumber(2)) { + auto animation_id = ( + std::stoul(sep->arg[2]) > EQ::constants::SpawnAnimations::Looting ? + EQ::constants::SpawnAnimations::Looting : + std::stoul(sep->arg[2]) + ); + auto animation_name = EQ::constants::GetSpawnAnimationName(animation_id); + c->Message( + Chat::Yellow, + fmt::format( + "{} is now using Spawn Animation {} on Spawn Group ID {}.", + npc_id_string, + ( + !animation_name.empty() ? + fmt::format( + "{} ({})", + animation_name, + animation_id + ) : + std::to_string(animation_id) + ), + commify(std::to_string(c->GetTarget()->CastToNPC()->GetSpawnGroupId())) + ).c_str() + ); + auto query = fmt::format( + "UPDATE spawn2 SET animation = {} WHERE spawngroupID = {}", + animation_id, + c->GetTarget()->CastToNPC()->GetSpawnGroupId() + ); + content_db.QueryDatabase(query); + + c->GetTarget()->SetAppearance(EmuAppearance(animation_id)); + } else { + c->Message(Chat::White, "Usage: #npcedit setanimation [Animation ID] - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); + } + return; + } else if (!strcasecmp(sep->arg[1], "respawntime")) { + if (sep->IsNumber(2)) { + auto respawn_time = std::stoul(sep->arg[2]); + if (respawn_time) { + c->Message( + Chat::Yellow, + fmt::format( + "{} now has a Respawn Timer of {} ({}) on Spawn Group ID {}.", + npc_id_string, + ConvertSecondsToTime(respawn_time), + respawn_time, + commify(std::to_string(c->GetTarget()->CastToNPC()->GetSpawnGroupId())) + ).c_str() + ); + auto query = fmt::format( + "UPDATE spawn2 SET respawntime = {} WHERE spawngroupID = {} AND version = {}", + respawn_time, + c->GetTarget()->CastToNPC()->GetSpawnGroupId(), + zone->GetInstanceVersion() + ); + content_db.QueryDatabase(query); + } else { + c->Message(Chat::White, "Respawn Timer must be greater than 0 seconds."); + return; + } + } else { + c->Message( + Chat::White, + "Usage: #npcedit respawntime [Respawn Time] - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)" + ); + } + return; + } else { + c->Message(Chat::White, "Usage: #npcedit name [Name] - Sets an NPC's Name"); + c->Message(Chat::White, "Usage: #npcedit lastname [Last Name] - Sets an NPC's Last Name"); + c->Message(Chat::White, "Usage: #npcedit level [Level] - Sets an NPC's Level"); + c->Message(Chat::White, "Usage: #npcedit race [Race ID] - Sets an NPC's Race"); + c->Message(Chat::White, "Usage: #npcedit class [Class ID] - Sets an NPC's Class"); + c->Message(Chat::White, "Usage: #npcedit bodytype [Body Type ID] - Sets an NPC's Bodytype"); + c->Message(Chat::White, "Usage: #npcedit hp [HP] - Sets an NPC's HP"); + c->Message(Chat::White, "Usage: #npcedit mana [Mana] - Sets an NPC's Mana"); + c->Message(Chat::White, "Usage: #npcedit gender [Gender ID] - Sets an NPC's Gender"); + c->Message(Chat::White, "Usage: #npcedit texture [Texture] - Sets an NPC's Texture"); + c->Message(Chat::White, "Usage: #npcedit helmtexture [Helmet Texture] - Sets an NPC's Helmet Texture"); + c->Message(Chat::White, "Usage: #npcedit herosforgemodel [Model Number] - Sets an NPC's Hero's Forge Model"); + c->Message(Chat::White, "Usage: #npcedit size [Size] - Sets an NPC's Size"); + c->Message(Chat::White, "Usage: #npcedit hpregen [HP Regen] - Sets an NPC's HP Regen Rate Per Tick"); + c->Message(Chat::White, "Usage: #npcedit hp_regen_per_second [HP Regen] - Sets an NPC's HP Regen Rate Per Second"); + c->Message(Chat::White, "Usage: #npcedit manaregen [Mana Regen] - Sets an NPC's Mana Regen Rate Per Tick"); + c->Message(Chat::White, "Usage: #npcedit loottable [Loottable ID] - Sets an NPC's Loottable ID"); + c->Message(Chat::White, "Usage: #npcedit merchantid [Merchant ID] - Sets an NPC's Merchant ID"); + c->Message(Chat::White, "Usage: #npcedit alt_currency_id [Alternate Currency ID] - Sets an NPC's Alternate Currency ID"); + c->Message(Chat::White, "Usage: #npcedit spell [Spell List ID] - Sets an NPC's Spells List ID"); + c->Message(Chat::White, "Usage: #npcedit npc_spells_effects_id [Spell Effects ID] - Sets an NPC's Spell Effects ID"); + c->Message(Chat::White, "Usage: #npcedit faction [Faction ID] - Sets an NPC's Faction ID"); + c->Message(Chat::White, "Usage: #npcedit adventure_template_id [Template ID] - Sets an NPC's Adventure Template ID"); + c->Message(Chat::White, "Usage: #npcedit trap_template [Template ID] - Sets an NPC's Trap Template ID"); + c->Message(Chat::White, "Usage: #npcedit damage [Minimum] [Maximum] - Sets an NPC's Damage"); + c->Message(Chat::White, "Usage: #npcedit attackcount [Attack Count] - Sets an NPC's Attack Count"); + c->Message(Chat::White, "Usage: #npcedit special_attacks [Special Attacks] - Sets an NPC's Special Attacks"); + c->Message(Chat::White, "Usage: #npcedit special_abilities [Special Abilities] - Sets an NPC's Special Abilities"); + c->Message(Chat::White, "Usage: #npcedit aggroradius [Radius] - Sets an NPC's Aggro Radius"); + c->Message(Chat::White, "Usage: #npcedit assistradius [Radius] - Sets an NPC's Assist Radius"); + c->Message(Chat::White, "Usage: #npcedit featuresave - Saves an NPC's current facial features to the database"); + c->Message(Chat::White, "Usage: #npcedit armortint_id [Armor Tint ID] - Sets an NPC's Armor Tint ID"); + c->Message(Chat::White, "Usage: #npcedit color [Red] [Green] [Blue] - Sets an NPC's Red, Green, and Blue armor tint"); + c->Message(Chat::White, "Usage: #npcedit ammoidfile [ID File] - Sets an NPC's Ammo ID File"); + c->Message( + Chat::White, + "Usage: #npcedit weapon [Primary Model] [Secondary Model] - Sets an NPC's Primary and Secondary Weapon Model" + ); + c->Message(Chat::White, "Usage: #npcedit meleetype [Primary Type] [Secondary Type] - Sets an NPC's Melee Skill Types"); + c->Message(Chat::White, "Usage: #npcedit rangedtype [Type] - Sets an NPC's Ranged Skill Type"); + c->Message(Chat::White, "Usage: #npcedit runspeed [Run Speed] - Sets an NPC's Run Speed"); + c->Message(Chat::White, "Usage: #npcedit mr [Resistance] - Sets an NPC's Magic Resistance"); + c->Message(Chat::White, "Usage: #npcedit pr [Resistance] - Sets an NPC's Poison Resistance"); + c->Message(Chat::White, "Usage: #npcedit dr [Resistance] - Sets an NPC's Disease Resistance"); + c->Message(Chat::White, "Usage: #npcedit fr [Resistance] - Sets an NPC's Fire Resistance"); + c->Message(Chat::White, "Usage: #npcedit cr [Resistance] - Sets an NPC's Cold Resistance"); + c->Message(Chat::White, "Usage: #npcedit corrup [Resistance] - Sets an NPC's Corruption Resistance"); + c->Message(Chat::White, "Usage: #npcedit phr [Resistance] - Sets and NPC's Physical Resistance"); + c->Message( + Chat::White, + "Usage: #npcedit seeinvis [Flag] - Sets an NPC's See Invisible Flag [0 = Cannot See Invisible, 1 = Can See Invisible]" + ); + c->Message( + Chat::White, + "Usage: #npcedit seeinvisundead [Flag] - Sets an NPC's See Invisible vs. Undead Flag [0 = Cannot See Invisible vs. Undead, 1 = Can See Invisible vs. Undead]" + ); + c->Message( + Chat::White, + "Usage: #npcedit qglobal [Flag] - Sets an NPC's Quest Global Flag [0 = Quest Globals Off, 1 = Quest Globals On]" + ); + c->Message(Chat::White, "Usage: #npcedit ac [Armor Class] - Sets an NPC's Armor Class"); + c->Message( + Chat::White, + "Usage: #npcedit npcaggro [Flag] - Sets an NPC's NPC Aggro Flag [0 = Aggro NPCs Off, 1 = Aggro NPCs On]" + ); + c->Message(Chat::White, "Usage: #npcedit spawn_limit [Limit] - Sets an NPC's Spawn Limit Counter"); + c->Message(Chat::White, "Usage: #npcedit attackspeed [Attack Speed] - Sets an NPC's Attack Speed Modifier"); + c->Message(Chat::White, "Usage: #npcedit attackdelay [Attack Delay] - Sets an NPC's Attack Delay"); + c->Message(Chat::White, "Usage: #npcedit findable [Flag] - Sets an NPC's Findable Flag [0 = Not Findable, 1 = Findable]"); + c->Message(Chat::White, "Usage: #npcedit str [Strength] - Sets an NPC's Strength"); + c->Message(Chat::White, "Usage: #npcedit sta [Stamina] - Sets an NPC's Stamina"); + c->Message(Chat::White, "Usage: #npcedit agi [Agility] - Sets an NPC's Agility"); + c->Message(Chat::White, "Usage: #npcedit dex [Dexterity] - Sets an NPC's Dexterity"); + c->Message(Chat::White, "Usage: #npcedit int [Intelligence] - Sets an NPC's Intelligence"); + c->Message(Chat::White, "Usage: #npcedit wis [Wisdom] - Sets an NPC's Wisdom"); + c->Message(Chat::White, "Usage: #npcedit cha [Charisma] - Sets an NPC's Charisma"); + c->Message( + Chat::White, + "Usage: #npcedit seehide [Flag] - Sets an NPC's See Hide Flag [0 = Cannot See Hide, 1 = Can See Hide]" + ); + c->Message( + Chat::White, + "Usage: #npcedit seeimprovedhide [Flag] - Sets an NPC's See Improved Hide Flag [0 = Cannot See Improved Hide, 1 = Can See Improved Hide]" + ); + c->Message(Chat::White, "Usage: #npcedit trackable [Flag] - Sets an NPC's Trackable Flag [0 = Not Trackable, 1 = Trackable]"); + c->Message(Chat::White, "Usage: #npcedit atk [Attack] - Sets an NPC's Attack"); + c->Message(Chat::White, "Usage: #npcedit accuracy [Accuracy] - Sets an NPC's Accuracy"); + c->Message(Chat::White, "Usage: #npcedit avoidance [Avoidance] - Sets an NPC's Avoidance"); + c->Message(Chat::White, "Usage: #npcedit slow_mitigation [Slow Mitigation] - Sets an NPC's Slow Mitigation"); + c->Message(Chat::White, "Usage: #npcedit version [Version] - Sets an NPC's Version"); + c->Message(Chat::White, "Usage: #npcedit maxlevel [Max Level] - Sets an NPC's Maximum Level"); + c->Message(Chat::White, "Usage: #npcedit scalerate [Scale Rate] - Sets an NPC's Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]"); + c->Message( + Chat::White, + "Usage: #npcedit spellscale [Scale Rate] - Sets an NPC's Spell Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message( + Chat::White, + "Usage: #npcedit healscale [Scale Rate] - Sets an NPC's Heal Scaling Rate [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message( + Chat::White, + "Usage: #npcedit no_target [Flag] - Sets an NPC's No Target Hotkey Flag [0 = Not Targetable with Target Hotkey, 1 = Targetable with Target Hotkey]" + ); + c->Message( + Chat::White, + "Usage: #npcedit raidtarget [Flag] - Sets an NPC's Raid Target Flag [0 = Not a Raid Target, 1 = Raid Target]" + ); + c->Message(Chat::White, "Usage: #npcedit armtexture [Texture] - Sets an NPC's Arm Texture"); + c->Message(Chat::White, "Usage: #npcedit bracertexture [Texture] - Sets an NPC's Bracer Texture"); + c->Message(Chat::White, "Usage: #npcedit handtexture [Texture] - Sets an NPC's Hand Texture"); + c->Message(Chat::White, "Usage: #npcedit legtexture [Texture] - Sets an NPC's Leg Texture"); + c->Message(Chat::White, "Usage: #npcedit feettexture [Texture] - Sets an NPC's Feet Texture"); + c->Message(Chat::White, "Usage: #npcedit walkspeed [Walk Speed] - Sets an NPC's Walk Speed"); + c->Message(Chat::White, "Usage: #npcedit show_name [Flag] - Sets an NPC's Show Name Flag [0 = Hidden, 1 = Shown]"); + c->Message( + Chat::White, + "Usage: #npcedit untargetable [Flag] - Sets an NPC's Untargetable Flag [0 = Targetable, 1 = Untargetable]" + ); + c->Message(Chat::White, "Usage: #npcedit charm_ac [Armor Class] - Sets an NPC's Armor Class while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_min_dmg [Damage] - Sets an NPC's Minimum Damage while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_max_dmg [Damage] - Sets an NPC's Maximum Damage while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_attack_delay [Attack Delay] - Sets an NPC's Attack Delay while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_accuracy_rating [Accuracy] - Sets an NPC's Accuracy Rating while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_avoidance_rating [Avoidance] - Sets an NPC's Avoidance Rating while Charmed"); + c->Message(Chat::White, "Usage: #npcedit charm_atk [Attack] - Sets an NPC's Attack while Charmed"); + c->Message( + Chat::White, + "Usage: #npcedit skip_global_loot [Flag] - Sets an NPC's Skip Global Loot Flag [0 = Don't Skip, 1 = Skip" + ); + c->Message( + Chat::White, + "Usage: #npcedit rarespawn [Flag] - Sets an NPC's Rare Spawn Flag [0 = Not a Rare Spawn, 1 = Rare Spawn]" + ); + c->Message( + Chat::White, + "Usage: #npcedit stuck_behavior [Stuck Behavior] - Sets an NPC's Stuck Behavior [0 = Run to Target, 1 = Warp to Target, 2 = Take No Action, 3 = Evade Combat]" + ); + c->Message( + Chat::White, + "Usage: #npcedit flymode [Fly Mode] - Sets an NPC's Fly Mode [0 = Ground, 1 = Flying, 2 = Levitating, 3 = Water, 4 = Floating, 5 = Levitating While Running]" + ); + c->Message( + Chat::White, + "Usage: #npcedit always_aggro [Flag] - Sets an NPC's Always Aggro Flag [0 = Does not Always Aggro, 1 = Always Aggro]" + ); + c->Message( + Chat::White, + "Usage: #npcedit exp_mod [Modifier] - Sets an NPC's Experience Modifier [50 = 50%, 100 = 100%, 200 = 200%]" + ); + c->Message(Chat::White, "Usage: #npcedit setanimation [Animation ID] - Sets an NPC's Animation on Spawn (Stored in spawn2 table)"); + c->Message( + Chat::White, + "Usage: #npcedit respawntime [Respawn Time] - Sets an NPC's Respawn Timer in Seconds (Stored in spawn2 table)" + ); + return; + } } diff --git a/zone/gm_commands/title.cpp b/zone/gm_commands/title.cpp index dff5a3ec4..669cf28b9 100755 --- a/zone/gm_commands/title.cpp +++ b/zone/gm_commands/title.cpp @@ -5,16 +5,12 @@ void command_title(Client *c, const Seperator *sep) { int arguments = sep->argnum; if (!arguments) { - c->Message( - Chat::White, - "Usage: #title [Remove|Title] [Save (0 = False, 1 = True)]" - ); + c->Message(Chat::White, "Usage: #title [Title] (use \"-1\" to remove title)"); return; } - bool is_remove = !strcasecmp(sep->arg[1], "remove"); - std::string title = is_remove ? "" : sep->arg[1]; - bool save_title = sep->IsNumber(2) ? atobool(sep->arg[2]) : false; + bool is_remove = !strcasecmp(sep->argplus[1], "-1"); + std::string title = is_remove ? "" : sep->argplus[1]; auto target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { @@ -30,9 +26,9 @@ void command_title(Client *c, const Seperator *sep) find_replace(title, "_", " "); } - if (!save_title || is_remove) { + if (is_remove) { target->SetAATitle(title); - } else if (save_title) { + } else { title_manager.CreateNewPlayerTitle(target, title); } @@ -43,7 +39,7 @@ void command_title(Client *c, const Seperator *sep) fmt::format( "Title has been {}{} for {}{}", is_remove ? "removed" : "changed", - !is_remove && save_title ? " and saved" : "", + !is_remove ? " and saved" : "", c->GetTargetDescription(target), ( is_remove ? diff --git a/zone/gm_commands/titlesuffix.cpp b/zone/gm_commands/titlesuffix.cpp index 34c971f64..eefa6614f 100755 --- a/zone/gm_commands/titlesuffix.cpp +++ b/zone/gm_commands/titlesuffix.cpp @@ -7,14 +7,13 @@ void command_titlesuffix(Client *c, const Seperator *sep) if (!arguments) { c->Message( Chat::White, - "Usage: #titlesuffix [Remove|Title] [Save (0 = False, 1 = True)]" + "Usage: #titlesuffix [Title Suffix] (use \"-1\" to remove title suffix)" ); return; } - bool is_remove = !strcasecmp(sep->arg[1], "remove"); - std::string suffix = is_remove ? "" : sep->arg[1]; - bool save_suffix = sep->IsNumber(2) ? atobool(sep->arg[2]) : false; + bool is_remove = !strcasecmp(sep->argplus[1], "-1"); + std::string suffix = is_remove ? "" : sep->argplus[1]; auto target = c; if (c->GetTarget() && c->GetTarget()->IsClient()) { @@ -30,9 +29,9 @@ void command_titlesuffix(Client *c, const Seperator *sep) find_replace(suffix, "_", " "); } - if (!save_suffix || is_remove) { + if (is_remove) { target->SetTitleSuffix(suffix); - } else if (save_suffix) { + } else { title_manager.CreateNewPlayerSuffix(target, suffix); } @@ -43,7 +42,7 @@ void command_titlesuffix(Client *c, const Seperator *sep) fmt::format( "Title suffix has been {}{} for {}{}", is_remove ? "removed" : "changed", - !is_remove && save_suffix ? " and saved" : "", + !is_remove ? " and saved" : "", c->GetTargetDescription(target), ( is_remove ? From 9e9ef6809b8222cf9b92c20cb6a0c82284601863 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 28 May 2022 14:35:17 -0400 Subject: [PATCH 033/552] [Cleanup] Cleanup spell and max level bucket logic. (#2181) * [Cleanup] Cleanup spell and max level bucket logic. - Spell buckets will now allow new mob->SetBucket() buckets since most people use these now. - Max level bucket will now allow new mob->SetBucket() bucket since most people use these now. - Clean up GetScribeableSpells() and GetLearnableDisciplines() logic and magic numbers. - Make GetClientMaxLevel() uint8 instead of int since it can only be 0-255. * Fix typo from other commit. * Lua setter. * Update client.cpp --- zone/client.cpp | 136 ++++++++++++++++++++++------------------- zone/client.h | 10 +-- zone/client_packet.cpp | 2 +- zone/embparser_api.cpp | 2 +- zone/exp.cpp | 67 +++++++++++--------- zone/lua_client.cpp | 6 +- zone/lua_client.h | 4 +- zone/perl_client.cpp | 8 +-- zone/spells.cpp | 88 ++++++++++++-------------- 9 files changed, 164 insertions(+), 159 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index a7f0f0cbd..b15cc5c2d 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -282,11 +282,9 @@ Client::Client(EQStreamInterface* ieqs) if (!RuleB(Character, PerCharacterQglobalMaxLevel) && !RuleB(Character, PerCharacterBucketMaxLevel)) { SetClientMaxLevel(0); } else if (RuleB(Character, PerCharacterQglobalMaxLevel)) { - int client_max_level = GetCharMaxLevelFromQGlobal(); - SetClientMaxLevel(client_max_level); + SetClientMaxLevel(GetCharMaxLevelFromQGlobal()); } else if (RuleB(Character, PerCharacterBucketMaxLevel)) { - int client_max_level = GetCharMaxLevelFromBucket(); - SetClientMaxLevel(client_max_level); + SetClientMaxLevel(GetCharMaxLevelFromBucket()); } KarmaUpdateTimer = new Timer(RuleI(Chat, KarmaUpdateIntervalMS)); @@ -10295,40 +10293,45 @@ void Client::Fling(float value, float target_x, float target_y, float target_z, } std::vector Client::GetLearnableDisciplines(uint8 min_level, uint8 max_level) { - bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals); - bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets); - bool SpellGlobalCheckResult = false; - bool SpellBucketCheckResult = false; std::vector learnable_disciplines; - for (int spell_id = 0; spell_id < SPDAT_RECORDS; ++spell_id) { + for (uint16 spell_id = 0; spell_id < SPDAT_RECORDS; ++spell_id) { bool learnable = false; - if (!IsValidSpell(spell_id)) - continue; - if (!IsDiscipline(spell_id)) - continue; - if (spells[spell_id].classes[WARRIOR] == 0) - continue; - if (max_level > 0 && spells[spell_id].classes[m_pp.class_ - 1] > max_level) - continue; - if (min_level > 1 && spells[spell_id].classes[m_pp.class_ - 1] < min_level) - continue; - if (spells[spell_id].skill == 52) - continue; - if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effect_id[EFFECT_COUNT - 1] == 10) - continue; - if (HasDisciplineLearned(spell_id)) + if (!IsValidSpell(spell_id)) { continue; + } - if (SpellGlobalRule) { - SpellGlobalCheckResult = SpellGlobalCheck(spell_id, CharacterID()); - if (SpellGlobalCheckResult) { - learnable = true; - } - } else if (SpellBucketRule) { - SpellBucketCheckResult = SpellBucketCheck(spell_id, CharacterID()); - if (SpellBucketCheckResult) { - learnable = true; - } + if (!IsDiscipline(spell_id)) { + continue; + } + + if (spells[spell_id].classes[WARRIOR] == 0) { + continue; + } + + if (max_level && spells[spell_id].classes[m_pp.class_ - 1] > max_level) { + continue; + } + + if (min_level > 1 && spells[spell_id].classes[m_pp.class_ - 1] < min_level) { + continue; + } + + if (spells[spell_id].skill == EQ::skills::SkillTigerClaw) { + continue; + } + + if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effect_id[EFFECT_COUNT - 1] == SE_CHA) { + continue; + } + + if (HasDisciplineLearned(spell_id)) { + continue; + } + + if (RuleB(Spells, EnableSpellGlobals) && SpellGlobalCheck(spell_id, CharacterID())) { + learnable = true; + } else if (RuleB(Spells, EnableSpellBuckets) && SpellBucketCheck(spell_id, CharacterID())) { + learnable = true; } else { learnable = true; } @@ -10361,40 +10364,45 @@ std::vector Client::GetMemmedSpells() { } std::vector Client::GetScribeableSpells(uint8 min_level, uint8 max_level) { - bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals); - bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets); - bool SpellGlobalCheckResult = false; - bool SpellBucketCheckResult = false; std::vector scribeable_spells; - for (int spell_id = 0; spell_id < SPDAT_RECORDS; ++spell_id) { + for (uint16 spell_id = 0; spell_id < SPDAT_RECORDS; ++spell_id) { bool scribeable = false; - if (!IsValidSpell(spell_id)) - continue; - if (IsDiscipline(spell_id)) - continue; - if (spells[spell_id].classes[WARRIOR] == 0) - continue; - if (max_level > 0 && spells[spell_id].classes[m_pp.class_ - 1] > max_level) - continue; - if (min_level > 1 && spells[spell_id].classes[m_pp.class_ - 1] < min_level) - continue; - if (spells[spell_id].skill == 52) - continue; - if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effect_id[EFFECT_COUNT - 1] == 10) - continue; - if (HasSpellScribed(spell_id)) + if (!IsValidSpell(spell_id)) { continue; + } - if (SpellGlobalRule) { - SpellGlobalCheckResult = SpellGlobalCheck(spell_id, CharacterID()); - if (SpellGlobalCheckResult) { - scribeable = true; - } - } else if (SpellBucketRule) { - SpellBucketCheckResult = SpellBucketCheck(spell_id, CharacterID()); - if (SpellBucketCheckResult) { - scribeable = true; - } + if (IsDiscipline(spell_id)) { + continue; + } + + if (spells[spell_id].classes[WARRIOR] == 0) { + continue; + } + + if (max_level && spells[spell_id].classes[m_pp.class_ - 1] > max_level) { + continue; + } + + if (min_level > 1 && spells[spell_id].classes[m_pp.class_ - 1] < min_level) { + continue; + } + + if (spells[spell_id].skill == EQ::skills::SkillTigerClaw) { + continue; + } + + if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effect_id[EFFECT_COUNT - 1] == SE_CHA) { + continue; + } + + if (HasSpellScribed(spell_id)) { + continue; + } + + if (RuleB(Spells, EnableSpellGlobals) && SpellGlobalCheck(spell_id, CharacterID())) { + scribeable = true; + } else if (RuleB(Spells, EnableSpellBuckets) && SpellBucketCheck(spell_id, CharacterID())) { + scribeable = true; } else { scribeable = true; } diff --git a/zone/client.h b/zone/client.h index f755ef432..180e30464 100644 --- a/zone/client.h +++ b/zone/client.h @@ -720,8 +720,8 @@ public: void SendGuildJoin(GuildJoin_Struct* gj); void RefreshGuildInfo(); - int GetClientMaxLevel() const { return client_max_level; } - void SetClientMaxLevel(int max_level) { client_max_level = max_level; } + uint8 GetClientMaxLevel() const { return client_max_level; } + void SetClientMaxLevel(uint8 max_level) { client_max_level = max_level; } void CheckManaEndUpdate(); void SendManaUpdate(); @@ -828,8 +828,8 @@ public: void UntrainDiscBySpellID(uint16 spell_id, bool update_client = true); bool SpellGlobalCheck(uint16 spell_id, uint32 char_id); bool SpellBucketCheck(uint16 spell_id, uint32 char_id); - uint32 GetCharMaxLevelFromQGlobal(); - uint32 GetCharMaxLevelFromBucket(); + uint8 GetCharMaxLevelFromQGlobal(); + uint8 GetCharMaxLevelFromBucket(); void Fling(float value, float target_x, float target_y, float target_z, bool ignore_los = false, bool clipping = false); @@ -2008,7 +2008,7 @@ private: void InterrogateInventory_(bool errorcheck, Client* requester, int16 head, int16 index, const EQ::ItemInstance* inst, const EQ::ItemInstance* parent, bool log, bool silent, bool &error, int depth); bool InterrogateInventory_error(int16 head, int16 index, const EQ::ItemInstance* inst, const EQ::ItemInstance* parent, int depth); - int client_max_level; + uint8 client_max_level; uint32 m_expedition_id = 0; ExpeditionInvite m_pending_expedition_invite { 0 }; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c97c60031..3c98f8e01 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1318,7 +1318,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) drakkin_details = m_pp.drakkin_details; // Max Level for Character:PerCharacterQglobalMaxLevel and Character:PerCharacterBucketMaxLevel - int client_max_level = 0; + uint8 client_max_level = 0; if (RuleB(Character, PerCharacterQglobalMaxLevel)) { client_max_level = GetCharMaxLevelFromQGlobal(); } else if (RuleB(Character, PerCharacterBucketMaxLevel)) { diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index e1164b760..18eddc546 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -8351,7 +8351,7 @@ XS(XS__checknamefilter); XS(XS__checknamefilter) { dXSARGS; if (items != 1) { - Perl_croak(aTHX_ "Usage: quest::checknamefilter(std::string name)"); + Perl_croak(aTHX_ "Usage: quest::checknamefilter(string name)"); } dXSTARG; diff --git a/zone/exp.cpp b/zone/exp.cpp index aad0f141b..b0fa4ac3a 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -22,6 +22,7 @@ #include "../common/string_util.h" #include "client.h" +#include "data_bucket.h" #include "groups.h" #include "mob.h" #include "raids.h" @@ -722,12 +723,12 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { } } - if (GetClientMaxLevel() > 0) { - int client_max_level = GetClientMaxLevel(); + auto client_max_level = GetClientMaxLevel(); + if (client_max_level) { if (GetLevel() >= client_max_level) { - uint32 expneeded = GetEXPForLevel(client_max_level); - if(set_exp > expneeded) { - set_exp = expneeded; + auto exp_needed = GetEXPForLevel(client_max_level); + if (set_exp > exp_needed) { + set_exp = exp_needed; } } } @@ -1148,44 +1149,52 @@ void Client::SendLeadershipEXPUpdate() { FastQueuePacket(&outapp); } -uint32 Client::GetCharMaxLevelFromQGlobal() { - QGlobalCache *char_c = nullptr; - char_c = GetQGlobals(); +uint8 Client::GetCharMaxLevelFromQGlobal() { + auto char_cache = GetQGlobals(); - std::list globalMap; - uint32 ntype = 0; + std::list global_map; - if(char_c) { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, CharacterID(), zone->GetZoneID()); + if (char_cache) { + QGlobalCache::Combine(global_map, char_cache->GetBucket(), 0, CharacterID(), zone->GetZoneID()); } - auto iter = globalMap.begin(); - uint32 gcount = 0; - while(iter != globalMap.end()) { - if((*iter).name.compare("CharMaxLevel") == 0){ - return atoi((*iter).value.c_str()); + for (const auto& global : global_map) { + if (global.name == "CharMaxLevel") { + if (StringIsNumber(global.value)) { + return static_cast(std::stoul(global.value)); + } } - ++iter; - ++gcount; } return 0; } -uint32 Client::GetCharMaxLevelFromBucket() +uint8 Client::GetCharMaxLevelFromBucket() { - uint32 char_id = CharacterID(); - std::string query = StringFormat("SELECT value FROM data_buckets WHERE `key` = '%i-CharMaxLevel'", char_id); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - LogError("Data bucket for CharMaxLevel for char ID [{}] failed", char_id); - return 0; + auto new_bucket_name = fmt::format( + "{}-CharMaxLevel", + GetBucketKey() + ); + + auto bucket_value = DataBucket::GetData(new_bucket_name); + if (!bucket_value.empty()) { + if (StringIsNumber(bucket_value)) { + return static_cast(std::stoul(bucket_value)); + } } - if (results.RowCount() > 0) { - auto row = results.begin(); - return atoi(row[0]); + auto old_bucket_name = fmt::format( + "{}-CharMaxLevel", + CharacterID() + ); + + bucket_value = DataBucket::GetData(old_bucket_name); + if (!bucket_value.empty()) { + if (StringIsNumber(bucket_value)) { + return static_cast(std::stoul(bucket_value)); + } } + return 0; } diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 4fba59888..69d5c2c25 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1844,12 +1844,12 @@ void Lua_Client::SetSecondaryWeaponOrnamentation(uint32 model_id) { self->SetSecondaryWeaponOrnamentation(model_id); } -void Lua_Client::SetClientMaxLevel(int value) { +void Lua_Client::SetClientMaxLevel(uint8 max_level) { Lua_Safe_Call_Void(); - self->SetClientMaxLevel(value); + self->SetClientMaxLevel(max_level); } -int Lua_Client::GetClientMaxLevel() { +uint8 Lua_Client::GetClientMaxLevel() { Lua_Safe_Call_Int(); return self->GetClientMaxLevel(); } diff --git a/zone/lua_client.h b/zone/lua_client.h index 5bb841c57..86a1ea552 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -423,8 +423,8 @@ public: void SetSecondaryWeaponOrnamentation(uint32 model_id); void TaskSelector(luabind::adl::object table); - void SetClientMaxLevel(int value); - int GetClientMaxLevel(); + void SetClientMaxLevel(uint8 max_level); + uint8 GetClientMaxLevel(); void DialogueWindow(std::string markdown); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 732d6cf55..81d6b8a3d 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -4759,12 +4759,12 @@ XS(XS_Client_SetClientMaxLevel); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetClientMaxLevel) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: Client::SetClientMaxLevel(THIS, int in_level)"); + Perl_croak(aTHX_ "Usage: Client::SetClientMaxLevel(THIS, uint8 max_level)"); { Client* THIS; - int in_level = (int)SvUV(ST(1)); + uint8 max_level = (uint8) SvUV(ST(1)); VALIDATE_THIS_IS_CLIENT; - THIS->SetClientMaxLevel(in_level); + THIS->SetClientMaxLevel(max_level); } XSRETURN_EMPTY; } @@ -4776,7 +4776,7 @@ XS(XS_Client_GetClientMaxLevel) { Perl_croak(aTHX_ "Usage: Client::GetClientMaxLevel(THIS)"); { Client* THIS; - int RETVAL; + uint8 RETVAL; dXSTARG; VALIDATE_THIS_IS_CLIENT; RETVAL = THIS->GetClientMaxLevel(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 6a448679d..a7b518655 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -78,6 +78,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include "../common/data_verification.h" #include "../common/misc_functions.h" +#include "data_bucket.h" #include "quest_parser_collection.h" #include "string_ids.h" #include "worldserver.h" @@ -5511,7 +5512,7 @@ uint32 Client::GetHighestScribedSpellinSpellGroup(uint32 spell_group) return highest_spell_id; } -bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { +bool Client::SpellGlobalCheck(uint16 spell_id, uint32 character_id) { std::string query = fmt::format( "SELECT qglobal, value FROM spell_globals WHERE spellid = {}", spell_id @@ -5536,7 +5537,7 @@ bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { query = fmt::format( "SELECT value FROM quest_globals WHERE charid = {} AND name = '{}'", - char_id, + character_id, EscapeString(spell_global_name) ); @@ -5546,7 +5547,7 @@ bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { "Spell global [{}] for spell ID [{}] for character ID [{}] query failed.", spell_global_name, spell_id, - char_id + character_id ); return false; // Query failed, do not allow scribing. @@ -5557,7 +5558,7 @@ bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { "Spell global [{}] for spell ID [{}] for character ID [{}] does not exist.", spell_global_name, spell_id, - char_id + character_id ); return false; // No rows found, do not allow scribing. @@ -5580,7 +5581,7 @@ bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { "Spell global [{}] for spell ID [{}] for character ID [{}] did not match value [{}] value found was [{}].", spell_global_name, spell_id, - char_id, + character_id, spell_global_value, global_value ); @@ -5588,7 +5589,7 @@ bool Client::SpellGlobalCheck(uint16 spell_id, uint32 char_id) { return false; } -bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) { +bool Client::SpellBucketCheck(uint16 spell_id, uint32 character_id) { auto query = fmt::format( "SELECT `key`, value FROM spell_buckets WHERE spellid = {}", spell_id @@ -5611,57 +5612,44 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) { return true; // If the entry in the spell_buckets table has nothing set for the qglobal name, allow scribing. } - query = fmt::format( - "SELECT value FROM data_buckets WHERE `key` = '{}-{}'", - char_id, - EscapeString(spell_bucket_name) + auto new_bucket_name = fmt::format( + "{}-{}", + GetBucketKey(), + spell_bucket_name ); - results = database.QueryDatabase(query); - if (!results.Success()) { - LogError( - "Spell bucket [{}] for spell ID [{}] for character ID [{}] query failed.", - spell_bucket_name, - spell_id, - char_id - ); - - return false; // Query failed, do not allow scribing. - } - - if (!results.RowCount()) { - LogError( - "Spell bucket [{}] for spell ID [{}] for character ID [{}] does not exist.", - spell_bucket_name, - spell_id, - char_id - ); - - return false; // No rows found, do not allow scribing. - } - - row = results.begin(); - std::string bucket_value = row[0]; - if (StringIsNumber(bucket_value) && StringIsNumber(spell_bucket_value)) { - if (std::stoi(bucket_value) >= std::stoi(spell_bucket_value)) { - return true; // If value is greater than or equal to spell bucket value, allow scribing. - } - } else { - if (bucket_value == spell_bucket_value) { - return true; // If value is equal to spell bucket value, allow scribing. + auto bucket_value = DataBucket::GetData(new_bucket_name); + if (!bucket_value.empty()) { + if (StringIsNumber(bucket_value) && StringIsNumber(spell_bucket_value)) { + if (std::stoi(bucket_value) >= std::stoi(spell_bucket_value)) { + return true; // If value is greater than or equal to spell bucket value, allow scribing. + } + } else { + if (bucket_value == spell_bucket_value) { + return true; // If value is equal to spell bucket value, allow scribing. + } } } - // If user's data bucket does not meet requirements, do not allow scribing. - LogError( - "Spell bucket [{}] for spell ID [{}] for character ID [{}] did not match value [{}] value found was [{}].", - spell_bucket_name, - spell_id, - char_id, - spell_bucket_value, - bucket_value + auto old_bucket_name = fmt::format( + "{}-{}", + character_id, + spell_bucket_name ); + bucket_value = DataBucket::GetData(old_bucket_name); + if (!bucket_value.empty()) { + if (StringIsNumber(bucket_value) && StringIsNumber(spell_bucket_value)) { + if (std::stoi(bucket_value) >= std::stoi(spell_bucket_value)) { + return true; // If value is greater than or equal to spell bucket value, allow scribing. + } + } else { + if (bucket_value == spell_bucket_value) { + return true; // If value is equal to spell bucket value, allow scribing. + } + } + } + return false; } From bcf7ccefcddf6fcf5f6d8b149247c6c0ea1e4376 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 29 May 2022 14:33:18 -0400 Subject: [PATCH 034/552] [Money Messages] Cleanup quest::givecash(), split, and task reward messages. (#2205) * [Money Messages] Cleanup quest::givecash(), split, and task reward messages. - Cleans up all the money messages using ConvertMoneyToString(). - Allows quest::givecash() to have optional parameters other than copper. * Commification. * Corpse messages. * String IDs and cleanup. --- common/string_util.cpp | 50 ++++++------- zone/corpse.cpp | 36 ++++++--- zone/embparser_api.cpp | 25 +++++-- zone/gm_commands/givemoney.cpp | 7 +- zone/groups.cpp | 125 ++++++++++++++------------------ zone/npc.cpp | 13 ++-- zone/questmgr.cpp | 72 ++++++++---------- zone/questmgr.h | 2 +- zone/raids.cpp | 129 +++++++++++++++------------------ zone/string_ids.h | 1 + zone/task_client_state.cpp | 54 ++++---------- 11 files changed, 243 insertions(+), 271 deletions(-) diff --git a/common/string_util.cpp b/common/string_util.cpp index aa469a76d..a13515254 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -1192,75 +1192,75 @@ std::string ConvertMoneyToString(uint32 platinum, uint32 gold, uint32 silver, ui if (copper && silver && gold && platinum) { // CSGP money_string = fmt::format( "{} Platinum, {} Gold, {} Silver, and {} Copper", - platinum, - gold, - silver, - copper + commify(std::to_string(platinum)), + commify(std::to_string(gold)), + commify(std::to_string(silver)), + commify(std::to_string(copper)) ); } else if (copper && silver && gold && !platinum) { // CSG money_string = fmt::format( "{} Gold, {} Silver, and {} Copper", - gold, - silver, - copper + commify(std::to_string(gold)), + commify(std::to_string(silver)), + commify(std::to_string(copper)) ); } else if (copper && silver && !gold && !platinum) { // CS money_string = fmt::format( "{} Silver and {} Copper", - silver, - copper + commify(std::to_string(silver)), + commify(std::to_string(copper)) ); } else if (!copper && silver && gold && platinum) { // SGP money_string = fmt::format( "{} Platinum, {} Gold, and {} Silver", - platinum, - gold, - silver + commify(std::to_string(platinum)), + commify(std::to_string(gold)), + commify(std::to_string(silver)) ); } else if (!copper && silver && gold && !platinum) { // SG money_string = fmt::format( "{} Gold and {} Silver", - gold, - silver + commify(std::to_string(gold)), + commify(std::to_string(silver)) ); } else if (copper && !silver && gold && platinum) { // CGP money_string = fmt::format( "{} Platinum, {} Gold, and {} Copper", - platinum, - gold, - copper + commify(std::to_string(platinum)), + commify(std::to_string(gold)), + commify(std::to_string(copper)) ); } else if (copper && !silver && gold && !platinum) { // CG money_string = fmt::format( "{} Gold and {} Copper", - gold, - copper + commify(std::to_string(gold)), + commify(std::to_string(copper)) ); } else if (!copper && !silver && gold && platinum) { // GP money_string = fmt::format( "{} Platinum and {} Gold", - platinum, - gold + commify(std::to_string(platinum)), + commify(std::to_string(gold)) ); } else if (!copper && !silver && !gold && platinum) { // P money_string = fmt::format( "{} Platinum", - platinum + commify(std::to_string(platinum)) ); } else if (!copper && !silver && gold && !platinum) { // G money_string = fmt::format( "{} Gold", - gold + commify(std::to_string(gold)) ); } else if (!copper && silver && !gold && !platinum) { // S money_string = fmt::format( "{} Silver", - silver + commify(std::to_string(silver)) ); } else if (copper && !silver && !gold && !platinum) { // C money_string = fmt::format( "{} Copper", - copper + commify(std::to_string(copper)) ); } return money_string; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 627d8f2c7..866d9e1a0 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -1019,8 +1019,27 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a loot_coin = (tmp[0] == 1 && tmp[1] == '\0'); if (loot_request_type == LootRequestType::GMPeek || loot_request_type == LootRequestType::GMAllowed) { - client->Message(Chat::Yellow, "This corpse contains %u platinum, %u gold, %u silver and %u copper.", - GetPlatinum(), GetGold(), GetSilver(), GetCopper()); + if ( + GetPlatinum() || + GetGold() || + GetSilver() || + GetCopper() + ) { + client->Message( + Chat::Yellow, + fmt::format( + "This corpse contains {}.", + ConvertMoneyToString( + GetPlatinum(), + GetGold(), + GetSilver(), + GetCopper() + ) + ).c_str() + ); + } else { + client->Message(Chat::Yellow, "This corpse contains no money."); + } auto outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); moneyOnCorpseStruct* d = (moneyOnCorpseStruct*)outapp->pBuffer; @@ -1505,13 +1524,12 @@ void Corpse::QueryLoot(Client* to) { } } - bool has_money = ( - platinum > 0 || - gold > 0 || - silver > 0 || - copper > 0 - ); - if (has_money) { + if ( + platinum || + gold || + silver || + copper + ) { to->Message( Chat::White, fmt::format( diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 18eddc546..7bb8a7405 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -1160,13 +1160,26 @@ XS(XS__untraindiscs) { XS(XS__givecash); XS(XS__givecash) { dXSARGS; - if (items != 4) - Perl_croak(aTHX_ "Usage: quest::givecash(int copper, int silver, int gold, int platinum)"); + if (items < 1 || items > 4) { + Perl_croak(aTHX_ "Usage: quest::givecash(uint32 copper, [uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0])"); + } - int copper = (int) SvIV(ST(0)); - int silver = (int) SvIV(ST(1)); - int gold = (int) SvIV(ST(2)); - int platinum = (int) SvIV(ST(3)); + uint32 copper = (uint32) SvUV(ST(0)); + uint32 silver = 0; + uint32 gold = 0; + uint32 platinum = 0; + + if (items > 1) { + silver = (uint32) SvUV(ST(1)); + } + + if (items > 2) { + gold = (uint32) SvUV(ST(2)); + } + + if (items > 3) { + platinum = (uint32) SvUV(ST(3)); + } quest_manager.givecash(copper, silver, gold, platinum); diff --git a/zone/gm_commands/givemoney.cpp b/zone/gm_commands/givemoney.cpp index 701bd2412..6be807778 100755 --- a/zone/gm_commands/givemoney.cpp +++ b/zone/gm_commands/givemoney.cpp @@ -35,7 +35,12 @@ void command_givemoney(Client *c, const Seperator *sep) Chat::White, fmt::format( "Added {} to {}.", - ConvertMoneyToString(platinum, gold, silver, copper), + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ), c->GetTargetDescription(target) ).c_str() ); diff --git a/zone/groups.cpp b/zone/groups.cpp index c82e56406..350fca8db 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -116,91 +116,76 @@ Group::~Group() //Split money used in OP_Split (/split and /autosplit). void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter) { //avoid unneeded work - if(copper == 0 && silver == 0 && gold == 0 && platinum == 0) + if ( + !copper && + !silver && + !gold && + !platinum + ) { return; + } - uint32 i; - uint8 membercount = 0; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { + uint8 member_count = 0; + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { // Don't split with Mercs or Bots - if (members[i] != nullptr && members[i]->IsClient()) { - membercount++; + if (members[i] && members[i]->IsClient()) { + member_count++; } } - if (membercount == 0) + if (!member_count) { return; + } - uint32 mod; - //try to handle round off error a little better - if(membercount > 1) { - mod = platinum % membercount; - if((mod) > 0) { - platinum -= mod; - gold += 10 * mod; + uint32 modifier; + if (member_count > 1) { + modifier = platinum % member_count; + + if (modifier) { + platinum -= modifier; + gold += 10 * modifier; } - mod = gold % membercount; - if((mod) > 0) { - gold -= mod; - silver += 10 * mod; + + modifier = gold % member_count; + + if (modifier) { + gold -= modifier; + silver += 10 * modifier; } - mod = silver % membercount; - if((mod) > 0) { - silver -= mod; - copper += 10 * mod; + + modifier = silver % member_count; + + if (modifier) { + silver -= modifier; + copper += 10 * modifier; } } - //calculate the splits - //We can still round off copper pieces, but I dont care - uint32 sc; - uint32 cpsplit = copper / membercount; - sc = copper % membercount; - uint32 spsplit = silver / membercount; - uint32 gpsplit = gold / membercount; - uint32 ppsplit = platinum / membercount; + auto copper_split = copper / member_count; + auto silver_split = silver / member_count; + auto gold_split = gold / member_count; + auto platinum_split = platinum / member_count; - char buf[128]; - buf[63] = '\0'; - std::string msg = "You receive"; - bool one = false; + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] && members[i]->IsClient()) { // If Group Member is Client + members[i]->CastToClient()->AddMoneyToPP( + copper_split, + silver_split, + gold_split, + platinum_split, + true + ); - if(ppsplit > 0) { - snprintf(buf, 63, " %u platinum", ppsplit); - msg += buf; - one = true; - } - if(gpsplit > 0) { - if(one) - msg += ","; - snprintf(buf, 63, " %u gold", gpsplit); - msg += buf; - one = true; - } - if(spsplit > 0) { - if(one) - msg += ","; - snprintf(buf, 63, " %u silver", spsplit); - msg += buf; - one = true; - } - if(cpsplit > 0) { - if(one) - msg += ","; - //this message is not 100% accurate for the splitter - //if they are receiving any roundoff - snprintf(buf, 63, " %u copper", cpsplit); - msg += buf; - one = true; - } - msg += " as your split"; - - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] != nullptr && members[i]->IsClient()) { // If Group Member is Client - Client *c = members[i]->CastToClient(); - //I could not get MoneyOnCorpse to work, so we use this - c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); - c->Message(Chat::Green, msg.c_str()); + members[i]->CastToClient()->MessageString( + Chat::MoneySplit, + YOU_RECEIVE_AS_SPLIT, + ConvertMoneyToString( + platinum_split, + gold_split, + silver_split, + copper_split + ).c_str() + ); } } } diff --git a/zone/npc.cpp b/zone/npc.cpp index 52f7348da..2beff9d7f 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -693,13 +693,12 @@ void NPC::QueryLoot(Client* to, bool is_pet_query) } if (!is_pet_query) { - bool has_money = ( - platinum > 0 || - gold > 0 || - silver > 0 || - copper > 0 - ); - if (has_money) { + if ( + platinum || + gold || + silver || + copper + ) { to->Message( Chat::White, fmt::format( diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index faf58eaa9..622f86c3a 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -1170,52 +1170,38 @@ void QuestManager::untraindiscs() { initiator->UntrainDiscAll(); } -void QuestManager::givecash(int copper, int silver, int gold, int platinum) { +void QuestManager::givecash(uint32 copper, uint32 silver, uint32 gold, uint32 platinum) { QuestManagerCurrentQuestVars(); - if (initiator && initiator->IsClient() && ((copper + silver + gold + platinum) > 0)) - { - initiator->AddMoneyToPP(copper, silver, gold, platinum, true); + if ( + initiator && + initiator->IsClient() && + ( + copper || + silver || + gold || + platinum + ) + ) { + initiator->AddMoneyToPP( + copper, + silver, + gold, + platinum, + true + ); - std::string tmp; - if (platinum > 0) - { - tmp = "You receive "; - tmp += itoa(platinum); - tmp += " platinum"; + if (initiator) { + initiator->MessageString( + Chat::MoneySplit, + YOU_RECEIVE, + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ).c_str() + ); } - if (gold > 0) - { - if (tmp.length() == 0) - tmp = "You receive "; - else - tmp += ","; - - tmp += itoa(gold); - tmp += " gold"; - } - if(silver > 0) - { - if (tmp.length() == 0) - tmp = "You receive "; - else - tmp += ","; - - tmp += itoa(silver); - tmp += " silver"; - } - if(copper > 0) - { - if (tmp.length() == 0) - tmp = "You receive "; - else - tmp += ","; - - tmp += itoa(copper); - tmp += " copper"; - } - tmp += " pieces."; - if (initiator) - initiator->Message(Chat::OOC, tmp.c_str()); } } diff --git a/zone/questmgr.h b/zone/questmgr.h index 96d8a51e6..1e8ab19a7 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -135,7 +135,7 @@ public: uint16 traindiscs(uint8 max_level, uint8 min_level = 1); void unscribespells(); void untraindiscs(); - void givecash(int copper, int silver, int gold, int platinum); + void givecash(uint32 copper, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0); void pvp(const char *mode); void movepc(int zone_id, float x, float y, float z, float heading); void gmmove(float x, float y, float z); diff --git a/zone/raids.cpp b/zone/raids.cpp index db57a4eed..002097fd9 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -24,6 +24,7 @@ #include "groups.h" #include "mob.h" #include "raids.h" +#include "string_ids.h" #include "worldserver.h" @@ -736,93 +737,79 @@ void Raid::BalanceMana(int32 penalty, uint32 gid, float range, Mob* caster, int3 void Raid::SplitMoney(uint32 gid, uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter) { //avoid unneeded work - if (gid == RAID_GROUPLESS) + if (gid == RAID_GROUPLESS) { return; + } - if(copper == 0 && silver == 0 && gold == 0 && platinum == 0) + if ( + !copper && + !silver && + !gold && + !platinum + ) { return; + } - uint32 i; - uint8 membercount = 0; - for (i = 0; i < MAX_RAID_MEMBERS; i++) { - if (members[i].member != nullptr && members[i].GroupNumber == gid) { - membercount++; + uint8 member_count = 0; + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) { + if (members[i].member && members[i].GroupNumber == gid) { + member_count++; } } - if (membercount == 0) + if (!member_count) { return; + } - uint32 mod; - //try to handle round off error a little better - if(membercount > 1) { - mod = platinum % membercount; - if((mod) > 0) { - platinum -= mod; - gold += 10 * mod; + uint32 modifier; + if (member_count > 1) { + modifier = platinum % member_count; + + if (modifier) { + platinum -= modifier; + gold += 10 * modifier; } - mod = gold % membercount; - if((mod) > 0) { - gold -= mod; - silver += 10 * mod; + + modifier = gold % member_count; + + if (modifier) { + gold -= modifier; + silver += 10 * modifier; } - mod = silver % membercount; - if((mod) > 0) { - silver -= mod; - copper += 10 * mod; + + modifier = silver % member_count; + + if (modifier) { + silver -= modifier; + copper += 10 * modifier; } } - //calculate the splits - //We can still round off copper pieces, but I dont care - uint32 sc; - uint32 cpsplit = copper / membercount; - sc = copper % membercount; - uint32 spsplit = silver / membercount; - uint32 gpsplit = gold / membercount; - uint32 ppsplit = platinum / membercount; + auto copper_split = copper / member_count; + auto silver_split = silver / member_count; + auto gold_split = gold / member_count; + auto platinum_split = platinum / member_count; - char buf[128]; - buf[63] = '\0'; - std::string msg = "You receive"; - bool one = false; + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) { + if (members[i].member && members[i].GroupNumber == gid) { // If Group Member is Client + members[i].member->AddMoneyToPP( + copper_split, + silver_split, + gold_split, + platinum_split, + true + ); - if(ppsplit > 0) { - snprintf(buf, 63, " %u platinum", ppsplit); - msg += buf; - one = true; - } - if(gpsplit > 0) { - if(one) - msg += ","; - snprintf(buf, 63, " %u gold", gpsplit); - msg += buf; - one = true; - } - if(spsplit > 0) { - if(one) - msg += ","; - snprintf(buf, 63, " %u silver", spsplit); - msg += buf; - one = true; - } - if(cpsplit > 0) { - if(one) - msg += ","; - //this message is not 100% accurate for the splitter - //if they are receiving any roundoff - snprintf(buf, 63, " %u copper", cpsplit); - msg += buf; - one = true; - } - msg += " as your split"; - - for (i = 0; i < MAX_RAID_MEMBERS; i++) { - if (members[i].member != nullptr && members[i].GroupNumber == gid) { // If Group Member is Client - //I could not get MoneyOnCorpse to work, so we use this - members[i].member->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); - - members[i].member->Message(Chat::Green, msg.c_str()); + members[i].member->MessageString( + Chat::MoneySplit, + YOU_RECEIVE_AS_SPLIT, + ConvertMoneyToString( + platinum_split, + gold_split, + silver_split, + copper_split + ).c_str() + ); } } } diff --git a/zone/string_ids.h b/zone/string_ids.h index d5b6c52f3..24fd2d22e 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -455,6 +455,7 @@ #define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE! #define FACE_ACCEPTED 12028 //Facial features accepted. #define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2. +#define YOU_RECEIVE_AS_SPLIT 12071 //You receive %1 as your split. #define ATTACKFAILED 12158 //%1 try to %2 %3, but %4! #define HIT_STRING 12183 //hit #define CRUSH_STRING 12191 //crush diff --git a/zone/task_client_state.cpp b/zone/task_client_state.cpp index 6d79a41e1..fa42ead87 100644 --- a/zone/task_client_state.cpp +++ b/zone/task_client_state.cpp @@ -1340,45 +1340,23 @@ void ClientTaskState::RewardTask(Client *client, TaskInformation *task_informati silver = copper / 10; copper = copper - (silver * 10); - std::string cash_message; - - if (platinum > 0) { - cash_message = "You receive "; - cash_message += itoa(platinum); - cash_message += " platinum"; + if ( + copper || + silver || + gold || + platinum + ) { + client->MessageString( + Chat::Yellow, + YOU_RECEIVE, + ConvertMoneyToString( + platinum, + gold, + silver, + copper + ).c_str() + ); } - if (gold > 0) { - if (cash_message.length() == 0) { - cash_message = "You receive "; - } - else { - cash_message += ","; - } - cash_message += itoa(gold); - cash_message += " gold"; - } - if (silver > 0) { - if (cash_message.length() == 0) { - cash_message = "You receive "; - } - else { - cash_message += ","; - } - cash_message += itoa(silver); - cash_message += " silver"; - } - if (copper > 0) { - if (cash_message.length() == 0) { - cash_message = "You receive "; - } - else { - cash_message += ","; - } - cash_message += itoa(copper); - cash_message += " copper"; - } - cash_message += " pieces."; - client->Message(Chat::Yellow, cash_message.c_str()); } int32 experience_reward = task_information->experience_reward; if (experience_reward > 0) { From 8f3ac74196ac7fec6485b86ca53025fae4348854 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 29 May 2022 14:33:30 -0400 Subject: [PATCH 035/552] [INT64] Fix int64 for OOC Regen and GetHP(), GetMaxHP(), GetItemHPBonuses() in Perl/Lua. (#2218) * [INT64] Fix int64 for OOC Regen and GetHP(), GetMaxHP(), GetItemHPBonuses() in Perl/Lua. - These all had int64 values and were overflowing, returning garbage data. * Update npc.cpp --- zone/lua_mob.cpp | 12 ++++++------ zone/lua_mob.h | 8 ++++---- zone/mob.h | 4 ++-- zone/npc.cpp | 8 ++++---- zone/perl_mob.cpp | 12 ++++++------ 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index b36499a71..ee84e3337 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -482,17 +482,17 @@ bool Lua_Mob::IsWarriorClass() { return self->IsWarriorClass(); } -int Lua_Mob::GetHP() { +int64 Lua_Mob::GetHP() { Lua_Safe_Call_Int(); return self->GetHP(); } -int Lua_Mob::GetMaxHP() { +int64 Lua_Mob::GetMaxHP() { Lua_Safe_Call_Int(); return self->GetMaxHP(); } -int Lua_Mob::GetItemHPBonuses() { +int64 Lua_Mob::GetItemHPBonuses() { Lua_Safe_Call_Int(); return self->GetItemHPBonuses(); } @@ -1356,9 +1356,9 @@ bool Lua_Mob::DivineAura() { return self->DivineAura(); } -void Lua_Mob::SetOOCRegen(int regen) { +void Lua_Mob::SetOOCRegen(int64 new_ooc_regen) { Lua_Safe_Call_Void(); - self->SetOOCRegen(regen); + self->SetOOCRegen(new_ooc_regen); } const char* Lua_Mob::GetEntityVariable(const char *name) { @@ -2860,7 +2860,7 @@ luabind::scope lua_register_mob() { .def("SetLevel", (void(Lua_Mob::*)(int))&Lua_Mob::SetLevel) .def("SetLevel", (void(Lua_Mob::*)(int,bool))&Lua_Mob::SetLevel) .def("SetMana", &Lua_Mob::SetMana) - .def("SetOOCRegen", (void(Lua_Mob::*)(int))&Lua_Mob::SetOOCRegen) + .def("SetOOCRegen", (void(Lua_Mob::*)(int64))&Lua_Mob::SetOOCRegen) .def("SetPet", &Lua_Mob::SetPet) .def("SetPetOrder", (void(Lua_Mob::*)(int))&Lua_Mob::SetPetOrder) .def("SetPseudoRoot", (void(Lua_Mob::*)(bool))&Lua_Mob::SetPseudoRoot) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index d1925917e..8bcef6541 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -123,9 +123,9 @@ public: void SetTarget(Lua_Mob t); double GetHPRatio(); bool IsWarriorClass(); - int GetHP(); - int GetMaxHP(); - int GetItemHPBonuses(); + int64 GetHP(); + int64 GetMaxHP(); + int64 GetItemHPBonuses(); int GetSpellHPBonuses(); double GetWalkspeed(); double GetRunspeed(); @@ -288,7 +288,7 @@ public: bool SetAA(int rank_id, int new_value); bool SetAA(int rank_id, int new_value, int charges); bool DivineAura(); - void SetOOCRegen(int regen); + void SetOOCRegen(int64 new_ooc_regen); const char* GetEntityVariable(const char *name); void SetEntityVariable(const char *name, const char *value); bool EntityVariableExists(const char *name); diff --git a/zone/mob.h b/zone/mob.h index 269572d03..f3cf67c65 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -497,7 +497,7 @@ public: bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0; virtual void SetHP(int64 hp); bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false); - inline void SetOOCRegen(int32 newoocregen) {ooc_regen = newoocregen;} + inline void SetOOCRegen(int64 new_ooc_regen) { ooc_regen = new_ooc_regen; } virtual void Heal(); virtual void HealDamage(uint64 ammount, Mob* caster = nullptr, uint16 spell_id = SPELL_UNKNOWN); virtual void SetMaxHP() { current_hp = max_hp; } @@ -1448,7 +1448,7 @@ protected: int64 hp_regen; int64 hp_regen_per_second; int64 mana_regen; - int32 ooc_regen; + int64 ooc_regen; uint8 maxlevel; uint32 scalerate; Buffs_Struct *buffs; diff --git a/zone/npc.cpp b/zone/npc.cpp index 2beff9d7f..1da68f366 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -884,10 +884,10 @@ bool NPC::Process() ProcessFlee(); } - uint32 npc_sitting_regen_bonus = 0; - uint32 pet_regen_bonus = 0; - uint64 npc_regen = 0; - int64 npc_hp_regen = GetNPCHPRegen(); + int64 npc_sitting_regen_bonus = 0; + int64 pet_regen_bonus = 0; + int64 npc_regen = 0; + int64 npc_hp_regen = GetNPCHPRegen(); if (GetAppearance() == eaSitting) { npc_sitting_regen_bonus += 3; diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 767f67267..e300a121d 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -1579,7 +1579,7 @@ XS(XS_Mob_GetHP) { Perl_croak(aTHX_ "Usage: Mob::GetHP(THIS)"); // @categories Stats and Attributes { Mob *THIS; - int32 RETVAL; + int64 RETVAL; dXSTARG; VALIDATE_THIS_IS_MOB; RETVAL = THIS->GetHP(); @@ -1596,7 +1596,7 @@ XS(XS_Mob_GetMaxHP) { Perl_croak(aTHX_ "Usage: Mob::GetMaxHP(THIS)"); // @categories Stats and Attributes { Mob *THIS; - int32 RETVAL; + int64 RETVAL; dXSTARG; VALIDATE_THIS_IS_MOB; RETVAL = THIS->GetMaxHP(); @@ -1613,7 +1613,7 @@ XS(XS_Mob_GetItemHPBonuses) { Perl_croak(aTHX_ "Usage: Mob::GetItemHPBonuses(THIS)"); // @categories Inventory and Items, Stats and Attributes { Mob *THIS; - int32 RETVAL; + int64 RETVAL; dXSTARG; VALIDATE_THIS_IS_MOB; RETVAL = THIS->GetItemHPBonuses(); @@ -4417,12 +4417,12 @@ XS(XS_Mob_SetOOCRegen); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_SetOOCRegen) { dXSARGS; if (items != 2) - Perl_croak(aTHX_ "Usage: Mob::SetOOCRegen(THIS, int32 new_ooc_regen)"); // @categories Stats and Attributes + Perl_croak(aTHX_ "Usage: Mob::SetOOCRegen(THIS, int64 new_ooc_regen)"); // @categories Stats and Attributes { Mob *THIS; - int32 newoocregen = (int32) SvIV(ST(1)); + int64 new_ooc_regen = (int64) SvIV(ST(1)); VALIDATE_THIS_IS_MOB; - THIS->SetOOCRegen(newoocregen); + THIS->SetOOCRegen(new_ooc_regen); } XSRETURN_EMPTY; } From b07945f0bec199b77f6c1db0c8493c50fffb3502 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 29 May 2022 14:33:35 -0400 Subject: [PATCH 036/552] [Commands] Cleanup #emptyinventory Command. (#2219) - Cleanup messages and logic. - Breakout #emptyinventory into its own command file. --- zone/command.cpp | 52 +-------------------------- zone/gm_commands/emptyinventory.cpp | 54 +++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 51 deletions(-) create mode 100644 zone/gm_commands/emptyinventory.cpp diff --git a/zone/command.cpp b/zone/command.cpp index 822d3c98e..eec6ff89c 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -986,57 +986,6 @@ void command_apply_shared_memory(Client *c, const Seperator *sep) { worldserver.SendPacket(&pack); } -void command_emptyinventory(Client *c, const Seperator *sep) -{ - Client *target = c; - if (c->GetGM() && c->GetTarget() && c->GetTarget()->IsClient()) { - target = c->GetTarget()->CastToClient(); - } - - EQ::ItemInstance *item = nullptr; - static const int16 slots[][2] = { - { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, - { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, - { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, - { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, - { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, - { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, - { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, - }; - int removed_count = 0; - const size_t size = sizeof(slots) / sizeof(slots[0]); - for (int slot_index = 0; slot_index < size; ++slot_index) { - for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { - item = target->GetInv().GetItem(slot_id); - if (item) { - int stack_size = std::max(static_cast(item->GetCharges()), 1); - removed_count += stack_size; - target->DeleteItemInInventory(slot_id, 0, true); - } - } - } - - if (removed_count) { - c->Message( - Chat::White, - fmt::format( - "Inventory cleared for {}, {} items deleted.", - c->GetTargetDescription(target), - removed_count - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "{} {} no items to delete.", - c->GetTargetDescription(target, TargetDescriptionType::UCYou), - c == target ? "have" : "has" - ).c_str() - ); - } -} - // All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block. #ifdef BOTS #include "bot_command.h" @@ -1110,6 +1059,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/emote.cpp" #include "gm_commands/emotesearch.cpp" #include "gm_commands/emoteview.cpp" +#include "gm_commands/emptyinventory.cpp" #include "gm_commands/enablerecipe.cpp" #include "gm_commands/endurance.cpp" #include "gm_commands/equipitem.cpp" diff --git a/zone/gm_commands/emptyinventory.cpp b/zone/gm_commands/emptyinventory.cpp new file mode 100644 index 000000000..582512289 --- /dev/null +++ b/zone/gm_commands/emptyinventory.cpp @@ -0,0 +1,54 @@ +#include "../client.h" + +void command_emptyinventory(Client *c, const Seperator *sep) +{ + auto target = c; + if (c->GetGM() && c->GetTarget() && c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + + EQ::ItemInstance *item = nullptr; + static const int16 slots[][2] = { + { EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END }, + { EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END }, + { EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END}, + { EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END }, + { EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END }, + { EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END }, + { EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END }, + }; + int removed_count = 0; + const size_t size = sizeof(slots) / sizeof(slots[0]); + for (int slot_index = 0; slot_index < size; ++slot_index) { + for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { + item = target->GetInv().GetItem(slot_id); + if (item) { + int stack_size = std::max(static_cast(item->GetCharges()), 1); + removed_count += stack_size; + target->DeleteItemInInventory(slot_id, 0, true); + } + } + } + + if (!removed_count) { + c->Message( + Chat::White, + fmt::format( + "{} {} no items to delete.", + c->GetTargetDescription(target, TargetDescriptionType::UCYou), + c == target ? "have" : "has" + ).c_str() + ); + return; + } + + c->Message( + Chat::White, + fmt::format( + "Inventory cleared for {}, {} item{} deleted.", + c->GetTargetDescription(target), + removed_count, + removed_count != 1 ? "s" : "" + ).c_str() + ); +} \ No newline at end of file From d493a6627b85784bf3ec795110afd86cad4e2503 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 29 May 2022 14:33:40 -0400 Subject: [PATCH 037/552] [Commands] Cleanup #nudge Command. (#2220) * [Commands] Cleanup #nudge Command. - Cleanup messages and logic. * Update nudge.cpp * Update nudge.cpp --- zone/gm_commands/nudge.cpp | 133 ++++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/zone/gm_commands/nudge.cpp b/zone/gm_commands/nudge.cpp index e8f4bfaa0..f396a4802 100755 --- a/zone/gm_commands/nudge.cpp +++ b/zone/gm_commands/nudge.cpp @@ -2,67 +2,78 @@ void command_nudge(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) { - c->Message(Chat::White, "Usage: #nudge [x=f] [y=f] [z=f] [h=f] (partial/mixed arguments allowed)"); - } - else { - - auto target = c->GetTarget(); - if (!target) { - - c->Message(Chat::Yellow, "This command requires a target."); - return; - } - if (target->IsMoving()) { - - c->Message(Chat::Yellow, "This command requires a stationary target."); - return; - } - - glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f); - for (auto index = 1; index <= 4; ++index) { - - if (!sep->arg[index]) { - continue; - } - - Seperator argsep(sep->arg[index], '='); - if (!argsep.arg[1][0]) { - continue; - } - - switch (argsep.arg[0][0]) { - case 'x': - position_offset.x = atof(argsep.arg[1]); - break; - case 'y': - position_offset.y = atof(argsep.arg[1]); - break; - case 'z': - position_offset.z = atof(argsep.arg[1]); - break; - case 'h': - position_offset.w = atof(argsep.arg[1]); - break; - default: - break; - } - } - - const auto ¤t_position = target->GetPosition(); - glm::vec4 new_position( - (current_position.x + position_offset.x), - (current_position.y + position_offset.y), - (current_position.z + position_offset.z), - (current_position.w + position_offset.w) - ); - - target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w); - + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #nudge [x=float] [y=float] [z=float] [h=float]"); c->Message( Chat::White, - "Nudging '%s' to {%1.3f, %1.3f, %1.3f, %1.2f} (adjustment: {%1.3f, %1.3f, %1.3f, %1.2f})", - target->GetName(), + fmt::format( + "Note: Partial or mixed arguments allowed, example {}.", + EQ::SayLinkEngine::GenerateQuestSaylink( + "#nudge x=5.0", + false, + "#nudge x=5.0" + ) + ).c_str() + ); + return; + } + + auto target = c->GetTarget(); + if (!target) { + c->Message(Chat::White, "You must have a target to use this command."); + return; + } + + if (target->IsMoving()) { + c->Message(Chat::White, "This command requires a stationary target."); + return; + } + + glm::vec4 position_offset(0.0f, 0.0f, 0.0f, 0.0f); + for (auto index = 1; index <= 4; ++index) { + if (!sep->arg[index]) { + continue; + } + + Seperator argsep(sep->arg[index], '='); + if (!argsep.arg[1][0]) { + continue; + } + + switch (argsep.arg[0][0]) { + case 'x': + position_offset.x = std::stof(argsep.arg[1]); + break; + case 'y': + position_offset.y = std::stof(argsep.arg[1]); + break; + case 'z': + position_offset.z = std::stof(argsep.arg[1]); + break; + case 'h': + position_offset.w = std::stof(argsep.arg[1]); + break; + default: + break; + } + } + + const auto& current_position = target->GetPosition(); + glm::vec4 new_position( + (current_position.x + position_offset.x), + (current_position.y + position_offset.y), + (current_position.z + position_offset.z), + (current_position.w + position_offset.w) + ); + + target->GMMove(new_position.x, new_position.y, new_position.z, new_position.w); + + c->Message( + Chat::White, + fmt::format( + "Nudging {} to {:.2f}, {:.2f}, {:.2f}, {:.2f} with offsets of {:.2f}, {:.2f}, {:.2f}, {:.2f}.", + c->GetTargetDescription(target), new_position.x, new_position.y, new_position.z, @@ -71,7 +82,7 @@ void command_nudge(Client *c, const Seperator *sep) position_offset.y, position_offset.z, position_offset.w - ); - } + ).c_str() + ); } From 11369247b1578d187d296907eafa9204e597b5ec Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sun, 29 May 2022 17:36:32 -0400 Subject: [PATCH 038/552] [INT64] Further int64 cleanup in Perl SetHP() and GetSpellHPBonuses() in Perl/Lua. (#2222) --- zone/lua_mob.cpp | 2 +- zone/lua_mob.h | 2 +- zone/mob.cpp | 4 ++-- zone/mob.h | 2 +- zone/perl_mob.cpp | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index ee84e3337..e99e968ad 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -497,7 +497,7 @@ int64 Lua_Mob::GetItemHPBonuses() { return self->GetItemHPBonuses(); } -int Lua_Mob::GetSpellHPBonuses() { +int64 Lua_Mob::GetSpellHPBonuses() { Lua_Safe_Call_Int(); return self->GetSpellHPBonuses(); } diff --git a/zone/lua_mob.h b/zone/lua_mob.h index 8bcef6541..a390dc6ab 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -126,7 +126,7 @@ public: int64 GetHP(); int64 GetMaxHP(); int64 GetItemHPBonuses(); - int GetSpellHPBonuses(); + int64 GetSpellHPBonuses(); double GetWalkspeed(); double GetRunspeed(); int GetCasterLevel(int spell_id); diff --git a/zone/mob.cpp b/zone/mob.cpp index 07b00830a..189ae6c8e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -981,8 +981,8 @@ int64 Mob::GetItemHPBonuses() { return item_hp; } -int32 Mob::GetSpellHPBonuses() { - int32 spell_hp = 0; +int64 Mob::GetSpellHPBonuses() { + int64 spell_hp = 0; spell_hp = spellbonuses.HP; spell_hp += spell_hp * spellbonuses.MaxHPChange / 10000; return spell_hp; diff --git a/zone/mob.h b/zone/mob.h index f3cf67c65..c1b0322ed 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -608,7 +608,7 @@ public: virtual int64 GetMaxEndurance() const { return 0; } virtual void SetEndurance(int32 newEnd) { return; } int64 GetItemHPBonuses(); - int32 GetSpellHPBonuses(); + int64 GetSpellHPBonuses(); virtual const int64& SetMana(int64 amount); inline float GetManaRatio() const { return max_mana == 0 ? 100 : ((static_cast(current_mana) / max_mana) * 100); } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index e300a121d..668857fc1 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -770,7 +770,7 @@ XS(XS_Mob_SetHP) { Perl_croak(aTHX_ "Usage: Mob::SetHP(THIS, int64 hp)"); // @categories Stats and Attributes { Mob *THIS; - int64 hp = (int32) SvIV(ST(1)); + int64 hp = (int64) SvIV(ST(1)); VALIDATE_THIS_IS_MOB; THIS->SetHP(hp); } @@ -1630,7 +1630,7 @@ XS(XS_Mob_GetSpellHPBonuses) { Perl_croak(aTHX_ "Usage: Mob::GetSpellHPBonuses(THIS)"); // @categories Spells and Disciplines { Mob *THIS; - int32 RETVAL; + int64 RETVAL; dXSTARG; VALIDATE_THIS_IS_MOB; RETVAL = THIS->GetSpellHPBonuses(); From 02e8b125a48b50241db03e6a28a165db731ea779 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 30 May 2022 22:10:49 -0400 Subject: [PATCH 039/552] [Hot Fix] Fix Linux compile due to missing include. (#2223) - Not sure how Windows compiles, but Linux fails. --- zone/groups.cpp | 1 + zone/questmgr.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/zone/groups.cpp b/zone/groups.cpp index 350fca8db..a93977aa3 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -25,6 +25,7 @@ #include "../common/packet_dump.h" #include "../common/string_util.h" #include "worldserver.h" +#include "string_ids.h" extern EntityList entity_list; extern WorldServer worldserver; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 622f86c3a..53e23533b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -37,6 +37,7 @@ #include "zonedb.h" #include "zone_store.h" #include "dialogue_window.h" +#include "string_ids.h" #include #include From ce8b8da0d66e0a5c113224a887f91964458608c6 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Mon, 30 May 2022 22:43:03 -0400 Subject: [PATCH 040/552] [Bug Fix] Fix Legacy Combat Lua Script (#2226) --- utils/mods/legacy_combat.lua | 69 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/utils/mods/legacy_combat.lua b/utils/mods/legacy_combat.lua index 6a031ec86..50fad15ca 100644 --- a/utils/mods/legacy_combat.lua +++ b/utils/mods/legacy_combat.lua @@ -8,40 +8,6 @@ * ]] -MonkACBonusWeight = RuleI.Get(Rule.MonkACBonusWeight); -NPCACFactor = RuleR.Get(Rule.NPCACFactor); -OldACSoftcapRules = RuleB.Get(Rule.OldACSoftcapRules); -ClothACSoftcap = RuleI.Get(Rule.ClothACSoftcap); -LeatherACSoftcap = RuleI.Get(Rule.LeatherACSoftcap); -MonkACSoftcap = RuleI.Get(Rule.MonkACSoftcap); -ChainACSoftcap = RuleI.Get(Rule.ChainACSoftcap); -PlateACSoftcap = RuleI.Get(Rule.PlateACSoftcap); -AAMitigationACFactor = RuleR.Get(Rule.AAMitigationACFactor); -WarriorACSoftcapReturn = RuleR.Get(Rule.WarriorACSoftcapReturn); -KnightACSoftcapReturn = RuleR.Get(Rule.KnightACSoftcapReturn); -LowPlateChainACSoftcapReturn = RuleR.Get(Rule.LowPlateChainACSoftcapReturn); -LowChainLeatherACSoftcapReturn = RuleR.Get(Rule.LowChainLeatherACSoftcapReturn); -CasterACSoftcapReturn = RuleR.Get(Rule.CasterACSoftcapReturn); -MiscACSoftcapReturn = RuleR.Get(Rule.MiscACSoftcapReturn); -WarACSoftcapReturn = RuleR.Get(Rule.WarACSoftcapReturn); -ClrRngMnkBrdACSoftcapReturn = RuleR.Get(Rule.ClrRngMnkBrdACSoftcapReturn); -PalShdACSoftcapReturn = RuleR.Get(Rule.PalShdACSoftcapReturn); -DruNecWizEncMagACSoftcapReturn = RuleR.Get(Rule.DruNecWizEncMagACSoftcapReturn); -RogShmBstBerACSoftcapReturn = RuleR.Get(Rule.RogShmBstBerACSoftcapReturn); -SoftcapFactor = RuleR.Get(Rule.SoftcapFactor); -ACthac0Factor = RuleR.Get(Rule.ACthac0Factor); -ACthac20Factor = RuleR.Get(Rule.ACthac20Factor); -BaseHitChance = RuleR.Get(Rule.BaseHitChance); -NPCBonusHitChance = RuleR.Get(Rule.NPCBonusHitChance); -HitFalloffMinor = RuleR.Get(Rule.HitFalloffMinor); -HitFalloffModerate = RuleR.Get(Rule.HitFalloffModerate); -HitFalloffMajor = RuleR.Get(Rule.HitFalloffMajor); -HitBonusPerLevel = RuleR.Get(Rule.HitBonusPerLevel); -AgiHitFactor = RuleR.Get(Rule.AgiHitFactor); -WeaponSkillFalloff = RuleR.Get(Rule.WeaponSkillFalloff); -ArcheryHitPenalty = RuleR.Get(Rule.ArcheryHitPenalty); -UseOldDamageIntervalRules = RuleB.Get(Rule.UseOldDamageIntervalRules); -CriticalMessageRange = RuleI.Get(Rule.CriticalDamage); --[[ * @@ -51,6 +17,41 @@ CriticalMessageRange = RuleI.Get(Rule.CriticalDamage); * ]] +MonkACBonusWeight = 15; +NPCACFactor = 2.25; +OldACSoftcapRules = false; +ClothACSoftcap = 75; +LeatherACSoftcap = 100; +MonkACSoftcap = 120; +ChainACSoftcap = 200; +PlateACSoftcap = 300; +AAMitigationACFactor = 3.0; +WarriorACSoftcapReturn = 0.45; +KnightACSoftcapReturn = 0.33; +LowPlateChainACSoftcapReturn = 0.23; +LowChainLeatherACSoftcapReturn = 0.17; +CasterACSoftcapReturn = 0.06; +MiscACSoftcapReturn = 0.3; +WarACSoftcapReturn = 0.3448; +ClrRngMnkBrdACSoftcapReturn = 0.3030; +PalShdACSoftcapReturn = 0.3226; +DruNecWizEncMagACSoftcapReturn = 0.2; +RogShmBstBerACSoftcapReturn = 0.25; +SoftcapFactor = 1.88; +ACthac0Factor = 0.55; +ACthac20Factor = 0.55; +BaseHitChance = 69.0; +NPCBonusHitChance = 26.0; +HitFalloffMinor = 5.0; +HitFalloffModerate = 7.0; +HitFalloffMajor = 50.0; +HitBonusPerLevel = 1.2; +AgiHitFactor = 0.01; +WeaponSkillFalloff = 0.33; +ArcheryHitPenalty = 0.25; +UseOldDamageIntervalRules = false; +CriticalMessageRange = RuleI.Get(Rule.CriticalDamage); + MeleeBaseCritChance = 0.0; ClientBaseCritChance = 0.0; BerserkBaseCritChance = 6.0; From 30e34c67b41fa66ff722995c72d547908b4bd54c Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Tue, 31 May 2022 15:46:14 -0400 Subject: [PATCH 041/552] [Quest API] Fix parameters in some Perl worldwide methods. (#2224) --- zone/embparser_api.cpp | 215 +++++++++++++++++++++++++++-------------- 1 file changed, 140 insertions(+), 75 deletions(-) diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 7bb8a7405..1549bdbd4 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -7691,15 +7691,18 @@ XS(XS__worldwideaddldonloss) { Perl_croak(aTHX_ "Usage: quest::worldwideaddldonloss(uint32 theme_id, [min_status = 0, max_status = 0])"); { uint8 update_type = CZLDoNUpdateSubtype_AddLoss; - uint32 theme_id = (uint32)SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(0)); int points = 1; uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8)SvUV(ST(1)); - if (items == 3) - max_status = (uint8)SvUV(ST(2)); + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { + max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } @@ -7710,21 +7713,25 @@ XS(XS__worldwideaddldonpoints); XS(XS__worldwideaddldonpoints) { dXSARGS; if (items < 1 || items > 4) - Perl_croak(aTHX_ "Usage: quest::worldwideaddldonpoints(uint32 theme_id. [int points = 1, min_status = 0, max_status = 0])"); + Perl_croak(aTHX_ "Usage: quest::worldwideaddldonpoints(uint32 theme_id, [int points = 1, min_status = 0, max_status = 0])"); { uint8 update_type = CZLDoNUpdateSubtype_AddPoints; - uint32 theme_id = (uint32)SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(0)); int points = 1; uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - points = (int)SvIV(ST(1)); - if (items == 3) - min_status = (uint8)SvUV(ST(2)); + if (items > 1) { + points = (int) SvIV(ST(1)); + } - if (items == 4) - max_status = (uint8)SvUV(ST(3)); + if (items > 2) { + min_status = (uint8) SvUV(ST(2)); + } + + if (items > 3) { + max_status = (uint8) SvUV(ST(3)); + } quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } @@ -7738,15 +7745,18 @@ XS(XS__worldwideaddldonwin) { Perl_croak(aTHX_ "Usage: quest::worldwideaddldonwin(uint32 theme_id, [min_status = 0, max_status = 0])"); { uint8 update_type = CZLDoNUpdateSubtype_AddWin; - uint32 theme_id = (uint32)SvUV(ST(0)); + uint32 theme_id = (uint32) SvUV(ST(0)); int points = 1; uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8)SvUV(ST(1)); - if (items == 3) - max_status = (uint8)SvUV(ST(2)); + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { + max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } @@ -7766,11 +7776,14 @@ XS(XS__worldwideassigntask) { int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } @@ -7787,11 +7800,14 @@ XS(XS__worldwidecastspell) { uint32 spell_id = (uint32) SvUV(ST(0)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideSpell(update_type, spell_id, min_status, max_status); } @@ -7807,11 +7823,14 @@ XS(XS__worldwidedialoguewindow) { const char* message = (const char*) SvPV_nolen(ST(0)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8)SvUV(ST(1)); - if (items == 3) - max_status = (uint8)SvUV(ST(2)); + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { + max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideDialogueWindow(message, min_status, max_status); } @@ -7831,11 +7850,14 @@ XS(XS__worldwidedisabletask) { int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } @@ -7855,11 +7877,14 @@ XS(XS__worldwideenabletask) { int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } @@ -7879,11 +7904,14 @@ XS(XS__worldwidefailtask) { int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } @@ -7904,11 +7932,14 @@ XS(XS__worldwidemarquee) { const char* message = (const char*) SvPV_nolen(ST(5)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 7) - min_status = (uint8) SvUV(ST(6)); - if (items == 8) + if (items > 6) { + min_status = (uint8) SvUV(ST(6)); + } + + if (items > 7) { max_status = (uint8) SvUV(ST(7)); + } quest_manager.WorldWideMarquee(type, priority, fade_in, fade_out, duration, message, min_status, max_status); } @@ -7925,11 +7956,14 @@ XS(XS__worldwidemessage) { const char* message = (const char*) SvPV_nolen(ST(1)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 3) - min_status = (uint8) SvUV(ST(2)); - if (items == 4) + if (items > 2) { + min_status = (uint8) SvUV(ST(2)); + } + + if (items > 3) { max_status = (uint8) SvUV(ST(3)); + } quest_manager.WorldWideMessage(type, message, min_status, max_status); } @@ -7947,11 +7981,14 @@ XS(XS__worldwidemove) { uint16 instance_id = 0; uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status, max_status); } @@ -7969,11 +8006,14 @@ XS(XS__worldwidemoveinstance) { uint16 instance_id = (uint16) SvUV(ST(0)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideMove(update_type, zone_short_name, instance_id, min_status, max_status); } @@ -7991,11 +8031,14 @@ XS(XS__worldwideremoveldonloss) { int points = 1; uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8)SvUV(ST(1)); - if (items == 3) - max_status = (uint8)SvUV(ST(2)); + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { + max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } @@ -8013,11 +8056,14 @@ XS(XS__worldwideremoveldonwin) { int points = 1; uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8)SvUV(ST(1)); - if (items == 3) - max_status = (uint8)SvUV(ST(2)); + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { + max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideLDoNUpdate(update_type, theme_id, points, min_status, max_status); } @@ -8034,11 +8080,14 @@ XS(XS__worldwideremovespell) { uint32 spell_id = (uint32) SvUV(ST(0)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideSpell(update_type, spell_id, min_status, max_status); } @@ -8058,11 +8107,14 @@ XS(XS__worldwideremovetask) { int task_subidentifier = -1; int update_count = 1; bool enforce_level_requirement = false; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } @@ -8083,11 +8135,14 @@ XS(XS__worldwideresetactivity) { uint8 max_status = AccountStatus::Player; int update_count = 1; bool enforce_level_requirement = false; - if (items == 3) - min_status = (uint8) SvUV(ST(2)); - if (items == 4) + if (items > 2) { + min_status = (uint8) SvUV(ST(2)); + } + + if (items > 3) { max_status = (uint8) SvUV(ST(3)); + } quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } @@ -8105,11 +8160,14 @@ XS(XS__worldwidesetentityvariableclient) { const char* variable_value = (const char*) SvPV_nolen(ST(1)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 3) - min_status = (uint8) SvUV(ST(2)); - if (items == 4) + if (items > 2) { + min_status = (uint8) SvUV(ST(2)); + } + + if (items > 3) { max_status = (uint8) SvUV(ST(3)); + } quest_manager.WorldWideSetEntityVariable(update_type, variable_name, variable_value, min_status, max_status); } @@ -8153,11 +8211,14 @@ XS(XS__worldwidesignalclient) { uint32 signal = (uint32) SvUV(ST(0)); uint8 min_status = AccountStatus::Player; uint8 max_status = AccountStatus::Player; - if (items == 2) - min_status = (uint8) SvUV(ST(1)); - if (items == 3) + if (items > 1) { + min_status = (uint8) SvUV(ST(1)); + } + + if (items > 2) { max_status = (uint8) SvUV(ST(2)); + } quest_manager.WorldWideSignal(update_type, signal, min_status, max_status); } @@ -8177,14 +8238,18 @@ XS(XS__worldwideupdateactivity) { uint8 max_status = AccountStatus::Player; int update_count = 1; bool enforce_level_requirement = false; - if (items == 3) + + if (items > 2) { update_count = (int) SvIV(ST(2)); + } - if (items == 4) + if (items > 3) { min_status = (uint8) SvUV(ST(3)); + } - if (items == 5) + if (items > 4) { max_status = (uint8) SvUV(ST(4)); + } quest_manager.WorldWideTaskUpdate(update_type, task_identifier, task_subidentifier, update_count, enforce_level_requirement, min_status, max_status); } From 86c9be410d6e0e45bd391557ca24a4b6e91cd2b3 Mon Sep 17 00:00:00 2001 From: titanium-forever <95503076+titanium-forever@users.noreply.github.com> Date: Wed, 1 Jun 2022 00:25:10 +0100 Subject: [PATCH 042/552] [Database Backup] Enable database dump of bot data (#2221) * Add option to dump bot data * Add player_bot_table dump suppor to command handler * Add tableList getter to the dump_service * Fix declaration in header file * Include missed bot tables * Rename player-bot to bot to be more descriptive * Fix missed reference to player-bots Co-authored-by: Kieren Hinch --- common/database/database_dump_service.cpp | 30 ++++++++++++++++++++ common/database/database_dump_service.h | 4 +++ common/database_schema.h | 34 +++++++++++++++++++++++ world/world_server_command_handler.cpp | 9 ++++++ 4 files changed, 77 insertions(+) diff --git a/common/database/database_dump_service.cpp b/common/database/database_dump_service.cpp index 7efd80f83..8fdb1a16d 100644 --- a/common/database/database_dump_service.cpp +++ b/common/database/database_dump_service.cpp @@ -163,6 +163,20 @@ std::string DatabaseDumpService::GetPlayerTablesList() return trim(tables_list); } +/** + * @return + */ +std::string DatabaseDumpService::GetBotTablesList() +{ + std::string tables_list; + std::vector tables = DatabaseSchema::GetBotTables(); + for (const auto &table : tables) { + tables_list += table + " "; + } + + return trim(tables_list); +} + /** * @return */ @@ -317,6 +331,11 @@ void DatabaseDumpService::Dump() tables_to_dump += GetPlayerTablesList() + " "; dump_descriptor += "-player"; } + + if (IsDumpBotTables()) { + tables_to_dump += GetBotTablesList() + " "; + dump_descriptor += "-bots"; + } if (IsDumpSystemTables()) { tables_to_dump += GetSystemTablesList() + " "; @@ -436,6 +455,7 @@ void DatabaseDumpService::Dump() // LogDebug("[{}] login", (IsDumpLoginServerTables() ? "true" : "false")); // LogDebug("[{}] player", (IsDumpPlayerTables() ? "true" : "false")); // LogDebug("[{}] system", (IsDumpSystemTables() ? "true" : "false")); +// LogDebug("[{}] bot", (IsDumpBotTables() ? "true" : "false")); } bool DatabaseDumpService::IsDumpSystemTables() const @@ -577,3 +597,13 @@ void DatabaseDumpService::SetDumpStateTables(bool dump_state_tables) { DatabaseDumpService::dump_state_tables = dump_state_tables; } + +bool DatabaseDumpService::IsDumpBotTables() const +{ + return dump_bot_tables; +} + +void DatabaseDumpService::SetDumpBotTables(bool dump_bot_tables) +{ + DatabaseDumpService::dump_bot_tables = dump_bot_tables; +} diff --git a/common/database/database_dump_service.h b/common/database/database_dump_service.h index f0614e85b..c14627441 100644 --- a/common/database/database_dump_service.h +++ b/common/database/database_dump_service.h @@ -53,6 +53,8 @@ public: void SetDumpDropTableSyntaxOnly(bool dump_drop_table_syntax_only); bool IsDumpStateTables() const; void SetDumpStateTables(bool dump_state_tables); + bool IsDumpBotTables() const; + void SetDumpBotTables(bool dump_bot_tables); private: bool dump_all_tables = false; @@ -67,6 +69,7 @@ private: bool dump_with_compression = false; bool dump_output_to_console = false; bool dump_drop_table_syntax_only = false; + bool dump_bot_tables = false; std::string dump_path; std::string dump_file_name; @@ -75,6 +78,7 @@ private: std::string GetMySQLVersion(); std::string GetBaseMySQLDumpCommand(); std::string GetPlayerTablesList(); + std::string GetBotTablesList(); std::string GetSystemTablesList(); std::string GetStateTablesList(); std::string GetContentTablesList(); diff --git a/common/database_schema.h b/common/database_schema.h index 072e1e799..0c430a5df 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -375,6 +375,40 @@ namespace DatabaseSchema { "inventory_versions", }; } + + /** + * @description Gets all player bot tables + * @note These tables have no content in the PEQ daily dump + * + * @return + */ + static std::vector GetBotTables() + { + return { + "bot_buffs", + "bot_command_settings", + "bot_create_combinations", + "bot_data", + "bot_group_members", + "bot_groups", + "bot_guild_members", + "bot_heal_rotation_members", + "bot_heal_rotation_targets", + "bot_heal_rotations", + "bot_inspect_messages", + "bot_inventories", + "bot_owner_options", + "bot_pet_buffs", + "bot_pet_inventories", + "bot_pets", + "bot_spell_casting_chances", + "bot_spells_entries", + "bot_stances", + "bot_timers", + "vw_bot_character_mobs", + "vw_bot_groups" + }; + } } diff --git a/world/world_server_command_handler.cpp b/world/world_server_command_handler.cpp index 0eade4f43..5f12a155e 100644 --- a/world/world_server_command_handler.cpp +++ b/world/world_server_command_handler.cpp @@ -198,6 +198,12 @@ namespace WorldserverCommandHandler { for (const auto &table: version_tables) { version_tables_json.append(table); } + + Json::Value bot_tables_json; + std::vector bot_tables = DatabaseSchema::GetBotTables(); + for (const auto &table: bot_tables) { + bot_tables_json.append(table); + } Json::Value schema; @@ -207,6 +213,7 @@ namespace WorldserverCommandHandler { schema["server_tables"] = server_tables_json; schema["state_tables"] = state_tables_json; schema["version_tables"] = version_tables_json; + schema["bot_tables"] = bot_tables_json; std::stringstream payload; payload << schema; @@ -230,6 +237,7 @@ namespace WorldserverCommandHandler { "--content-tables", "--login-tables", "--player-tables", + "--bot-tables", "--state-tables", "--system-tables", "--query-serv-tables", @@ -260,6 +268,7 @@ namespace WorldserverCommandHandler { database_dump_service->SetDumpContentTables(cmd[{"--content-tables"}] || dump_all); database_dump_service->SetDumpLoginServerTables(cmd[{"--login-tables"}] || dump_all); database_dump_service->SetDumpPlayerTables(cmd[{"--player-tables"}] || dump_all); + database_dump_service->SetDumpBotTables(cmd[{"--bot-tables"}] || dump_all); database_dump_service->SetDumpStateTables(cmd[{"--state-tables"}] || dump_all); database_dump_service->SetDumpSystemTables(cmd[{"--system-tables"}] || dump_all); database_dump_service->SetDumpQueryServerTables(cmd[{"--query-serv-tables"}] || dump_all); From 162d34e1d92c2457bd9ec9d08057b29d8a809b22 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 May 2022 20:47:01 -0500 Subject: [PATCH 043/552] [Tasks] Fix validation loading (#2230) --- zone/task_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/task_manager.cpp b/zone/task_manager.cpp index 01c654eaa..712e0e4ad 100644 --- a/zone/task_manager.cpp +++ b/zone/task_manager.cpp @@ -227,9 +227,9 @@ bool TaskManager::LoadTasks(int single_task) activity_data->target_name = task_activity.target_name; activity_data->item_list = task_activity.item_list; activity_data->skill_list = task_activity.skill_list; - activity_data->skill_id = std::stoi(task_activity.skill_list); // for older clients + activity_data->skill_id = StringIsNumber(task_activity.skill_list) ? std::stoi(task_activity.skill_list) : 0; // for older clients activity_data->spell_list = task_activity.spell_list; - activity_data->spell_id = std::stoi(task_activity.spell_list); // for older clients + activity_data->spell_id = StringIsNumber(task_activity.spell_list) ? std::stoi(task_activity.spell_list) : 0; // for older clients activity_data->description_override = task_activity.description_override; activity_data->goal_id = task_activity.goalid; activity_data->goal_method = (TaskMethodType) task_activity.goalmethod; From 291aaea581cdae6d237b5be4e3b48bf054fa41e0 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 May 2022 20:55:00 -0500 Subject: [PATCH 044/552] [Bug Fix] Fix null pointer crash on zones that have not booted a zone yet with #reload commands or anything that calls GetZoneDescription (#2231) --- zone/zone.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index f796b8416..7540232e0 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -60,6 +60,7 @@ #include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/repositories/content_flags_repository.h" #include "../common/repositories/zone_points_repository.h" +#include "../common/serverinfo.h" #include #include @@ -723,7 +724,7 @@ void Zone::GetMerchantDataForZoneLoad() { if (found) { continue; } - + mle.slot = std::stoul(row[1]); mle.item = std::stoul(row[2]); mle.faction_required = static_cast(std::stoi(row[3])); @@ -2734,6 +2735,14 @@ uint32 Zone::GetCurrencyItemID(uint32 currency_id) std::string Zone::GetZoneDescription() { + if (!IsLoaded()) { + return fmt::format( + "{} PID ({})", + IsStaticZone() ? "Static" : "Dynamic", + EQ::GetPID() + ); + } + auto d = fmt::format( "{} ({}){}{}", GetLongName(), From a45117cd040ce2c7ca20603aebbd58ffa43433a0 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 May 2022 21:24:12 -0500 Subject: [PATCH 045/552] [Bug Fix] Adjustment for nullptr crash (#2232) --- zone/zone.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 7540232e0..08972de64 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -2737,13 +2737,12 @@ std::string Zone::GetZoneDescription() { if (!IsLoaded()) { return fmt::format( - "{} PID ({})", - IsStaticZone() ? "Static" : "Dynamic", + "PID ({})", EQ::GetPID() ); } - auto d = fmt::format( + return fmt::format( "{} ({}){}{}", GetLongName(), GetZoneID(), @@ -2764,8 +2763,6 @@ std::string Zone::GetZoneDescription() "" ) ); - - return d; } void Zone::SendReloadMessage(std::string reload_type) From 59f32b8d3471ee425d6d57b8b7b74424ae89a782 Mon Sep 17 00:00:00 2001 From: Chris Miles Date: Tue, 31 May 2022 22:15:05 -0500 Subject: [PATCH 046/552] [Loading] Zone Version Loading Fixes (#2233) * Zone version loading fixes * Remove errant code --- zone/zone.cpp | 47 ++++++++++++++++++++++------------------------- zone/zonedb.cpp | 2 +- 2 files changed, 23 insertions(+), 26 deletions(-) diff --git a/zone/zone.cpp b/zone/zone.cpp index 08972de64..4c66c6c79 100755 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -655,38 +655,35 @@ void Zone::LoadNewMerchantData(uint32 merchantid) { void Zone::GetMerchantDataForZoneLoad() { LogInfo("Loading Merchant Lists"); + auto query = fmt::format( SQL ( SELECT - DISTINCT merchantlist.merchantid, - merchantlist.slot, - merchantlist.item, - merchantlist.faction_required, - merchantlist.level_required, - merchantlist.alt_currency_cost, - merchantlist.classes_required, - merchantlist.probability, - merchantlist.bucket_name, - merchantlist.bucket_value, - merchantlist.bucket_comparison - FROM - merchantlist, - npc_types, - spawnentry, - spawn2 - WHERE - npc_types.merchant_id = merchantlist.merchantid - AND npc_types.id = spawnentry.npcid - AND spawnentry.spawngroupid = spawn2.spawngroupid - AND spawn2.zone = '{}' - AND spawn2.version = {} - {} + merchantid, + slot, + item, + faction_required, + level_required, + alt_currency_cost, + classes_required, + probability, + bucket_name, + bucket_value, + bucket_comparison + from merchantlist where merchantid IN ( + select merchant_id from npc_types where id in ( + select npcID from spawnentry where spawngroupID IN ( + select spawngroupID from spawn2 where `zone` = '{}' and (`version` = {} OR `version` = -1) + ) + ) + ) + {} ORDER BY - merchantlist.slot + merchantlist.slot ), GetShortName(), GetInstanceVersion(), - ContentFilterCriteria::apply("merchantlist") + ContentFilterCriteria::apply() ); auto results = content_db.QueryDatabase(query); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index c87dc1e5f..f6a6493a3 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -2356,7 +2356,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load SQL( id IN ( select npcID from spawnentry where spawngroupID IN ( - select spawngroupID from spawn2 where `zone` = '{}' and `version` = {} + select spawngroupID from spawn2 where `zone` = '{}' and (`version` = {} OR `version` = -1) ) ) ), From 57ac46d090a1e58e3fdeebb4d567e495fd8c05ea Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 1 Jun 2022 15:31:31 -0400 Subject: [PATCH 047/552] [Commands] Cleanup #date Command. (#2228) * [Commands] Cleanup #date Command. - Cleanup messages and logic. * Cleanup. --- zone/command.cpp | 2 +- zone/gm_commands/date.cpp | 82 ++++++++++++++++++++++++++++----------- 2 files changed, 61 insertions(+), 23 deletions(-) diff --git a/zone/command.cpp b/zone/command.cpp index eec6ff89c..01ab70256 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -112,7 +112,7 @@ int command_init(void) command_add("cvs", "Summary of client versions currently online.", AccountStatus::GMMgmt, command_cvs) || command_add("damage", "[Amount] - Damage yourself or your target", AccountStatus::GMAdmin, command_damage) || command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", AccountStatus::QuestTroupe, command_databuckets) || - command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", AccountStatus::EQSupport, command_date) || + command_add("date", "[Year] [Month] [Day] [Hour] [Minute] - Set EQ time (Hour and Minute are optional)", AccountStatus::EQSupport, command_date) || command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", AccountStatus::GMAdmin, command_dbspawn2) || command_add("delacct", "[accountname] - Delete an account", AccountStatus::GMLeadAdmin, command_delacct) || command_add("delpetition", "[petition number] - Delete a petition", AccountStatus::ApprenticeGuide, command_delpetition) || diff --git a/zone/gm_commands/date.cpp b/zone/gm_commands/date.cpp index 3e8aa2455..0154f233f 100755 --- a/zone/gm_commands/date.cpp +++ b/zone/gm_commands/date.cpp @@ -2,28 +2,66 @@ void command_date(Client *c, const Seperator *sep) { - //yyyy mm dd hh mm local - if (sep->arg[3][0] == 0 || !sep->IsNumber(1) || !sep->IsNumber(2) || !sep->IsNumber(3)) { - c->Message(Chat::Red, "Usage: #date yyyy mm dd [HH MM]"); - } - else { - int h = 0, m = 0; - TimeOfDay_Struct eqTime; - zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime); - if (!sep->IsNumber(4)) { - h = eqTime.hour; - } - else { - h = atoi(sep->arg[4]); - } - if (!sep->IsNumber(5)) { - m = eqTime.minute; - } - else { - m = atoi(sep->arg[5]); - } - c->Message(Chat::Red, "Setting world time to %s-%s-%s %i:%i...", sep->arg[1], sep->arg[2], sep->arg[3], h, m); - zone->SetDate(atoi(sep->arg[1]), atoi(sep->arg[2]), atoi(sep->arg[3]), h, m); + int arguments = sep->argnum; + if ( + !arguments || + !sep->IsNumber(1) || + !sep->IsNumber(2) || + !sep->IsNumber(3) + ) { + c->Message(Chat::White, "Usage: #date [Year] [Month] [Day] [Hour] [Minute]"); + return; } + + TimeOfDay_Struct eq_time; + zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eq_time); + + auto year = static_cast(std::stoul(sep->arg[1])); + auto month = static_cast(std::stoul(sep->arg[2])); + auto day = static_cast(std::stoul(sep->arg[3])); + + auto hour = !sep->IsNumber(4) ? eq_time.hour : static_cast(std::stoul(sep->arg[4]) + 1); + auto minute = !sep->IsNumber(5) ? eq_time.minute : static_cast(std::stoul(sep->arg[5])); + + c->Message( + Chat::White, + fmt::format("Setting world time to {}/{}/{} {:02}:{:02} {}.", + year, + month, + day, + ( + (hour % 12) == 0 ? + 12 : + (hour % 12) + ), + minute, + ( + hour >= 13 ? + "PM" : + "AM" + ) + ).c_str() + ); + + zone->SetDate(year, month, day, hour, minute); + + LogInfo( + "{} :: Setting world time to {}/{}/{} {:02}:{:02} {}.", + c->GetCleanName(), + year, + month, + day, + ( + (hour % 12) == 0 ? + 12 : + (hour % 12) + ), + minute, + ( + hour >= 13 ? + "PM" : + "AM" + ) + ); } From 38da37755d4385680c367a0623d1ecc7ba08f56f Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:05:43 -0400 Subject: [PATCH 048/552] [Bug Fix] Fix MovePC in #zone and #zoneinstance Commands. (#2236) * [Bug Fix] Fix MovePC in #zone and #zoneinstance COmmands. * #zone 0 Optional Functionality. --- zone/gm_commands/zone.cpp | 33 ++++++++++++++++++++++-------- zone/gm_commands/zone_instance.cpp | 18 ++++++++-------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/zone/gm_commands/zone.cpp b/zone/gm_commands/zone.cpp index add9471da..bdd9dfc98 100644 --- a/zone/gm_commands/zone.cpp +++ b/zone/gm_commands/zone.cpp @@ -8,21 +8,34 @@ void command_zone(Client *c, const Seperator *sep) return; } + std::string zone_identifier = sep->arg[1]; + + if (StringIsNumber(zone_identifier) && zone_identifier == "0") { + c->Message(Chat::White, "Sending you to the safe coordinates of this zone."); + + c->MovePC( + 0.0f, + 0.0f, + 0.0f, + 0.0f, + 0, + ZoneToSafeCoords + ); + return; + } + auto zone_id = ( sep->IsNumber(1) ? - std::stoul(sep->arg[1]) : - ZoneID(sep->arg[1]) + std::stoul(zone_identifier) : + ZoneID(zone_identifier) ); auto zone_short_name = ZoneName(zone_id); - if ( - !zone_id || - !zone_short_name - ) { + if (!zone_id || !zone_short_name) { c->Message( Chat::White, fmt::format( "No zones were found matching '{}'.", - sep->arg[1] + zone_identifier ).c_str() ); return; @@ -44,6 +57,7 @@ void command_zone(Client *c, const Seperator *sep) auto x = sep->IsNumber(2) ? std::stof(sep->arg[2]) : 0.0f; auto y = sep->IsNumber(3) ? std::stof(sep->arg[3]) : 0.0f; auto z = sep->IsNumber(4) ? std::stof(sep->arg[4]) : 0.0f; + auto zone_mode = sep->IsNumber(2) ? ZoneSolicited : ZoneToSafeCoords; c->MovePC( zone_id, @@ -51,6 +65,7 @@ void command_zone(Client *c, const Seperator *sep) y, z, 0.0f, - sep->IsNumber(2) ? 0 : ZoneToSafeCoords + 0, + zone_mode ); -} \ No newline at end of file +} diff --git a/zone/gm_commands/zone_instance.cpp b/zone/gm_commands/zone_instance.cpp index d54b3b251..4d36580b7 100644 --- a/zone/gm_commands/zone_instance.cpp +++ b/zone/gm_commands/zone_instance.cpp @@ -10,7 +10,7 @@ void command_zone_instance(Client *c, const Seperator *sep) auto instance_id = std::stoul(sep->arg[1]); if (!instance_id) { - c->Message(Chat::White, "You must enter a valid instance id."); + c->Message(Chat::White, "You must enter a valid Instance ID."); return; } @@ -36,18 +36,16 @@ void command_zone_instance(Client *c, const Seperator *sep) ); return; } - - if (database.CharacterInInstanceGroup(instance_id, c->CharacterID())) { - c->Message(Chat::White, "You are already a part of this instance, sending you there."); - c->MoveZoneInstance(instance_id); - return; + + if (!database.CharacterInInstanceGroup(instance_id, c->CharacterID())) { + database.AddClientToInstance(instance_id, c->CharacterID()); } - + if (!database.VerifyInstanceAlive(instance_id, c->CharacterID())) { c->Message( Chat::White, fmt::format( - "Instance ID {} expired or you are not apart of this instance.", + "Instance ID {} expired.", instance_id ).c_str() ); @@ -57,6 +55,7 @@ void command_zone_instance(Client *c, const Seperator *sep) auto x = sep->IsNumber(2) ? std::stof(sep->arg[2]) : 0.0f; auto y = sep->IsNumber(3) ? std::stof(sep->arg[3]) : 0.0f; auto z = sep->IsNumber(4) ? std::stof(sep->arg[4]) : 0.0f; + auto zone_mode = sep->IsNumber(2) ? ZoneSolicited : ZoneToSafeCoords; c->MovePC( zone_id, @@ -65,6 +64,7 @@ void command_zone_instance(Client *c, const Seperator *sep) y, z, 0.0f, - sep->IsNumber(2) ? 0 : ZoneToSafeCoords + 0, + zone_mode ); } From d1404a2d95f9e7958a3aee3faea2197261ca5dd9 Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Wed, 1 Jun 2022 17:12:31 -0400 Subject: [PATCH 049/552] [Rules] Add Rules to disable various item functionalities and cleanup data types. (#2225) - Noticed some data types were unsigned when they should be signed based on Live items having these stats as negatives. - Loregroup was uint32 and should be int32 for Loregroup -1, fixed any references to -1 as 0xFFFFFFFF. - Attack was uint32 and should be int32. - DamageShield was uint32 and should be int32. - DotShielding was uint32 and should be int32. - Endurance was uint32 and should be int32. - EnduranceRegen was uint32 and should be int32. - Haste was uint32 and should be int32. - ManaRegen was uint32 and should be int32. - Regen was uint32 and should be int32. - RULE_BOOL(Items, DisableAttuneable, false, "Enable this to disable Attuneable Items") - RULE_BOOL(Items, DisableBardFocusEffects, false, "Enable this to disable Bard Focus Effects on Items") - RULE_BOOL(Items, DisableLore, false, "Enable this to disable Lore Items") - RULE_BOOL(Items, DisableNoDrop, false, "Enable this to disable No Drop Items") - RULE_BOOL(Items, DisableNoPet, false, "Enable this to disable No Pet Items") - RULE_BOOL(Items, DisableNoRent, false, "Enable this to disable No Rent Items") - RULE_BOOL(Items, DisableNoTransfer, false, "Enable this to disable No Transfer Items") - RULE_BOOL(Items, DisablePotionBelt, false, "Enable this to disable Potion Belt Items") - RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell Focus Effects on Items") --- common/item_data.h | 18 +- common/ruletypes.h | 12 + common/shareddb.cpp | 520 +++++++++++++++++++++++++------------------- zone/inventory.cpp | 6 +- 4 files changed, 319 insertions(+), 237 deletions(-) diff --git a/common/item_data.h b/common/item_data.h index e3123e22f..ac738a2f9 100644 --- a/common/item_data.h +++ b/common/item_data.h @@ -370,7 +370,7 @@ namespace EQ uint32 Slots; // Bitfield for which slots this item can be used in uint32 Price; // Item cost (?) uint32 Icon; // Icon Number - uint32 LoreGroup; // Later items use LoreGroup instead of LoreFlag. we might want to see about changing this to int32 since it is commonly -1 and is constantly being cast from signed (-1) to unsigned (4294967295) + int32 LoreGroup; // Later items use LoreGroup instead of LoreFlag. we might want to see about changing this to int32 since it is commonly -1 and is constantly being cast from signed (-1) to unsigned (4294967295) bool LoreFlag; // This will be true if LoreGroup is non-zero bool PendingLoreFlag; bool ArtifactFlag; @@ -473,14 +473,14 @@ namespace EQ uint32 LDoNSold; uint32 BaneDmgRaceAmt; uint32 AugRestrict; - uint32 Endur; - uint32 DotShielding; - uint32 Attack; - uint32 Regen; - uint32 ManaRegen; - uint32 EnduranceRegen; - uint32 Haste; - uint32 DamageShield; + int32 Endur; + int32 DotShielding; + int32 Attack; + int32 Regen; + int32 ManaRegen; + int32 EnduranceRegen; + int32 Haste; + int32 DamageShield; uint32 RecastDelay; int RecastType; uint32 AugDistiller; diff --git a/common/ruletypes.h b/common/ruletypes.h index 3e347a962..9aee11df5 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -800,6 +800,18 @@ RULE_CATEGORY(Doors) RULE_BOOL(Doors, RequireKeyOnCursor, false, "Enable this to require pre-keyring keys to be on player cursor to open doors.") RULE_CATEGORY_END() +RULE_CATEGORY(Items) +RULE_BOOL(Items, DisableAttuneable, false, "Enable this to disable Attuneable Items") +RULE_BOOL(Items, DisableBardFocusEffects, false, "Enable this to disable Bard Focus Effects on Items") +RULE_BOOL(Items, DisableLore, false, "Enable this to disable Lore Items") +RULE_BOOL(Items, DisableNoDrop, false, "Enable this to disable No Drop Items") +RULE_BOOL(Items, DisableNoPet, false, "Enable this to disable No Pet Items") +RULE_BOOL(Items, DisableNoRent, false, "Enable this to disable No Rent Items") +RULE_BOOL(Items, DisableNoTransfer, false, "Enable this to disable No Transfer Items") +RULE_BOOL(Items, DisablePotionBelt, false, "Enable this to disable Potion Belt Items") +RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell Focus Effects on Items") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 2cc1d8c15..c334b984c 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -38,6 +38,7 @@ #include "shareddb.h" #include "string_util.h" #include "eqemu_config.h" +#include "data_verification.h" #include "repositories/criteria/content_filter_criteria.h" namespace ItemField @@ -634,18 +635,18 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) uint32 aug[EQ::invaug::SOCKET_COUNT]; - aug[0] = (uint32)atoul(row[4]); - aug[1] = (uint32)atoul(row[5]); - aug[2] = (uint32)atoul(row[6]); - aug[3] = (uint32)atoul(row[7]); - aug[4] = (uint32)atoul(row[8]); - aug[5] = (uint32)atoul(row[9]); + aug[0] = std::stoul(row[4]); + aug[1] = std::stoul(row[5]); + aug[2] = std::stoul(row[6]); + aug[3] = std::stoul(row[7]); + aug[4] = std::stoul(row[8]); + aug[5] = std::stoul(row[9]); bool instnodrop = (row[10] && (uint16)atoi(row[10])) ? true : false; - uint32 ornament_icon = (uint32)atoul(row[12]); - uint32 ornament_idfile = (uint32)atoul(row[13]); - uint32 ornament_hero_model = (uint32)atoul(row[14]); + uint32 ornament_icon = std::stoul(row[12]); + uint32 ornament_idfile = std::stoul(row[13]); + uint32 ornament_hero_model = std::stoul(row[14]); const EQ::ItemData *item = GetItem(item_id); @@ -788,9 +789,9 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, EQ::InventoryPr aug[5] = (uint32)atoi(row[9]); bool instnodrop = (row[10] && (uint16)atoi(row[10])) ? true : false; - uint32 ornament_icon = (uint32)atoul(row[12]); - uint32 ornament_idfile = (uint32)atoul(row[13]); - uint32 ornament_hero_model = (uint32)atoul(row[14]); + uint32 ornament_icon = std::stoul(row[12]); + uint32 ornament_idfile = std::stoul(row[13]); + uint32 ornament_hero_model = std::stoul(row[14]); const EQ::ItemData *item = GetItem(item_id); int16 put_slot_id = INVALID_INDEX; @@ -943,29 +944,41 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ { EQ::FixedMemoryHashSet hash(reinterpret_cast(data), size, items, max_item_id); - std::string ndbuffer; - bool disableNoRent = false; - if (GetVariable("disablenorent", ndbuffer)) { - if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableNoRent = true; + std::string variable_buffer; + + bool disable_attuneable = RuleB(Items, DisableAttuneable); + bool disable_bard_focus_effects = RuleB(Items, DisableBardFocusEffects); + bool disable_lore = RuleB(Items, DisableLore); + bool disable_no_drop = RuleB(Items, DisableNoDrop); + bool disable_no_pet = RuleB(Items, DisableNoPet); + bool disable_no_rent = RuleB(Items, DisableNoRent); + bool disable_no_transfer = RuleB(Items, DisableNoTransfer); + bool disable_potion_belt = RuleB(Items, DisablePotionBelt); + bool disable_spell_focus_effects = RuleB(Items, DisableSpellFocusEffects); + + // Old Variable Code + if (GetVariable("disablelore", variable_buffer)) { + if (variable_buffer == "1") { + disable_lore = true; } } - bool disableNoDrop = false; - if (GetVariable("disablenodrop", ndbuffer)) { - if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableNoDrop = true; + + if (GetVariable("disablenodrop", variable_buffer)) { + if (variable_buffer == "1") { + disable_no_drop = true; } } - bool disableLoreGroup = false; - if (GetVariable("disablelore", ndbuffer)) { - if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableLoreGroup = true; + + if (GetVariable("disablenorent", variable_buffer)) { + if (variable_buffer == "1") { + disable_no_rent = true; } } - bool disableNoTransfer = false; - if (GetVariable("disablenotransfer", ndbuffer)) { - if (ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableNoTransfer = true; + + + if (GetVariable("disablenotransfer", variable_buffer)) { + if (variable_buffer == "1") { + disable_no_transfer = true; } } @@ -984,212 +997,269 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ for (auto row = results.begin(); row != results.end(); ++row) { memset(&item, 0, sizeof(EQ::ItemData)); - item.ItemClass = (uint8)atoi(row[ItemField::itemclass]); - strcpy(item.Name, row[ItemField::name]); - strcpy(item.Lore, row[ItemField::lore]); - strcpy(item.IDFile, row[ItemField::idfile]); + // Unique Identifier + item.ID = std::stoul(row[ItemField::id]); - item.ID = (uint32)atoul(row[ItemField::id]); - item.Weight = (int32)atoi(row[ItemField::weight]); - item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]); - item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]); - item.Size = (uint8)atoi(row[ItemField::size]); - item.Slots = (uint32)atoul(row[ItemField::slots]); - item.Price = (uint32)atoul(row[ItemField::price]); - item.Icon = (uint32)atoul(row[ItemField::icon]); - item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0); - item.Tradeskills = (atoi(row[ItemField::tradeskills]) == 0) ? false : true; - item.CR = (int8)atoi(row[ItemField::cr]); - item.DR = (int8)atoi(row[ItemField::dr]); - item.PR = (int8)atoi(row[ItemField::pr]); - item.MR = (int8)atoi(row[ItemField::mr]); - item.FR = (int8)atoi(row[ItemField::fr]); - item.AStr = (int8)atoi(row[ItemField::astr]); - item.ASta = (int8)atoi(row[ItemField::asta]); - item.AAgi = (int8)atoi(row[ItemField::aagi]); - item.ADex = (int8)atoi(row[ItemField::adex]); - item.ACha = (int8)atoi(row[ItemField::acha]); - item.AInt = (int8)atoi(row[ItemField::aint]); - item.AWis = (int8)atoi(row[ItemField::awis]); - item.HP = (int32)atoul(row[ItemField::hp]); - item.Mana = (int32)atoul(row[ItemField::mana]); - item.AC = (int32)atoul(row[ItemField::ac]); - item.Deity = (uint32)atoul(row[ItemField::deity]); - item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]); - item.SkillModMax = (int32)atoul(row[ItemField::skillmodmax]); - item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]); - item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]); - item.BaneDmgAmt = (int32)atoul(row[ItemField::banedmgamt]); - item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]); - item.Magic = (atoi(row[ItemField::magic]) == 0) ? false : true; - item.CastTime_ = (int32)atoul(row[ItemField::casttime_]); - item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]); - item.BardType = (uint32)atoul(row[ItemField::bardtype]); - item.BardValue = (int32)atoul(row[ItemField::bardvalue]); - item.Light = (int8)atoi(row[ItemField::light]); - item.Delay = (uint8)atoi(row[ItemField::delay]); - item.RecLevel = (uint8)atoi(row[ItemField::reclevel]); - item.RecSkill = (uint8)atoi(row[ItemField::recskill]); - item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]); - item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]); - item.Range = (uint8)atoi(row[ItemField::range]); - item.Damage = (uint32)atoi(row[ItemField::damage]); - item.Color = (uint32)atoul(row[ItemField::color]); - item.Classes = (uint32)atoul(row[ItemField::classes]); - item.Races = (uint32)atoul(row[ItemField::races]); + // Name and Lore + strn0cpy(item.Name, row[ItemField::name], sizeof(item.Name)); + strn0cpy(item.Lore, row[ItemField::lore], sizeof(item.Lore)); - item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]); - item.ItemType = (uint8)atoi(row[ItemField::itemtype]); - item.SubType = atoi(row[ItemField::subtype]); - item.Material = (uint8)atoi(row[ItemField::material]); - item.HerosForgeModel = (uint32)atoi(row[ItemField::herosforgemodel]); - item.SellRate = (float)atof(row[ItemField::sellrate]); - item.CastTime = (uint32)atoul(row[ItemField::casttime]); - item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]); - item.ProcRate = (int32)atoi(row[ItemField::procrate]); - item.CombatEffects = (int8)atoi(row[ItemField::combateffects]); - item.Shielding = (int8)atoi(row[ItemField::shielding]); - item.StunResist = (int8)atoi(row[ItemField::stunresist]); - item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]); - item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]); - item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]); - item.SpellShield = (int8)atoi(row[ItemField::spellshield]); - item.Avoidance = (int8)atoi(row[ItemField::avoidance]); - item.Accuracy = (int8)atoi(row[ItemField::accuracy]); - item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]); - item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]); - item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]); - item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]); - item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]); - item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]); - item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]); - item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]); - item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]); + // Flags + item.ArtifactFlag = std::stoi(row[ItemField::artifactflag]) ? true : false; + item.Attuneable = disable_attuneable ? false : std::stoi(row[ItemField::attuneable]) ? true : false; + item.BenefitFlag = std::stoi(row[ItemField::benefitflag]) ? true : false; + item.FVNoDrop = std::stoi(row[ItemField::fvnodrop]) ? true : false; + item.Magic = std::stoi(row[ItemField::magic]) ? true : false; + item.NoDrop = disable_no_drop ? static_cast(255) : static_cast(std::stoul(row[ItemField::nodrop])); + item.NoPet = disable_no_pet ? false : std::stoi(row[ItemField::nopet]) ? true : false; + item.NoRent = disable_no_rent ? static_cast(255) : static_cast(std::stoul(row[ItemField::norent])); + item.NoTransfer = disable_no_transfer ? false : std::stoi(row[ItemField::notransfer]) ? true : false; + item.PendingLoreFlag = std::stoi(row[ItemField::pendingloreflag]) ? true : false; + item.QuestItemFlag = std::stoi(row[ItemField::questitemflag]) ? true : false; + item.Stackable = std::stoi(row[ItemField::stackable]) ? true : false; + item.Tradeskills = std::stoi(row[ItemField::tradeskills]) ? true : false; + item.SummonedFlag = std::stoi(row[ItemField::summonedflag]) ? true : false; - strcpy(item.CharmFile, row[ItemField::charmfile]); + // Lore + item.LoreGroup = disable_lore ? 0 : std::stoi(row[ItemField::loregroup]); + item.LoreFlag = disable_lore ? false : item.LoreGroup != 0; - item.AugType = (uint32)atoul(row[ItemField::augtype]); - item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]); - item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]); - item.AugSlotUnk2[0] = 0; - item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]); - item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]); - item.AugSlotUnk2[1] = 0; - item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]); - item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]); - item.AugSlotUnk2[2] = 0; - item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]); - item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]); - item.AugSlotUnk2[3] = 0; - item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]); - item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]); - item.AugSlotUnk2[4] = 0; - item.AugSlotType[5] = (uint8)atoi(row[ItemField::augslot6type]); - item.AugSlotVisible[5] = (uint8)atoi(row[ItemField::augslot6visible]); - item.AugSlotUnk2[5] = 0; + // Type + item.AugType = std::stoul(row[ItemField::augtype]); + item.ItemType = static_cast(std::stoul(row[ItemField::itemtype])); + item.SubType = std::stoi(row[ItemField::subtype]); - item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]); - item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]); - item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]); - item.BagType = (uint8)atoi(row[ItemField::bagtype]); - item.BagSlots = (uint8)std::min(atoi(row[ItemField::bagslots]), 10); // FIXME: remove when big bags supported - item.BagSize = (uint8)atoi(row[ItemField::bagsize]); - item.BagWR = (uint8)atoi(row[ItemField::bagwr]); - item.Book = (uint8)atoi(row[ItemField::book]); - item.BookType = (uint32)atoul(row[ItemField::booktype]); + // Miscellaneous + item.ExpendableArrow = static_cast(std::stoul(row[ItemField::expendablearrow])); + item.Light = static_cast(std::stoi(row[ItemField::light])); + item.MaxCharges = static_cast(std::stoi(row[ItemField::maxcharges])); + item.Size = static_cast(std::stoul(row[ItemField::size])); + item.StackSize = static_cast(std::stoi(row[ItemField::stacksize])); + item.Weight = std::stoi(row[ItemField::weight]); - strcpy(item.Filename, row[ItemField::filename]); + // Potion Belt + item.PotionBelt = disable_potion_belt ? false : std::stoi(row[ItemField::potionbelt]) ? true : false; + item.PotionBeltSlots = disable_potion_belt ? 0 : static_cast(std::stoul(row[ItemField::potionbeltslots])); - item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]); - item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]); - item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]); - item.LoreFlag = item.LoreGroup != 0; - item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag]) == 0) ? false : true; - item.ArtifactFlag = (atoi(row[ItemField::artifactflag]) == 0) ? false : true; - item.SummonedFlag = (atoi(row[ItemField::summonedflag]) == 0) ? false : true; - item.Favor = (uint32)atoul(row[ItemField::favor]); - item.FVNoDrop = (atoi(row[ItemField::fvnodrop]) == 0) ? false : true; - item.Endur = (uint32)atoul(row[ItemField::endur]); - item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]); - item.Attack = (uint32)atoul(row[ItemField::attack]); - item.Regen = (uint32)atoul(row[ItemField::regen]); - item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]); - item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]); - item.Haste = (uint32)atoul(row[ItemField::haste]); - item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); - item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); - item.RecastType = (int)atoi(row[ItemField::recasttype]); - item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); - item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); - item.Attuneable = (atoi(row[ItemField::attuneable]) == 0) ? false : true; - item.NoPet = (atoi(row[ItemField::nopet]) == 0) ? false : true; - item.PointType = (uint32)atoul(row[ItemField::pointtype]); - item.PotionBelt = (atoi(row[ItemField::potionbelt]) == 0) ? false : true; - item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots]) == 0) ? false : true; - item.StackSize = (uint16)atoi(row[ItemField::stacksize]); - item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer]) == 0) ? false : true; - item.Stackable = (atoi(row[ItemField::stackable]) == 0) ? false : true; - item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]); - item.Click.Type = (uint8)atoul(row[ItemField::clicktype]); - item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]); - item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]); + // Merchant + item.Favor = std::stoul(row[ItemField::favor]); + item.GuildFavor = std::stoul(row[ItemField::guildfavor]); + item.Price = std::stoul(row[ItemField::price]); + item.SellRate = std::stof(row[ItemField::sellrate]); + + // Display + item.Color = std::stoul(row[ItemField::color]); + item.EliteMaterial = std::stoul(row[ItemField::elitematerial]); + item.HerosForgeModel = std::stoul(row[ItemField::herosforgemodel]); + item.Icon = std::stoul(row[ItemField::icon]); + strn0cpy(item.IDFile, row[ItemField::idfile], sizeof(item.IDFile)); + item.Material = static_cast(std::stoul(row[ItemField::material])); - strcpy(item.CharmFile, row[ItemField::charmfile]); + // Resists + item.CR = static_cast(EQ::Clamp(std::stoi(row[ItemField::cr]), -128, 127)); + item.DR = static_cast(EQ::Clamp(std::stoi(row[ItemField::dr]), -128, 127)); + item.FR = static_cast(EQ::Clamp(std::stoi(row[ItemField::fr]), -128, 127)); + item.MR = static_cast(EQ::Clamp(std::stoi(row[ItemField::mr]), -128, 127)); + item.PR = static_cast(EQ::Clamp(std::stoi(row[ItemField::pr]), -128, 127)); + item.SVCorruption = static_cast(EQ::Clamp(std::stoi(row[ItemField::svcorruption]), -128, 127)); - item.Proc.Effect = (int32)atoul(row[ItemField::proceffect]); - item.Proc.Type = (uint8)atoul(row[ItemField::proctype]); - item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]); - item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]); - item.Worn.Effect = (int32)atoul(row[ItemField::worneffect]); - item.Worn.Type = (uint8)atoul(row[ItemField::worntype]); - item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]); - item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]); - item.Focus.Effect = (int32)atoul(row[ItemField::focuseffect]); - item.Focus.Type = (uint8)atoul(row[ItemField::focustype]); - item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]); - item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]); - item.Scroll.Effect = (int32)atoul(row[ItemField::scrolleffect]); - item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]); - item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]); - item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]); - item.Bard.Effect = (int32)atoul(row[ItemField::bardeffect]); - item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]); - item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]); - item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]); - item.QuestItemFlag = (atoi(row[ItemField::questitemflag]) == 0) ? false : true; - item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]); - item.Purity = (uint32)atoul(row[ItemField::purity]); - item.EvolvingItem = (uint8)atoul(row[ItemField::evoitem]); - item.EvolvingID = (uint8)atoul(row[ItemField::evoid]); - item.EvolvingLevel = (uint8)atoul(row[ItemField::evolvinglevel]); - item.EvolvingMax = (uint8)atoul(row[ItemField::evomax]); - item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]); - item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]); - item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]); - item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]); - item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]); - item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]); - item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]); - item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]); - item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]); - item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]); - item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]); - item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]); - item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]); - item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]); - item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]); - item.HealAmt = (int32)atoi(row[ItemField::healamt]); - item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]); - item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]); - item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]); - item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]); - item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]); + // Heroic Resists + item.HeroicCR = std::stoi(row[ItemField::heroic_cr]); + item.HeroicDR = std::stoi(row[ItemField::heroic_dr]); + item.HeroicFR = std::stoi(row[ItemField::heroic_fr]); + item.HeroicMR = std::stoi(row[ItemField::heroic_mr]); + item.HeroicPR = std::stoi(row[ItemField::heroic_pr]); + item.HeroicSVCorrup = std::stoi(row[ItemField::heroic_svcorrup]); - strcpy(item.ClickName, row[ItemField::clickname]); - strcpy(item.ProcName, row[ItemField::procname]); - strcpy(item.WornName, row[ItemField::wornname]); - strcpy(item.FocusName, row[ItemField::focusname]); - strcpy(item.ScrollName, row[ItemField::scrollname]); + // Stats + item.AAgi = static_cast(EQ::Clamp(std::stoi(row[ItemField::aagi]), -128, 127)); + item.ACha = static_cast(EQ::Clamp(std::stoi(row[ItemField::acha]), -128, 127)); + item.ADex = static_cast(EQ::Clamp(std::stoi(row[ItemField::adex]), -128, 127)); + item.AInt = static_cast(EQ::Clamp(std::stoi(row[ItemField::aint]), -128, 127)); + item.ASta = static_cast(EQ::Clamp(std::stoi(row[ItemField::asta]), -128, 127)); + item.AStr = static_cast(EQ::Clamp(std::stoi(row[ItemField::astr]), -128, 127)); + item.AWis = static_cast(EQ::Clamp(std::stoi(row[ItemField::awis]), -128, 127)); + + // Heroic Stats + item.HeroicAgi = std::stoi(row[ItemField::heroic_agi]); + item.HeroicCha = std::stoi(row[ItemField::heroic_cha]); + item.HeroicDex = std::stoi(row[ItemField::heroic_dex]); + item.HeroicInt = std::stoi(row[ItemField::heroic_int]); + item.HeroicSta = std::stoi(row[ItemField::heroic_sta]); + item.HeroicStr = std::stoi(row[ItemField::heroic_str]); + item.HeroicWis = std::stoi(row[ItemField::heroic_wis]); + + // Health, Mana, and Endurance + item.HP = std::stoi(row[ItemField::hp]); + item.Regen = std::stoul(row[ItemField::regen]); + item.Mana = std::stoi(row[ItemField::mana]); + item.ManaRegen = std::stoul(row[ItemField::manaregen]); + item.Endur = std::stoul(row[ItemField::endur]); + item.EnduranceRegen = std::stoul(row[ItemField::enduranceregen]); + + // Bane Damage + item.BaneDmgAmt = std::stoi(row[ItemField::banedmgamt]); + item.BaneDmgBody = std::stoul(row[ItemField::banedmgbody]); + item.BaneDmgRace = std::stoul(row[ItemField::banedmgrace]); + item.BaneDmgRaceAmt = std::stoul(row[ItemField::banedmgraceamt]); + + // Elemental Damage + item.ElemDmgType = static_cast(std::stoul(row[ItemField::elemdmgtype])); + item.ElemDmgAmt = static_cast(std::stoul(row[ItemField::elemdmgamt])); + + // Combat + item.BackstabDmg = std::stoul(row[ItemField::backstabdmg]); + item.Damage = std::stoul(row[ItemField::damage]); + item.Delay = static_cast(std::stoul(row[ItemField::delay])); + item.Range = static_cast(std::stoul(row[ItemField::range])); + + // Combat Stats + item.AC = std::stoi(row[ItemField::ac]); + item.Accuracy = static_cast(EQ::Clamp(std::stoi(row[ItemField::accuracy]), -128, 127)); + item.Attack = std::stoi(row[ItemField::attack]); + item.Avoidance = static_cast(EQ::Clamp(std::stoi(row[ItemField::avoidance]), -128, 127)); + item.Clairvoyance = std::stoul(row[ItemField::clairvoyance]); + item.CombatEffects = static_cast(EQ::Clamp(std::stoi(row[ItemField::combateffects]), -128, 127)); + item.DamageShield = std::stoi(row[ItemField::damageshield]); + item.DotShielding = std::stoi(row[ItemField::dotshielding]); + item.DSMitigation = std::stoul(row[ItemField::dsmitigation]); + item.Haste = std::stoi(row[ItemField::haste]); + item.HealAmt = std::stoi(row[ItemField::healamt]); + item.Purity = std::stoul(row[ItemField::purity]); + item.Shielding = static_cast(EQ::Clamp(std::stoi(row[ItemField::shielding]), -128, 127)); + item.SpellDmg = std::stoi(row[ItemField::spelldmg]); + item.SpellShield = static_cast(EQ::Clamp(std::stoi(row[ItemField::spellshield]), -128, 127)); + item.StrikeThrough = static_cast(EQ::Clamp(std::stoi(row[ItemField::strikethrough]), -128, 127)); + item.StunResist = static_cast(EQ::Clamp(std::stoi(row[ItemField::stunresist]), -128, 127)); + + // Restrictions + item.AugRestrict = std::stoul(row[ItemField::augrestrict]); + item.Classes = std::stoul(row[ItemField::classes]); + item.Deity = std::stoul(row[ItemField::deity]); + item.ItemClass = static_cast(std::stoul(row[ItemField::itemclass])); + item.Races = std::stoul(row[ItemField::races]); + item.RecLevel = static_cast(std::stoul(row[ItemField::reclevel])); + item.RecSkill = static_cast(std::stoul(row[ItemField::recskill])); + item.ReqLevel = static_cast(std::stoul(row[ItemField::reqlevel])); + item.Slots = std::stoul(row[ItemField::slots]); + + // Skill Modifier + item.SkillModValue = std::stoi(row[ItemField::skillmodvalue]); + item.SkillModMax = std::stoi(row[ItemField::skillmodmax]); + item.SkillModType = std::stoul(row[ItemField::skillmodtype]); + + // Extra Damage Skill + item.ExtraDmgSkill = std::stoul(row[ItemField::extradmgskill]); + item.ExtraDmgAmt = std::stoul(row[ItemField::extradmgamt]); + + // Bard + item.BardType = std::stoul(row[ItemField::bardtype]); + item.BardValue = std::stoi(row[ItemField::bardvalue]); + + // Faction + item.FactionAmt1 = std::stoi(row[ItemField::factionamt1]); + item.FactionMod1 = std::stoi(row[ItemField::factionmod1]); + item.FactionAmt2 = std::stoi(row[ItemField::factionamt2]); + item.FactionMod2 = std::stoi(row[ItemField::factionmod2]); + item.FactionAmt3 = std::stoi(row[ItemField::factionamt3]); + item.FactionMod3 = std::stoi(row[ItemField::factionmod3]); + item.FactionAmt4 = std::stoi(row[ItemField::factionamt4]); + item.FactionMod4 = std::stoi(row[ItemField::factionmod4]); + + // Augment + item.AugDistiller = std::stoul(row[ItemField::augdistiller]); + item.AugSlotType[0] = static_cast(std::stoul(row[ItemField::augslot1type])); + item.AugSlotVisible[0] = static_cast(std::stoul(row[ItemField::augslot1visible])); + item.AugSlotType[1] = static_cast(std::stoul(row[ItemField::augslot2type])); + item.AugSlotVisible[1] = static_cast(std::stoul(row[ItemField::augslot2visible])); + item.AugSlotType[2] = static_cast(std::stoul(row[ItemField::augslot3type])); + item.AugSlotVisible[2] = static_cast(std::stoul(row[ItemField::augslot3visible])); + item.AugSlotType[3] = static_cast(std::stoul(row[ItemField::augslot4type])); + item.AugSlotVisible[3] = static_cast(std::stoul(row[ItemField::augslot4visible])); + item.AugSlotType[4] = static_cast(std::stoul(row[ItemField::augslot5type])); + item.AugSlotVisible[4] = static_cast(std::stoul(row[ItemField::augslot5visible])); + item.AugSlotType[5] = static_cast(std::stoul(row[ItemField::augslot6type])); + item.AugSlotVisible[5] = static_cast(std::stoul(row[ItemField::augslot6visible])); + + // Augment Unknowns + for (uint8 i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) { + item.AugSlotUnk2[i] = 0; + } + + // LDoN + item.LDoNTheme = std::stoul(row[ItemField::ldontheme]); + item.LDoNPrice = std::stoul(row[ItemField::ldonprice]); + item.LDoNSellBackRate = std::stoul(row[ItemField::ldonsellbackrate]); + item.LDoNSold = std::stoul(row[ItemField::ldonsold]); + item.PointType = std::stoul(row[ItemField::pointtype]); + + // Bag + item.BagSize = static_cast(std::stoul(row[ItemField::bagsize])); + item.BagSlots = static_cast(EQ::Clamp(std::stoi(row[ItemField::bagslots]), 0, 10)); // Will need to be changed from std::min to just use database value when bag slots are increased + item.BagType = static_cast(std::stoul(row[ItemField::bagtype])); + item.BagWR = static_cast(EQ::Clamp(std::stoi(row[ItemField::bagwr]), 0, 100)); + + // Bard Effect + item.Bard.Effect = disable_bard_focus_effects ? 0 : std::stoi(row[ItemField::bardeffect]); + item.Bard.Type = disable_bard_focus_effects ? 0 : static_cast(std::stoul(row[ItemField::bardtype])); + item.Bard.Level = disable_bard_focus_effects ? 0 : static_cast(std::stoul(row[ItemField::bardlevel])); + item.Bard.Level2 = disable_bard_focus_effects ? 0 : static_cast(std::stoul(row[ItemField::bardlevel2])); + + // Book + item.Book = static_cast(std::stoul(row[ItemField::book])); + item.BookType = std::stoul(row[ItemField::booktype]); + + // Click Effect + item.CastTime = std::stoul(row[ItemField::casttime]); + item.CastTime_ = std::stoi(row[ItemField::casttime_]); + item.Click.Effect = std::stoul(row[ItemField::clickeffect]); + item.Click.Type = static_cast(std::stoul(row[ItemField::clicktype])); + item.Click.Level = static_cast(std::stoul(row[ItemField::clicklevel])); + item.Click.Level2 = static_cast(std::stoul(row[ItemField::clicklevel2])); + strn0cpy(item.ClickName, row[ItemField::clickname], sizeof(item.ClickName)); + item.RecastDelay = std::stoul(row[ItemField::recastdelay]); + item.RecastType = std::stoi(row[ItemField::recasttype]); + + // Focus Effect + item.Focus.Effect = disable_spell_focus_effects ? 0 : std::stoi(row[ItemField::focuseffect]); + item.Focus.Type = disable_spell_focus_effects ? 0 : static_cast(std::stoul(row[ItemField::focustype])); + item.Focus.Level = disable_spell_focus_effects ? 0 : static_cast(std::stoul(row[ItemField::focuslevel])); + item.Focus.Level2 = disable_spell_focus_effects ? 0 : static_cast(std::stoul(row[ItemField::focuslevel2])); + strn0cpy(item.FocusName, disable_spell_focus_effects ? "" : row[ItemField::focusname], sizeof(item.FocusName)); + + // Proc Effect + item.Proc.Effect = std::stoi(row[ItemField::proceffect]); + item.Proc.Type = static_cast(std::stoul(row[ItemField::proctype])); + item.Proc.Level = static_cast(std::stoul(row[ItemField::proclevel])); + item.Proc.Level2 = static_cast(std::stoul(row[ItemField::proclevel2])); + strn0cpy(item.ProcName, row[ItemField::procname], sizeof(item.ProcName)); + item.ProcRate = std::stoi(row[ItemField::procrate]); + + // Scroll Effect + item.Scroll.Effect = std::stoi(row[ItemField::scrolleffect]); + item.Scroll.Type = static_cast(std::stoul(row[ItemField::scrolltype])); + item.Scroll.Level = static_cast(std::stoul(row[ItemField::scrolllevel])); + item.Scroll.Level2 = static_cast(std::stoul(row[ItemField::scrolllevel2])); + strn0cpy(item.ScrollName, row[ItemField::scrollname], sizeof(item.ScrollName)); + + // Worn Effect + item.Worn.Effect = std::stoi(row[ItemField::worneffect]); + item.Worn.Type = static_cast(std::stoul(row[ItemField::worntype])); + item.Worn.Level = static_cast(std::stoul(row[ItemField::wornlevel])); + item.Worn.Level2 = static_cast(std::stoul(row[ItemField::wornlevel2])); + strn0cpy(item.WornName, row[ItemField::wornname], sizeof(item.WornName)); + + // Evolving Item + item.EvolvingID = std::stoul(row[ItemField::evoid]); + item.EvolvingItem = static_cast(std::stoul(row[ItemField::evoitem])); + item.EvolvingLevel = static_cast(std::stoul(row[ItemField::evolvinglevel])); + item.EvolvingMax = static_cast(std::stoul(row[ItemField::evomax])); + + // Scripting + item.CharmFileID = std::stoul(row[ItemField::charmfileid]); + strn0cpy(item.CharmFile, row[ItemField::charmfile], sizeof(item.CharmFile)); + strn0cpy(item.Filename, row[ItemField::filename], sizeof(item.Filename)); + item.ScriptFileID = std::stoul(row[ItemField::scriptfileid]); try { hash.insert(item.ID, item); diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 12b87ffab..1eab314ec 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -170,7 +170,7 @@ bool Client::CheckLoreConflict(const EQ::ItemData* item) if (!item->LoreFlag) { return false; } if (item->LoreGroup == 0) { return false; } - if (item->LoreGroup == 0xFFFFFFFF) // Standard lore items; look everywhere except the shared bank, return the result + if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != INVALID_INDEX); // If the item has a lore group, we check for other items with the same group and return the result @@ -1072,7 +1072,7 @@ void Client::SendCursorBuffer() if (test_item == nullptr) { return; } bool lore_pass = true; - if (test_item->LoreGroup == 0xFFFFFFFF) { + if (test_item->LoreGroup == -1) { lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); } else if (test_item->LoreGroup != 0) { @@ -1799,7 +1799,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { if (!test_item->LoreFlag) { return true; } bool lore_pass = true; - if (test_item->LoreGroup == 0xFFFFFFFF) { + if (test_item->LoreGroup == -1) { lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX); } else if (test_item->LoreGroup != 0) { From de830e5535ca1d9657b4944d363c58cef8225e0d Mon Sep 17 00:00:00 2001 From: Quintinon Date: Wed, 1 Jun 2022 14:16:49 -0700 Subject: [PATCH 050/552] [Code Cleanup] Resharper Warnings (#2235) * Remove unused local variable * Remove another unused variable * Correct typos and remove unused initialization * Cleanup some code in OPCharCreate * Remove unused function in client.cpp and undefined declaration. Also the function potentially had a null pointer dereference according to Visual Studio. --- world/client.cpp | 20 ++++---------------- world/client.h | 2 -- zone/client.cpp | 2 -- zone/mob.cpp | 14 ++++++-------- 4 files changed, 10 insertions(+), 28 deletions(-) diff --git a/world/client.cpp b/world/client.cpp index 555b0f24c..39a1789c8 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1490,14 +1490,6 @@ void Client::TellClientZoneUnavailable() { autobootup_timeout.Disable(); } -bool Client::GenPassKey(char* key) { - char* passKey=nullptr; - *passKey += ((char)('A'+((int)emu_random.Int(0, 25)))); - *passKey += ((char)('A'+((int)emu_random.Int(0, 25)))); - memcpy(key, passKey, strlen(passKey)); - return true; -} - void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req) { LogNetcode("Sending EQApplicationPacket OpCode {:#04x}", app->GetOpcode()); @@ -1571,7 +1563,6 @@ void Client::SendApproveWorld() bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { PlayerProfile_Struct pp; - ExtendedProfile_Struct ext; EQ::InventoryProfile inv; pp.SetPlayerProfileVersion(EQ::versions::ConvertClientVersionToMobVersion(EQ::versions::ConvertClientVersionBitToClientVersion(m_ClientVersionBit))); @@ -1579,9 +1570,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) inv.SetGMInventory(false); // character cannot have gm flag at this point time_t bday = time(nullptr); - char startzone[50]={0}; - uint32 i; - struct in_addr in; + in_addr in; int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA; @@ -1660,8 +1649,8 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) memset(pp.spell_book, 0xFF, (sizeof(uint32) * EQ::spells::SPELLBOOK_SIZE)); memset(pp.mem_spells, 0xFF, (sizeof(uint32) * EQ::spells::SPELL_GEM_COUNT)); - for(i = 0; i < BUFF_COUNT; i++) - pp.buffs[i].spellid = 0xFFFF; + for (auto& buff : pp.buffs) + buff.spellid = 0xFFFF; /* If server is PVP by default, make all character set to it. */ pp.pvp = database.GetServerType() == 1 ? 1 : 0; @@ -1782,7 +1771,6 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) return false; } - uint32 max_stats = 0; uint32 allocs = character_create_allocations.size(); RaceClassAllocation allocation = {0}; found = false; @@ -1799,7 +1787,7 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) return false; } - max_stats = allocation.DefaultPointAllocation[0] + + uint32 max_stats = allocation.DefaultPointAllocation[0] + allocation.DefaultPointAllocation[1] + allocation.DefaultPointAllocation[2] + allocation.DefaultPointAllocation[3] + diff --git a/world/client.h b/world/client.h index d94f5a598..76f14e6c1 100644 --- a/world/client.h +++ b/world/client.h @@ -39,7 +39,6 @@ public: ~Client(); bool Process(); - void ReceiveData(uchar* buf, int len); void SendCharInfo(); void SendMaxCharCreate(); void SendMembership(); @@ -54,7 +53,6 @@ public: void SendLogServer(); void SendApproveWorld(); void SendPostEnterWorld(); - bool GenPassKey(char* key); inline uint32 GetIP() { return ip; } inline uint16 GetPort() { return port; } diff --git a/zone/client.cpp b/zone/client.cpp index b15cc5c2d..3896fd058 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -5531,7 +5531,6 @@ bool Client::TryReward(uint32 claim_id) if (free_slot == 0xFFFFFFFF) return false; - char errbuf[MYSQL_ERRMSG_SIZE]; std::string query = StringFormat("SELECT amount FROM account_rewards " "WHERE account_id = %i AND reward_id = %i", AccountID(), claim_id); @@ -7997,7 +7996,6 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui for (int i = 0; i < MAX_NPC_FACTIONS; i++) { int32 faction_before_hit; - int32 faction_to_use_for_messaging; FactionMods fm; int32 this_faction_max; int32 this_faction_min; diff --git a/zone/mob.cpp b/zone/mob.cpp index 189ae6c8e..d171cbc78 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4549,9 +4549,9 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) /*The effects SE_SpellTrigger (SPA 340) and SE_Chance_Best_in_Spell_Grp (SPA 469) work as follows, you typically will have 2-3 different spells each with their own chance to be triggered with all chances equaling up to 100 pct, with only 1 spell out of the group being ultimately cast. (ie Effect1 trigger spellA with 30% chance, Effect2 triggers spellB with 20% chance, Effect3 triggers spellC with 50% chance). - The following function ensures a stastically accurate chance for each spell to be cast based on their chance values. These effects are also used in spells where there + The following function ensures a statistically accurate chance for each spell to be cast based on their chance values. These effects are also used in spells where there is only 1 effect using the trigger effect. In those situations we simply roll a chance for that spell to be cast once. - Note: Both SPA 340 and 469 can be in same spell and both cummulative add up to 100 pct chances. SPA469 only difference being the spell cast will + Note: Both SPA 340 and 469 can be in same spell and both cumulative add up to 100 pct chances. SPA469 only difference being the spell cast will be "best in spell group", instead of a defined spell_id.*/ int chance_array[EFFECT_COUNT] = {}; @@ -4568,15 +4568,13 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) if (total_chance == 100) { int current_chance = 0; - int cummulative_chance = 0; for (int i = 0; i < EFFECT_COUNT; i++){ - //Find spells with SPA 340 and add the cummulative percent chances to the roll array + //Find spells with SPA 340 and add the cumulative percent chances to the roll array if ((spells[spell_id].effect_id[i] == SE_SpellTrigger) || (spells[spell_id].effect_id[i] == SE_Chance_Best_in_Spell_Grp)){ - - cummulative_chance = current_chance + spells[spell_id].base_value[i]; - chance_array[i] = cummulative_chance; - current_chance = cummulative_chance; + const int cumulative_chance = current_chance + spells[spell_id].base_value[i]; + chance_array[i] = cumulative_chance; + current_chance = cumulative_chance; } } int random_roll = zone->random.Int(1, 100); From a00f086bb8e831df1fa4f99375fdada93dd7e454 Mon Sep 17 00:00:00 2001 From: Quintinon Date: Wed, 1 Jun 2022 14:17:14 -0700 Subject: [PATCH 051/552] [Combat] Fix shield calculation (#2234) * Fix max mitigation calculation * Fix shield ability not receiving correct arguments from perl script. * Correct shielder having wrong mitigation set. --- zone/attack.cpp | 6 +++--- zone/mob.cpp | 2 +- zone/perl_mob.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 854a4b6d4..757c73ca9 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -5668,17 +5668,17 @@ void Mob::DoShieldDamageOnShielder(Mob *shield_target, int64 hit_damage_done, EQ shielder->shield_timer.Disable(); shield_target->SetShielderID(0); shield_target->SetShieldTargetMitigation(0); - return; //Too far away, no message is given thoughh. + return; //Too far away, no message is given though. } - int mitigation = shielder->GetShielderMitigation(); //Default shielder mitigates 25 pct of damage taken, this can be increased up to max 50 by equiping a shield item + int mitigation = shielder->GetShielderMitigation(); //Default shielder mitigates 25 pct of damage taken, this can be increased up to max 50 by equipping a shield item if (shielder->IsClient() && shielder->HasShieldEquiped()) { EQ::ItemInstance* inst = shielder->CastToClient()->GetInv().GetItem(EQ::invslot::slotSecondary); if (inst) { const EQ::ItemData* shield = inst->GetItem(); if (shield && shield->ItemType == EQ::item::ItemTypeShield) { mitigation += shield->AC * 50 / 100; //1% increase per 2 AC - std::min(50, mitigation);//50 pct max mitigation bonus from /shield + mitigation = std::min(50, mitigation);//50 pct max mitigation bonus from /shield } } } diff --git a/zone/mob.cpp b/zone/mob.cpp index d171cbc78..6d127af32 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -6696,7 +6696,7 @@ bool Mob::ShieldAbility(uint32 target_id, int shielder_max_distance, int shield_ entity_list.MessageCloseString(this, false, 100, 0, START_SHIELDING, GetCleanName(), shield_target->GetCleanName()); SetShieldTargetID(shield_target->GetID()); - SetShielderMitigation(shield_target_mitigation); + SetShielderMitigation(shielder_mitigation); SetShielderMaxDistance(shielder_max_distance); shield_target->SetShielderID(GetID()); diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 668857fc1..f7557a14d 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -6569,7 +6569,7 @@ XS(XS_Mob_ShieldAbility) { if (items < 8) { can_shield_npc = true; } - THIS->ShieldAbility(target_id, shielder_max_distance, shield_duration, shield_duration, shield_duration, use_aa, can_shield_npc); + THIS->ShieldAbility(target_id, shielder_max_distance, shield_duration, shield_target_mitigation, shielder_mitigation, use_aa, can_shield_npc); } XSRETURN_EMPTY; From be1772d464ccba77d3520fa71f92597e06cb90fc Mon Sep 17 00:00:00 2001 From: Quintinon Date: Wed, 1 Jun 2022 18:17:39 -0700 Subject: [PATCH 052/552] [Bug Fix] Correct (probably) unintended bitwise AND instead of logical AND (#2239) --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 6d127af32..a3739866d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4598,7 +4598,7 @@ bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) SpellFinished(spells[spell_id].limit_value[effect_slot], target, EQ::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].limit_value[effect_slot]].resist_difficulty); return true; } - else if (IsClient() & spells[spell_id].effect_id[effect_slot] == SE_Chance_Best_in_Spell_Grp) { + else if (IsClient() && spells[spell_id].effect_id[effect_slot] == SE_Chance_Best_in_Spell_Grp) { uint32 best_spell_id = CastToClient()->GetHighestScribedSpellinSpellGroup(spells[spell_id].limit_value[effect_slot]); if (IsValidSpell(best_spell_id)) { SpellFinished(best_spell_id, target, EQ::spells::CastingSlot::Item, 0, -1, spells[best_spell_id].resist_difficulty); From 17034a6e47b9b36c7def7b49842491f80c73465e Mon Sep 17 00:00:00 2001 From: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com> Date: Sat, 4 Jun 2022 13:59:46 -0400 Subject: [PATCH 053/552] [Commands] Cleanup #spawneditmass Command. (#2229) * [Commands] Cleanup #spawneditmass Command. - Cleanup messages and logic. - Split command into its own file. * Save querying database if they're not using the only supported option. * Condense to one message. --- zone/command.cpp | 133 +------------------------- zone/gm_commands/spawneditmass.cpp | 148 +++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+), 131 deletions(-) create mode 100644 zone/gm_commands/spawneditmass.cpp diff --git a/zone/command.cpp b/zone/command.cpp index 01ab70256..b8914e593 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -299,7 +299,7 @@ int command_init(void) command_add("showzonepoints", "Show zone points for current zone", AccountStatus::Guide, command_showzonepoints) || command_add("shutdown", "Shut this zone process down", AccountStatus::GMLeadAdmin, command_shutdown) || command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", AccountStatus::Steward, command_spawn) || - command_add("spawneditmass", "Mass editing spawn command", AccountStatus::GMLeadAdmin, command_spawneditmass) || + command_add("spawneditmass", "[Search Criteria] [Edit Option] [Edit Value] [Apply] Mass editing spawn command (Apply is optional, 0 = False, 1 = True, default is False)", AccountStatus::GMLeadAdmin, command_spawneditmass) || command_add("spawnfix", "Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || command_add("spawnstatus", "[All|Disabled|Enabled|Spawn ID] - Show respawn timer status", AccountStatus::GMAdmin, command_spawnstatus) || command_add("spellinfo", "[spellid] - Get detailed info about a spell", AccountStatus::Steward, command_spellinfo) || @@ -651,136 +651,6 @@ void command_help(Client *c, const Seperator *sep) ); } -void command_spawneditmass(Client *c, const Seperator *sep) -{ - std::string query = fmt::format( - SQL( - SELECT - npc_types.id, - npc_types.name, - spawn2.respawntime, - spawn2.id - FROM - npc_types - JOIN spawnentry ON spawnentry.npcID = npc_types.id - JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID - WHERE - spawn2.zone = '{0}' and spawn2.version = {1} - GROUP BY npc_types.id - ), - zone->GetShortName(), - zone->GetInstanceVersion() - ); - - std::string status = "(Searching)"; - - if (strcasecmp(sep->arg[4], "apply") == 0) { - status = "(Applying)"; - } - - std::string search_value; - std::string edit_option; - std::string edit_value; - std::string apply_set; - - if (sep->arg[1]) { - search_value = sep->arg[1]; - } - - if (sep->arg[2]) { - edit_option = sep->arg[2]; - } - - if (sep->arg[3]) { - edit_value = sep->arg[3]; - } - - if (sep->arg[4]) { - apply_set = sep->arg[4]; - } - - if (!edit_option.empty() && edit_value.empty()) { - c->Message(Chat::Yellow, "Please specify an edit option value | #npceditmass