diff --git a/common/version.h b/common/version.h
index 0c6dea2b5..5640a63fe 100644
--- a/common/version.h
+++ b/common/version.h
@@ -34,7 +34,7 @@
#define CURRENT_BINARY_DATABASE_VERSION 9142
#ifdef BOTS
- #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9025
+ #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026
#else
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
#endif
diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl
index 042c3d078..e3a051ef7 100644
--- a/utils/scripts/eqemu_server.pl
+++ b/utils/scripts/eqemu_server.pl
@@ -399,11 +399,13 @@ sub build_linux_source {
print `git clone https://github.com/EQEmu/Server.git`;
mkdir($source_dir . "/Server/build") if (!-e $source_dir . "/Server/build");
- chdir($source_dir . "/Server/build");
+ chdir($source_dir . "/Server");
print `git submodule init`;
print `git submodule update`;
+ chdir($source_dir . "/Server/build");
+
print "Generating CMake build files...\n";
if ($os_flavor eq "fedora_core") {
print `cmake $cmake_options -DEQEMU_BUILD_LOGIN=ON -DEQEMU_BUILD_LUA=ON -DLUA_INCLUDE_DIR=/usr/include/lua-5.1/ -G "Unix Makefiles" ..`;
diff --git a/utils/scripts/linux_installer/install.sh b/utils/scripts/linux_installer/install.sh
index 6af4a9df3..5cef4ab8b 100644
--- a/utils/scripts/linux_installer/install.sh
+++ b/utils/scripts/linux_installer/install.sh
@@ -153,7 +153,7 @@ elif [[ "$OS" == "red_hat" ]]; then
yum -y install \
open-vm-tools \
vim \
- cmake \
+ cmake3 \
boost-* \
zlib-devel \
mariadb \
@@ -178,6 +178,12 @@ elif [[ "$OS" == "red_hat" ]]; then
"Development Tools" \
"Basic Web Server" \
"Compatibility Libraries"
+ # Deal with the cmake 3 prerequisite on RHEL/CentOS 6/7 Note: Might break with RHEL/CentOS 8
+ alternatives --install /usr/local/bin/cmake cmake /usr/bin/cmake3 20 \
+ --slave /usr/local/bin/ctest ctest /usr/bin/ctest3 \
+ --slave /usr/local/bin/cpack cpack /usr/bin/cpack3 \
+ --slave /usr/local/bin/ccmake ccmake /usr/bin/ccmake3 \
+ --family cmake
elif [[ "$OS" == "fedora_core" ]]; then
# Do Fedora stuff
diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt
index 405cf3f5c..0a1e4466a 100644
--- a/utils/sql/git/bots/bots_db_update_manifest.txt
+++ b/utils/sql/git/bots/bots_db_update_manifest.txt
@@ -24,6 +24,7 @@
9023|2019_06_22_bots_owner_option_stats_update.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'stats_update'|empty|
9024|2019_06_27_bots_pet_get_lost.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'petgetlost'|empty|
9025|2019_08_26_bots_owner_option_spawn_message.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'spawn_message_enabled'|empty|
+9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|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/bots/required/2019_09_09_bots_owner_options_rework.sql b/utils/sql/git/bots/required/2019_09_09_bots_owner_options_rework.sql
new file mode 100644
index 000000000..6240e9366
--- /dev/null
+++ b/utils/sql/git/bots/required/2019_09_09_bots_owner_options_rework.sql
@@ -0,0 +1,10 @@
+DROP TABLE IF EXISTS `bot_owner_options`;
+
+CREATE TABLE `bot_owner_options` (
+ `owner_id` INT(11) UNSIGNED NOT NULL,
+ `option_type` SMALLINT(3) UNSIGNED NOT NULL,
+ `option_value` SMALLINT(3) UNSIGNED NULL DEFAULT '0',
+ PRIMARY KEY (`owner_id`, `option_type`)
+)
+COLLATE='latin1_swedish_ci'
+ENGINE=InnoDB;
diff --git a/zone/bot.cpp b/zone/bot.cpp
index 971c1d1b6..efc5510b8 100644
--- a/zone/bot.cpp
+++ b/zone/bot.cpp
@@ -255,6 +255,18 @@ void Bot::SetBotSpellID(uint32 newSpellID) {
this->npc_spells_id = newSpellID;
}
+void Bot::SetSurname(std::string bot_surname) {
+ _surname = bot_surname.substr(0, 31);
+}
+
+void Bot::SetTitle(std::string bot_title) {
+ _title = bot_title.substr(0, 31);
+}
+
+void Bot::SetSuffix(std::string bot_suffix) {
+ _suffix = bot_suffix.substr(0, 31);
+}
+
uint32 Bot::GetBotArcheryRange() {
const EQEmu::ItemInstance *range_inst = GetBotItem(EQEmu::invslot::slotRange);
const EQEmu::ItemInstance *ammo_inst = GetBotItem(EQEmu::invslot::slotAmmo);
@@ -3260,7 +3272,7 @@ bool Bot::Spawn(Client* botCharacterOwner) {
else
this->GetBotOwner()->CastToClient()->Message(Chat::Red, "%s save failed!", this->GetCleanName());
- // Spawn the bot at the bow owner's loc
+ // Spawn the bot at the bot owner's loc
this->m_Position.x = botCharacterOwner->GetX();
this->m_Position.y = botCharacterOwner->GetY();
this->m_Position.z = botCharacterOwner->GetZ();
@@ -3365,6 +3377,9 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
ns->spawn.helm = helmtexture; //(GetShowHelm() ? helmtexture : 0); //0xFF;
ns->spawn.equip_chest2 = texture; //0xFF;
ns->spawn.show_name = true;
+ strcpy(ns->spawn.lastName, GetSurname().c_str());
+ strcpy(ns->spawn.title, GetTitle().c_str());
+ strcpy(ns->spawn.suffix, GetSuffix().c_str());
const EQEmu::ItemData* item = nullptr;
const EQEmu::ItemInstance* inst = nullptr;
uint32 spawnedbotid = 0;
@@ -3499,7 +3514,7 @@ void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) {
Bot* bot = *biter;
if(bot && (bot->GetLevel() != client->GetLevel())) {
bot->SetPetChooser(false); // not sure what this does, but was in bot 'update' code
- bot->CalcBotStats(client->GetBotOptionStatsUpdate());
+ bot->CalcBotStats(client->GetBotOption(Client::booStatsUpdate));
if(sendlvlapp)
bot->SendLevelAppearance();
// modified from Client::SetLevel()
@@ -4178,7 +4193,7 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
client->Message(Chat::Lime, "Trade with '%s' resulted in %i accepted item%s, %i returned item%s.", GetCleanName(), accepted_count, ((accepted_count == 1) ? "" : "s"), returned_count, ((returned_count == 1) ? "" : "s"));
if (accepted_count)
- CalcBotStats(client->GetBotOptionStatsUpdate());
+ CalcBotStats(client->GetBotOption(Client::booStatsUpdate));
}
bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) {
@@ -4188,7 +4203,7 @@ bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQEmu::skills::Sk
Save();
Mob *my_owner = GetBotOwner();
- if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOptionDeathMarquee()) {
+ if (my_owner && my_owner->IsClient() && my_owner->CastToClient()->GetBotOption(Client::booDeathMarquee)) {
if (killerMob)
my_owner->CastToClient()->SendMarqueeMessage(Chat::Yellow, 510, 0, 1000, 3000, StringFormat("%s has been slain by %s", GetCleanName(), killerMob->GetCleanName()));
else
diff --git a/zone/bot.h b/zone/bot.h
index 3b363748a..20348181e 100644
--- a/zone/bot.h
+++ b/zone/bot.h
@@ -270,7 +270,12 @@ public:
bool GetNeedsHateRedux(Mob *tar);
bool HasOrMayGetAggro();
void SetDefaultBotStance();
-
+ void SetSurname(std::string bot_surname);
+ void SetTitle(std::string bot_title);
+ void SetSuffix(std::string bot_suffix);
+ std::string GetSurname() { return _surname; }
+ std::string GetTitle() { return _title; }
+ std::string GetSuffix() { return _suffix; }
inline virtual int32 GetMaxStat();
inline virtual int32 GetMaxResist();
inline virtual int32 GetMaxSTR();
@@ -650,6 +655,9 @@ private:
uint32 _guildId;
uint8 _guildRank;
std::string _guildName;
+ std::string _surname;
+ std::string _title;
+ std::string _suffix;
uint32 _lastZoneId;
bool _rangerAutoWeaponSelect;
BotRoleType _botRole;
diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp
index 391f55dd7..467996848 100644
--- a/zone/bot_command.cpp
+++ b/zone/bot_command.cpp
@@ -1351,10 +1351,13 @@ int bot_command_init(void)
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) ||
@@ -3507,63 +3510,180 @@ void bot_command_movement_speed(Client *c, const Seperator *sep)
void bot_command_owner_option(Client *c, const Seperator *sep)
{
if (helper_is_help_or_usage(sep->arg[1])) {
- c->Message(m_usage, "usage: %s [deathmarquee | statsupdate] (argument: enable | disable | null (toggles))", sep->arg[0]);
- c->Message(m_usage, "usage: %s [spawnmessage] [argument: say | tell | silent | class | default]", sep->arg[0]);
+
+ c->Message(m_usage, "usage: %s [option] [argument | null]", sep->arg[0]);
+
+ std::string window_title = "Bot Owner Options";
+ std::string window_text =
+ "
"
+ ""
+ "| Option | "
+ "Argument | "
+ "Notes | "
+ "
"
+ ""
+ "| deathmarquee | "
+ "enable | "
+ " | "
+ "
"
+ ""
+ " | "
+ "disable | "
+ " | "
+ "
"
+ ""
+ " | "
+ "null | "
+ "(toggles) | "
+ "
"
+ ""
+ "| statsupdate | "
+ "enable | "
+ " | "
+ "
"
+ ""
+ " | "
+ "disable | "
+ " | "
+ "
"
+ ""
+ " | "
+ "null | "
+ "(toggles) | "
+ "
"
+ ""
+ "| spawnmessage | "
+ "say | "
+ " | "
+ "
"
+ ""
+ " | "
+ "tell | "
+ " | "
+ "
"
+ ""
+ " | "
+ "silent | "
+ " | "
+ "
"
+ ""
+ " | "
+ "class | "
+ " | "
+ "
"
+ ""
+ " | "
+ "default | "
+ " | "
+ "
"
+ "
";
+
+ c->SendPopupToClient(window_title.c_str(), window_text.c_str());
+
return;
}
- std::string owner_option = sep->arg[1];
- std::string argument = sep->arg[2];
+ std::string owner_option(sep->arg[1]);
+ std::string argument(sep->arg[2]);
if (!owner_option.compare("deathmarquee")) {
- if (!argument.compare("enable"))
- c->SetBotOptionDeathMarquee(true);
- else if (!argument.compare("disable"))
- c->SetBotOptionDeathMarquee(false);
- else
- c->SetBotOptionDeathMarquee(!c->GetBotOptionDeathMarquee());
-
- database.botdb.SaveOwnerOptionDeathMarquee(c->CharacterID(), c->GetBotOptionDeathMarquee());
- c->Message(m_action, "Bot 'death marquee' is now %s.", (c->GetBotOptionDeathMarquee() == true ? "enabled" : "disabled"));
- }
- else if (!owner_option.compare("statsupdate")) {
- if (!argument.compare("enable"))
- c->SetBotOptionStatsUpdate(true);
- else if (!argument.compare("disable"))
- c->SetBotOptionStatsUpdate(false);
- else
- c->SetBotOptionStatsUpdate(!c->GetBotOptionStatsUpdate());
- database.botdb.SaveOwnerOptionStatsUpdate(c->CharacterID(), c->GetBotOptionStatsUpdate());
- c->Message(m_action, "Bot 'stats update' is now %s.", (c->GetBotOptionStatsUpdate() == true ? "enabled" : "disabled"));
- }
- else if (!owner_option.compare("spawnmessage")) {
- if (!argument.compare("say")) {
- c->SetBotOptionSpawnMessageSay();
+ if (!argument.compare("enable")) {
+ c->SetBotOption(Client::booDeathMarquee, true);
}
- else if (!argument.compare("tell")) {
- c->SetBotOptionSpawnMessageTell();
- }
- else if (!argument.compare("silent")) {
- c->SetBotOptionSpawnMessageSilent();
- }
- else if (!argument.compare("class")) {
- c->SetBotOptionSpawnMessageClassSpecific(true);
- }
- else if (!argument.compare("default")) {
- c->SetBotOptionSpawnMessageClassSpecific(false);
+ else if (!argument.compare("disable")) {
+ c->SetBotOption(Client::booDeathMarquee, false);
}
else {
+ c->SetBotOption(Client::booDeathMarquee, !c->GetBotOption(Client::booDeathMarquee));
+ }
+
+ database.botdb.SaveOwnerOption(c->CharacterID(), Client::booDeathMarquee, c->GetBotOption(Client::booDeathMarquee));
+
+ c->Message(m_action, "Bot 'death marquee' is now %s.", (c->GetBotOption(Client::booDeathMarquee) == true ? "enabled" : "disabled"));
+ }
+ else if (!owner_option.compare("statsupdate")) {
+
+ if (!argument.compare("enable")) {
+ c->SetBotOption(Client::booStatsUpdate, true);
+ }
+ else if (!argument.compare("disable")) {
+ c->SetBotOption(Client::booStatsUpdate, false);
+ }
+ else {
+ c->SetBotOption(Client::booStatsUpdate, !c->GetBotOption(Client::booStatsUpdate));
+ }
+
+ database.botdb.SaveOwnerOption(c->CharacterID(), Client::booStatsUpdate, c->GetBotOption(Client::booStatsUpdate));
+
+ c->Message(m_action, "Bot 'stats update' is now %s.", (c->GetBotOption(Client::booStatsUpdate) == true ? "enabled" : "disabled"));
+ }
+ else if (!owner_option.compare("spawnmessage")) {
+
+ Client::BotOwnerOption boo = Client::_booCount;
+
+ if (!argument.compare("say")) {
+
+ boo = Client::booSpawnMessageSay;
+ c->SetBotOption(Client::booSpawnMessageSay, true);
+ c->SetBotOption(Client::booSpawnMessageTell, false);
+ }
+ else if (!argument.compare("tell")) {
+
+ boo = Client::booSpawnMessageSay;
+ c->SetBotOption(Client::booSpawnMessageSay, false);
+ c->SetBotOption(Client::booSpawnMessageTell, true);
+ }
+ else if (!argument.compare("silent")) {
+
+ boo = Client::booSpawnMessageSay;
+ c->SetBotOption(Client::booSpawnMessageSay, false);
+ c->SetBotOption(Client::booSpawnMessageTell, false);
+ }
+ else if (!argument.compare("class")) {
+
+ boo = Client::booSpawnMessageClassSpecific;
+ c->SetBotOption(Client::booSpawnMessageClassSpecific, true);
+ }
+ else if (!argument.compare("default")) {
+
+ boo = Client::booSpawnMessageClassSpecific;
+ c->SetBotOption(Client::booSpawnMessageClassSpecific, false);
+ }
+ else {
+
c->Message(m_fail, "Owner option '%s' argument '%s' is not recognized.", owner_option.c_str(), argument.c_str());
return;
}
- database.botdb.SaveOwnerOptionSpawnMessage(
- c->CharacterID(),
- c->GetBotOptionSpawnMessageSay(),
- c->GetBotOptionSpawnMessageTell(),
- c->GetBotOptionSpawnMessageClassSpecific()
- );
+ if (boo == Client::booSpawnMessageSay) {
+
+ database.botdb.SaveOwnerOption(
+ c->CharacterID(),
+ std::pair(
+ Client::booSpawnMessageSay,
+ Client::booSpawnMessageTell
+ ),
+ std::pair(
+ c->GetBotOption(Client::booSpawnMessageSay),
+ c->GetBotOption(Client::booSpawnMessageTell)
+ )
+ );
+ }
+ else if (boo == Client::booSpawnMessageClassSpecific) {
+
+ database.botdb.SaveOwnerOption(
+ c->CharacterID(),
+ Client::booSpawnMessageClassSpecific,
+ c->GetBotOption(Client::booSpawnMessageClassSpecific)
+ );
+ }
+ else {
+
+ c->Message(m_action, "Bot 'spawn message' is now ERROR.");
+ return;
+ }
+
c->Message(m_action, "Bot 'spawn message' is now %s.", argument.c_str());
}
else {
@@ -4472,7 +4592,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
return;
}
std::string bot_name = sep->arg[1];
-
+ bot_name = ucfirst(bot_name);
if (sep->arg[2][0] == '\0' || !sep->IsNumber(2)) {
c->Message(m_fail, "Invalid Class!");
return;
@@ -5000,17 +5120,22 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
if (helper_command_alias_fail(c, "bot_subcommand_bot_list", sep->arg[0], "botlist"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
- c->Message(m_usage, "usage: %s ([class] [value]) ([race] [value]) ([name] [partial-full])", sep->arg[0]);
+ c->Message(m_usage, "usage: %s (account) ([class] [value]) ([race] [value]) ([name] [partial-full])", sep->arg[0]);
c->Message(m_note, "note: filter criteria is orderless and optional");
return;
}
-
+ bool Account = false;
+ int seps = 1;
uint32 filter_value[FilterCount];
int name_criteria_arg = 0;
memset(&filter_value, 0, sizeof(uint32) * FilterCount);
int filter_mask = 0;
- for (int i = 1; i < (FilterCount * 2); i += 2) {
+ if (strcasecmp(sep->arg[1], "account") == 0) {
+ Account = true;
+ seps = 2;
+ }
+ for (int i = seps; i < (FilterCount * 2); i += 2) {
if (sep->arg[i][0] == '\0')
break;
@@ -5035,7 +5160,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
}
std::list bots_list;
- if (!database.botdb.LoadBotsList(c->CharacterID(), bots_list)) {
+ if (!database.botdb.LoadBotsList(c->CharacterID(), bots_list, Account)) {
c->Message(m_fail, "%s", BotDatabase::fail::LoadBotsList());
return;
}
@@ -5045,6 +5170,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
}
int bot_count = 0;
+ int bots_owned = 0;
for (auto bots_iter : bots_list) {
if (filter_mask) {
if ((filter_mask & MaskClass) && filter_value[FilterClass] != bots_iter.Class)
@@ -5060,23 +5186,26 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
continue;
}
}
-
- c->Message(m_message, "%s is a level %u %s %s %s",
- bots_iter.Name,
+ Bot * botCheckNotOnline = entity_list.GetBotByBotName(bots_iter.Name);
+ std::string botspawn_saylink = StringFormat("^botspawn %s", bots_iter.Name);
+ c->Message(Chat::White, "%s is a level %u %s %s %s who is owned by %s",
+ ((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQEmu::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)),
bots_iter.Level,
Bot::RaceIdToString(bots_iter.Race).c_str(),
((bots_iter.Gender == FEMALE) ? ("Female") : ((bots_iter.Gender == MALE) ? ("Male") : ("Neuter"))),
- Bot::ClassIdToString(bots_iter.Class).c_str()
+ Bot::ClassIdToString(bots_iter.Class).c_str(),
+ bots_iter.Owner
);
-
+ if (c->CharacterID() == bots_iter.Owner_ID) { ++bots_owned; }
++bot_count;
}
if (!bot_count) {
- c->Message(m_fail, "You have no bots meeting this criteria");
+ c->Message(Chat::Red, "You have no bots meeting this criteria");
}
else {
- c->Message(m_action, "%i of %i bot%s shown", bot_count, bots_list.size(), ((bot_count != 1) ? ("s") : ("")));
- c->Message(m_message, "Your limit is %i bot%s", RuleI(Bots, CreationLimit), ((RuleI(Bots, CreationLimit) != 1) ? ("s") : ("")));
+ c->Message(Chat::Yellow, "%i of %i bot%s shown.", bot_count, bots_list.size(), ((bot_count != 1) ? ("s") : ("")));
+ c->Message(Chat::Yellow, "%i of %i bot%s are owned by you. (You may spawn any available by clicking name)", bots_owned, bot_count, ((bot_count != 1) ? ("s") : ("")));
+ c->Message(Chat::White, "Your limit is %i bot%s", RuleI(Bots, CreationLimit), ((RuleI(Bots, CreationLimit) != 1) ? ("s") : ("")));
}
}
@@ -5122,6 +5251,103 @@ void bot_subcommand_bot_out_of_combat(Client *c, const Seperator *sep)
}
}
+void bot_subcommand_bot_surname(Client *c, const Seperator *sep)
+{
+ if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) {
+ c->Message(Chat::Red, "You must specify a [surname] to use this command (use _ to define spaces or -remove to clear.)");
+ return;
+ }
+ auto my_bot = ActionableBots::AsTarget_ByBot(c);
+ if (!my_bot) {
+ c->Message(Chat::Red, "You must a bot that you own to use this command");
+ return;
+ }
+ if (strlen(sep->arg[1]) > 31) {
+ c->Message(Chat::Red, "Surname must be 31 characters or less.");
+ return;
+ }
+ std::string bot_surname = sep->arg[1];
+ bot_surname = (bot_surname == "-remove") ? "" : bot_surname;
+ std::replace(bot_surname.begin(), bot_surname.end(), '_', ' ');
+ my_bot->SetSurname(bot_surname);
+ if (!database.botdb.SaveBot(my_bot)) {
+ c->Message(Chat::Red, BotDatabase::fail::SaveBot());
+ return;
+ }
+ else {
+ auto outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct));
+ GMLastName_Struct * gmn = (GMLastName_Struct*)outapp->pBuffer;
+ strcpy(gmn->name, my_bot->GetCleanName());
+ strcpy(gmn->gmname, my_bot->GetCleanName());
+ strcpy(gmn->lastname, my_bot->GetSurname().c_str());
+ gmn->unknown[0] = 1;
+ gmn->unknown[1] = 1;
+ gmn->unknown[2] = 1;
+ gmn->unknown[3] = 1;
+ entity_list.QueueClients(my_bot->CastToClient(), outapp);
+ safe_delete(outapp);
+ c->Message(Chat::Yellow, "Bot Surname Saved.");
+ }
+}
+
+void bot_subcommand_bot_title(Client *c, const Seperator *sep)
+{
+ if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) {
+ c->Message(Chat::Red, "You must specify a [title] to use this command. (use _ to define spaces or -remove to clear.)");
+ return;
+ }
+ auto my_bot = ActionableBots::AsTarget_ByBot(c);
+ if (!my_bot) {
+ c->Message(Chat::Red, "You must a bot that you own to use this command");
+ return;
+ }
+ if (strlen(sep->arg[1]) > 31) {
+ c->Message(Chat::Red, "Title must be 31 characters or less.");
+ return;
+ }
+ std::string bot_title = sep->arg[1];
+ bot_title = (bot_title == "-remove") ? "" : bot_title;
+ std::replace(bot_title.begin(), bot_title.end(), '_', ' ');
+ my_bot->SetTitle(bot_title);
+ if (!database.botdb.SaveBot(my_bot)) {
+ c->Message(Chat::Red, BotDatabase::fail::SaveBot());
+ return;
+ }
+ else {
+ my_bot->CastToClient()->SetAATitle(my_bot->GetTitle().c_str());
+ c->Message(Chat::Yellow, "Bot Title Saved.");
+ }
+}
+
+void bot_subcommand_bot_suffix(Client *c, const Seperator *sep)
+{
+ if (sep->arg[1][0] == '\0' || sep->IsNumber(1)) {
+ c->Message(Chat::Red, "You must specify a [suffix] to use this command. (use _ to define spaces or -remove to clear.)");
+ return;
+ }
+ auto my_bot = ActionableBots::AsTarget_ByBot(c);
+ if (!my_bot) {
+ c->Message(Chat::Red, "You must a bot that you own to use this command");
+ return;
+ }
+ if (strlen(sep->arg[1]) > 31) {
+ c->Message(Chat::Red, "Suffix must be 31 characters or less.");
+ return;
+ }
+ std::string bot_suffix = sep->arg[1];
+ bot_suffix = (bot_suffix == "-remove") ? "" : bot_suffix;
+ std::replace(bot_suffix.begin(), bot_suffix.end(), '_', ' ');
+ my_bot->SetSuffix(bot_suffix);
+ if (!database.botdb.SaveBot(my_bot)) {
+ c->Message(Chat::Red, BotDatabase::fail::SaveBot());
+ return;
+ }
+ else {
+ my_bot->CastToClient()->SetTitleSuffix(my_bot->GetSuffix().c_str());
+ c->Message(Chat::Yellow, "Bot Suffix Saved.");
+ }
+}
+
void bot_subcommand_bot_report(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_subcommand_bot_report", sep->arg[0], "botreport"))
@@ -5285,13 +5511,16 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep)
};
uint8 message_index = 0;
- if (c->GetBotOptionSpawnMessageClassSpecific())
+ if (c->GetBotOption(Client::booSpawnMessageClassSpecific)) {
message_index = VALIDATECLASSID(my_bot->GetClass());
+ }
- if (c->GetBotOptionSpawnMessageSay())
+ if (c->GetBotOption(Client::booSpawnMessageSay)) {
Bot::BotGroupSay(my_bot, "%s", bot_spawn_message[message_index]);
- else if (c->GetBotOptionSpawnMessageTell())
+ }
+ else if (c->GetBotOption(Client::booSpawnMessageTell)) {
c->Message(Chat::Tell, "%s tells you, \"%s\"", my_bot->GetCleanName(), bot_spawn_message[message_index]);
+ }
}
void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
@@ -5671,7 +5900,7 @@ void bot_subcommand_bot_update(Client *c, const Seperator *sep)
continue;
bot_iter->SetPetChooser(false);
- bot_iter->CalcBotStats(c->GetBotOptionStatsUpdate());
+ bot_iter->CalcBotStats(c->GetBotOption(Client::booStatsUpdate));
bot_iter->SendAppearancePacket(AT_WhoLevel, bot_iter->GetLevel(), true, true);
++bot_count;
}
@@ -7460,7 +7689,7 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
}
my_bot->BotRemoveEquipItem(slotId);
- my_bot->CalcBotStats(c->GetBotOptionStatsUpdate());
+ my_bot->CalcBotStats(c->GetBotOption(Client::booStatsUpdate));
}
switch (slotId) {
diff --git a/zone/bot_command.h b/zone/bot_command.h
index d60238f23..c8bad1e42 100644
--- a/zone/bot_command.h
+++ b/zone/bot_command.h
@@ -614,8 +614,11 @@ void bot_subcommand_bot_report(Client *c, const Seperator *sep);
void bot_subcommand_bot_spawn(Client *c, const Seperator *sep);
void bot_subcommand_bot_stance(Client *c, const Seperator *sep);
void bot_subcommand_bot_stop_melee_level(Client *c, const Seperator *sep);
+void bot_subcommand_bot_suffix(Client *c, const Seperator *sep);
void bot_subcommand_bot_summon(Client *c, const Seperator *sep);
+void bot_subcommand_bot_surname(Client *c, const Seperator *sep);
void bot_subcommand_bot_tattoo(Client *c, const Seperator *sep);
+void bot_subcommand_bot_title(Client *c, const Seperator *sep);
void bot_subcommand_bot_toggle_archer(Client *c, const Seperator *sep);
void bot_subcommand_bot_toggle_helm(Client *c, const Seperator *sep);
void bot_subcommand_bot_update(Client *c, const Seperator *sep);
diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp
index db2f2018b..931b927f6 100644
--- a/zone/bot_database.cpp
+++ b/zone/bot_database.cpp
@@ -29,6 +29,8 @@
#include "bot.h"
#include "client.h"
+#include
+
bool BotDatabase::LoadBotCommandSettings(std::map>> &bot_command_settings)
{
@@ -217,12 +219,19 @@ bool BotDatabase::LoadQuestableSpawnCount(const uint32 owner_id, int& spawn_coun
return true;
}
-bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list& bots_list)
+bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list& bots_list, bool ByAccount)
{
if (!owner_id)
return false;
- query = StringFormat("SELECT `bot_id`, `name`, `class`, `level`, `race`, `gender` FROM `bot_data` WHERE `owner_id` = '%u'", owner_id);
+ if (ByAccount == true)
+ query = StringFormat("SELECT bot_id, bd.`name`, bd.class, bd.`level`, bd.race, bd.gender, cd.`name` as owner, bd.owner_id, cd.account_id, cd.id"
+ " FROM bot_data as bd inner join character_data as cd on bd.owner_id = cd.id"
+ " WHERE cd.account_id = (select account_id from bot_data bd inner join character_data as cd on bd.owner_id = cd.id where bd.owner_id = '%u' LIMIT 1)"
+ " ORDER BY bd.owner_id", owner_id);
+ else
+ query = StringFormat("SELECT `bot_id`, `name`, `class`, `level`, `race`, `gender`, 'You' as owner, owner_id FROM `bot_data` WHERE `owner_id` = '%u'", owner_id);
+
auto results = database.QueryDatabase(query);
if (!results.Success())
return false;
@@ -240,12 +249,17 @@ bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list 63)
+ bot_owner = bot_owner.substr(0, 63);
+ if (!bot_owner.empty())
+ strcpy(bot_entry.Owner, bot_owner.c_str());
bot_entry.Class = atoi(row[2]);
bot_entry.Level = atoi(row[3]);
bot_entry.Race = atoi(row[4]);
bot_entry.Gender = atoi(row[5]);
-
+ bot_entry.Owner_ID = atoi(row[7]);
bots_list.push_back(bot_entry);
}
@@ -320,8 +334,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
" `spells_id`,"
" `name`,"
" `last_name`,"
- " `title`," /* planned use[4] */
- " `suffix`," /* planned use[5] */
+ " `title`,"
+ " `suffix`,"
" `zone_id`,"
" `gender`,"
" `race`,"
@@ -418,7 +432,9 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct);
if (loaded_bot) {
loaded_bot->SetShowHelm((atoi(row[43]) > 0 ? true : false));
-
+ loaded_bot->SetSurname(row[3]);//maintaining outside mob::lastname to cater to spaces
+ loaded_bot->SetTitle(row[4]);
+ loaded_bot->SetSuffix(row[5]);
uint32 bfd = atoi(row[44]);
if (bfd < 1)
bfd = 1;
@@ -627,12 +643,14 @@ bool BotDatabase::SaveBot(Bot* bot_inst)
" `corruption` = '%i',"
" `show_helm` = '%i',"
" `follow_distance` = '%i',"
- " `stop_melee_level` = '%u'"
+ " `stop_melee_level` = '%u',"
+ " `title` = '%s',"
+ " `suffix` = '%s'"
" WHERE `bot_id` = '%u'",
bot_inst->GetBotOwnerCharacterID(),
bot_inst->GetBotSpellID(),
bot_inst->GetCleanName(),
- bot_inst->GetLastName(),
+ bot_inst->GetSurname().c_str(),
bot_inst->GetLastZoneID(),
bot_inst->GetBaseGender(),
bot_inst->GetBaseRace(),
@@ -670,6 +688,8 @@ bool BotDatabase::SaveBot(Bot* bot_inst)
((bot_inst->GetShowHelm()) ? (1) : (0)),
bot_inst->GetFollowDistance(),
bot_inst->GetStopMeleeLevel(),
+ bot_inst->GetTitle().c_str(),
+ bot_inst->GetSuffix().c_str(),
bot_inst->GetBotID()
);
auto results = database.QueryDatabase(query);
@@ -2207,111 +2227,92 @@ bool BotDatabase::SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id,
bool BotDatabase::LoadOwnerOptions(Client *owner)
{
- if (!owner || !owner->CharacterID())
- return false;
-
- query = StringFormat(
- "SELECT `death_marquee`, `stats_update`, `spawn_message_enabled`, `spawn_message_type` FROM `bot_owner_options`"
- " WHERE `owner_id` = '%u'",
- owner->CharacterID()
- );
- auto results = database.QueryDatabase(query);
- if (!results.Success())
- return false;
- if (!results.RowCount()) {
- query = StringFormat("REPLACE INTO `bot_owner_options` (`owner_id`) VALUES ('%u')", owner->CharacterID());
- results = database.QueryDatabase(query);
-
+ if (!owner || !owner->CharacterID()) {
return false;
}
- auto row = results.begin();
- owner->SetBotOptionDeathMarquee((atoi(row[0]) != 0));
- owner->SetBotOptionStatsUpdate((atoi(row[1]) != 0));
- switch (atoi(row[2])) {
- case 2:
- owner->SetBotOptionSpawnMessageSay();
- break;
- case 1:
- owner->SetBotOptionSpawnMessageTell();
- break;
+ query = fmt::format("SELECT `option_type`, `option_value` FROM `bot_owner_options` WHERE `owner_id` = '{}'", owner->CharacterID());
+
+ auto results = database.QueryDatabase(query);
+ if (!results.Success()) {
+ return false;
+ }
+
+ for (auto row : results) {
+
+ owner->SetBotOption(static_cast(atoul(row[0])), (atoul(row[1]) != 0));
+ }
+
+ return true;
+}
+
+bool BotDatabase::SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag)
+{
+ if (!owner_id) {
+ return false;
+ }
+
+ switch (static_cast(type)) {
+ case Client::booDeathMarquee:
+ case Client::booStatsUpdate:
+ case Client::booSpawnMessageClassSpecific: {
+
+ query = fmt::format(
+ "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}')",
+ owner_id,
+ type,
+ (flag == true ? 1 : 0)
+ );
+
+ auto results = database.QueryDatabase(query);
+ if (!results.Success()) {
+ return false;
+ }
+
+ return true;
+ }
default:
- owner->SetBotOptionSpawnMessageSilent();
- break;
+ return false;
}
- owner->SetBotOptionSpawnMessageClassSpecific((atoi(row[3]) != 0));
-
- return true;
}
-bool BotDatabase::SaveOwnerOptionDeathMarquee(const uint32 owner_id, const bool flag)
+bool BotDatabase::SaveOwnerOption(const uint32 owner_id, const std::pair type, const std::pair flag)
{
- if (!owner_id)
+ if (!owner_id) {
return false;
+ }
- query = StringFormat(
- "UPDATE `bot_owner_options`"
- " SET `death_marquee` = '%u'"
- " WHERE `owner_id` = '%u'",
- (flag == true ? 1 : 0),
- owner_id
- );
- auto results = database.QueryDatabase(query);
- if (!results.Success())
+ switch (static_cast(type.first)) {
+ case Client::booSpawnMessageSay:
+ case Client::booSpawnMessageTell: {
+ switch (static_cast(type.second)) {
+ case Client::booSpawnMessageSay:
+ case Client::booSpawnMessageTell: {
+
+ query = fmt::format(
+ "REPLACE INTO `bot_owner_options`(`owner_id`, `option_type`, `option_value`) VALUES ('{}', '{}', '{}'), ('{}', '{}', '{}')",
+ owner_id,
+ type.first,
+ (flag.first == true ? 1 : 0),
+ owner_id,
+ type.second,
+ (flag.second == true ? 1 : 0)
+ );
+
+ auto results = database.QueryDatabase(query);
+ if (!results.Success()) {
+ return false;
+ }
+
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
+ default:
return false;
-
- return true;
-}
-
-bool BotDatabase::SaveOwnerOptionStatsUpdate(const uint32 owner_id, const bool flag)
-{
- if (!owner_id)
- return false;
-
- query = StringFormat(
- "UPDATE `bot_owner_options`"
- " SET `stats_update` = '%u'"
- " WHERE `owner_id` = '%u'",
- (flag == true ? 1 : 0),
- owner_id
- );
- auto results = database.QueryDatabase(query);
- if (!results.Success())
- return false;
-
- return true;
-}
-
-bool BotDatabase::SaveOwnerOptionSpawnMessage(const uint32 owner_id, const bool say, const bool tell, const bool class_specific)
-{
- if (!owner_id)
- return false;
-
- uint8 enabled_value = 0;
- if (say)
- enabled_value = 2;
- else if (tell)
- enabled_value = 1;
-
- uint8 type_value = 0;
- if (class_specific)
- type_value = 1;
-
- query = StringFormat(
- "UPDATE `bot_owner_options`"
- " SET"
- " `spawn_message_enabled` = '%u',"
- " `spawn_message_type` = '%u'"
- " WHERE `owner_id` = '%u'",
- enabled_value,
- type_value,
- owner_id
- );
- auto results = database.QueryDatabase(query);
- if (!results.Success())
- return false;
-
- return true;
+ }
}
diff --git a/zone/bot_database.h b/zone/bot_database.h
index 0040f4a45..82cf68f09 100644
--- a/zone/bot_database.h
+++ b/zone/bot_database.h
@@ -52,7 +52,7 @@ public:
bool QueryNameAvailablity(const std::string& bot_name, bool& available_flag);
bool QueryBotCount(const uint32 owner_id, uint32& bot_count);
bool LoadQuestableSpawnCount(const uint32 owner_id, int& spawn_count);
- bool LoadBotsList(const uint32 owner_id, std::list& bots_list);
+ bool LoadBotsList(const uint32 owner_id, std::list& bots_list, bool ByAccount = false);
bool LoadOwnerID(const std::string& bot_name, uint32& owner_id);
bool LoadOwnerID(const uint32 bot_id, uint32& owner_id);
@@ -141,10 +141,9 @@ public:
bool SaveStopMeleeLevel(const uint32 owner_id, const uint32 bot_id, const uint8 sml_value);
bool LoadOwnerOptions(Client *owner);
- bool SaveOwnerOptionDeathMarquee(const uint32 owner_id, const bool flag);
- bool SaveOwnerOptionStatsUpdate(const uint32 owner_id, const bool flag);
- bool SaveOwnerOptionSpawnMessage(const uint32 owner_id, const bool say, const bool tell, const bool class_specific);
-
+ bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag);
+ bool SaveOwnerOption(const uint32 owner_id, const std::pair type, const std::pair flag);
+
/* Bot bot-group functions */
bool QueryBotGroupExistence(const std::string& botgroup_name, bool& extant_flag);
diff --git a/zone/bot_structs.h b/zone/bot_structs.h
index d54c0c042..7ba32349b 100644
--- a/zone/bot_structs.h
+++ b/zone/bot_structs.h
@@ -32,6 +32,8 @@ struct BotsAvailableList {
uint8 Level;
uint16 Race;
uint8 Gender;
+ char Owner[64];
+ uint32 Owner_ID;
};
struct BotGroup {
diff --git a/zone/client.cpp b/zone/client.cpp
index c1293be3f..ba65695c9 100644
--- a/zone/client.cpp
+++ b/zone/client.cpp
@@ -347,7 +347,11 @@ Client::Client(EQStreamInterface* ieqs)
dev_tools_window_enabled = true;
#ifdef BOTS
- bot_owner_options = DefaultBotOwnerOptions;
+ bot_owner_options[booDeathMarquee] = false;
+ bot_owner_options[booStatsUpdate] = false;
+ bot_owner_options[booSpawnMessageSay] = false;
+ bot_owner_options[booSpawnMessageTell] = true;
+ bot_owner_options[booSpawnMessageClassSpecific] = true;
#endif
AI_Init();
@@ -9134,4 +9138,24 @@ glm::vec4 &Client::GetLastPositionBeforeBulkUpdate()
void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_bulk_update)
{
Client::last_position_before_bulk_update = in_last_position_before_bulk_update;
-}
\ No newline at end of file
+}
+
+#ifdef BOTS
+
+bool Client::GetBotOption(BotOwnerOption boo) const {
+
+ if (boo < _booCount) {
+ return bot_owner_options[boo];
+ }
+
+ return false;
+}
+
+void Client::SetBotOption(BotOwnerOption boo, bool flag) {
+
+ if (boo < _booCount) {
+ bot_owner_options[boo] = flag;
+ }
+}
+
+#endif
diff --git a/zone/client.h b/zone/client.h
index 88bee0b03..54a1260ae 100644
--- a/zone/client.h
+++ b/zone/client.h
@@ -1626,39 +1626,26 @@ private:
int client_max_level;
#ifdef BOTS
- struct BotOwnerOptions {
- bool death_marquee;
- bool stats_update;
- bool spawn_message_say;
- bool spawn_message_tell;
- bool spawn_message_class_specific;
- };
+
- BotOwnerOptions bot_owner_options;
-
- const BotOwnerOptions DefaultBotOwnerOptions = {
- false, // death_marquee
- false, // stats_update
- false, // spawn_message_say
- true, // spawn_message_tell
- true // spawn_message_class_specific
- };
+
public:
- void SetBotOptionDeathMarquee(bool flag) { bot_owner_options.death_marquee = flag; }
- void SetBotOptionStatsUpdate(bool flag) { bot_owner_options.stats_update = flag; }
- void SetBotOptionSpawnMessageSay() { bot_owner_options.spawn_message_say = true; bot_owner_options.spawn_message_tell = false; }
- void SetBotOptionSpawnMessageTell() { bot_owner_options.spawn_message_say = false; bot_owner_options.spawn_message_tell = true; }
- void SetBotOptionSpawnMessageSilent() { bot_owner_options.spawn_message_say = false; bot_owner_options.spawn_message_tell = false; }
- void SetBotOptionSpawnMessageClassSpecific(bool flag) { bot_owner_options.spawn_message_class_specific = flag; }
+ enum BotOwnerOption : size_t {
+ booDeathMarquee,
+ booStatsUpdate,
+ booSpawnMessageSay,
+ booSpawnMessageTell,
+ booSpawnMessageClassSpecific,
+ _booCount
+ };
- bool GetBotOptionDeathMarquee() const { return bot_owner_options.death_marquee; }
- bool GetBotOptionStatsUpdate() const { return bot_owner_options.stats_update; }
- bool GetBotOptionSpawnMessageSay() const { return bot_owner_options.spawn_message_say; }
- bool GetBotOptionSpawnMessageTell() const { return bot_owner_options.spawn_message_tell; }
- bool GetBotOptionSpawnMessageClassSpecific() const { return bot_owner_options.spawn_message_class_specific; }
+ bool GetBotOption(BotOwnerOption boo) const;
+ void SetBotOption(BotOwnerOption boo, bool flag = true);
+
+private:
+ bool bot_owner_options[_booCount];
-private:
#endif
};
diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp
index d5dde9750..94b014243 100644
--- a/zone/client_packet.cpp
+++ b/zone/client_packet.cpp
@@ -3956,9 +3956,11 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app)
#ifdef BOTS
// This block is necessary to clean up any bot objects owned by a Client
Bot::BotOrderCampAll(this);
- auto group = GetGroup();
- if (group && group->GroupCount() < 2)
- group->DisbandGroup();
+ // Evidently, this is bad under certain conditions and causes crashes...
+ // Group and Raid code really needs to be overhauled to account for non-client types (mercs and bots)
+ //auto group = GetGroup();
+ //if (group && group->GroupCount() < 2)
+ // group->DisbandGroup();
#endif
if (IsLFP())
worldserver.StopLFP(CharacterID());
diff --git a/zone/command.cpp b/zone/command.cpp
index db9cd5524..761cecedd 100755
--- a/zone/command.cpp
+++ b/zone/command.cpp
@@ -182,6 +182,7 @@ int command_init(void)
command_add("crashtest", "- Crash the zoneserver", 255, command_crashtest) ||
command_add("cvs", "- Summary of client versions currently online.", 200, command_cvs) ||
command_add("damage", "[amount] - Damage your target", 100, command_damage) ||
+ command_add("databuckets", "View|Delete [key] [limit]- View data buckets, limit 50 default or Delete databucket by key", 80, command_databuckets) ||
command_add("date", "[yyyy] [mm] [dd] [HH] [MM] - Set EQ time", 90, command_date) ||
command_add("dbspawn2", "[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table", 100, command_dbspawn2) ||
command_add("delacct", "[accountname] - Delete an account", 150, command_delacct) ||
@@ -12637,6 +12638,85 @@ void command_scale(Client *c, const Seperator *sep)
}
}
+void command_databuckets(Client *c, const Seperator *sep)
+ {
+ if (sep->arg[1][0] == 0) {
+ c->Message(Chat::Yellow, "Usage: #databuckets view (partial key)|(limit) OR #databuckets delete (key)");
+ return;
+ }
+ if (strcasecmp(sep->arg[1], "view") == 0) {
+
+ std::string key_filter;
+ uint8 limit = 50;
+ for (int i = 2; i < 4; i++) {
+ if (sep->arg[i][0] == '\0')
+ break;
+ if (strcasecmp(sep->arg[i], "limit") == 0) {
+ limit = (uint8)atoi(sep->arg[i + 1]);
+ continue;
+ }
+ }
+ if (sep->arg[2]) {
+ key_filter = str_tolower(sep->arg[2]);
+ }
+ std::string query = "SELECT `id`, `key`, `value`, `expires` FROM data_buckets";
+ if (!key_filter.empty()) query += StringFormat(" WHERE `key` LIKE '%%%s%%'", key_filter.c_str());
+ query += StringFormat(" LIMIT %u", limit);
+ auto results = database.QueryDatabase(query);
+ if (!results.Success())
+ return;
+ if (results.RowCount() == 0) {
+ c->Message(Chat::Yellow, "No data_buckets found");
+ return;
+ }
+ int _ctr = 0;
+ // put in window for easier readability in case want command line for something else
+ std::string window_title = "Data Buckets";
+ std::string window_text =
+ ""
+ ""
+ "| ID | "
+ "Expires | "
+ "Key | "
+ "Value | "
+ "
";
+ for (auto row = results.begin(); row != results.end(); ++row) {
+ auto id = static_cast(atoi(row[0]));
+ std::string key = row[1];
+ std::string value = row[2];
+ std::string expires = row[3];
+ window_text.append(StringFormat(
+ ""
+ "| %u | "
+ "%s | "
+ "%s | "
+ "%s | "
+ "
",
+ id,
+ expires.c_str(),
+ key.c_str(),
+ value.c_str()
+ ));
+ _ctr++;
+ std::string del_saylink = StringFormat("#databuckets delete %s", key.c_str());
+ c->Message(Chat::White, "%s : %s",
+ EQEmu::SayLinkEngine::GenerateQuestSaylink(del_saylink, false, "Delete").c_str(), key.c_str(), " Value: ", value.c_str());
+ }
+ window_text.append("
");
+ c->SendPopupToClient(window_title.c_str(), window_text.c_str());
+ std::string response = _ctr > 0 ? StringFormat("Found %i matching data buckets", _ctr).c_str() : "No Databuckets found.";
+ c->Message(Chat::Yellow, response.c_str());
+ }
+ else if (strcasecmp(sep->arg[1], "delete") == 0)
+ {
+ if (DataBucket::DeleteData(sep->argplus[2]))
+ c->Message(Chat::Yellow, "data bucket %s deleted.", sep->argplus[2]);
+ else
+ c->Message(Chat::Red, "An error occurred deleting data bucket %s", sep->argplus[2]);
+ return;
+ }
+}
+
void command_who(Client *c, const Seperator *sep)
{
std::string query =
diff --git a/zone/command.h b/zone/command.h
index af92ec3f8..b1b488d49 100644
--- a/zone/command.h
+++ b/zone/command.h
@@ -77,6 +77,7 @@ void command_crashtest(Client *c, const Seperator *sep);
void command_cvs(Client *c, const Seperator *sep);
void command_d1(Client *c, const Seperator *sep);
void command_damage(Client *c, const Seperator *sep);
+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);
diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp
index 91f456768..2f0a3def2 100755
--- a/zone/zonedb.cpp
+++ b/zone/zonedb.cpp
@@ -15,6 +15,7 @@
#include
#include
+#include
extern Zone* zone;
@@ -4072,60 +4073,121 @@ bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, in
bool ZoneDatabase::LoadFactionData()
{
- std::string query = "SELECT MAX(id) FROM faction_list";
- auto results = QueryDatabase(query);
- if (!results.Success()) {
+ std::string query("SELECT MAX(`id`) FROM `faction_list`");
+
+ auto faction_max_results = QueryDatabase(query);
+ if (!faction_max_results.Success() || faction_max_results.RowCount() == 0) {
return false;
}
- if (results.RowCount() == 0)
- return false;
+ auto fmr_row = faction_max_results.begin();
- auto row = results.begin();
+ max_faction = atoul(fmr_row[0]);
+ faction_array = new Faction *[max_faction + 1];
- max_faction = row[0] ? atoi(row[0]) : 0;
- faction_array = new Faction*[max_faction+1];
- for(unsigned int index=0; index faction_ids;
+
+ // load factions
+ query = "SELECT `id`, `name`, `base` FROM `faction_list`";
- query = "SELECT id, name, base FROM faction_list";
- results = QueryDatabase(query);
- if (!results.Success()) {
+ auto faction_results = QueryDatabase(query);
+ if (!faction_results.Success()) {
return false;
}
- for (row = results.begin(); row != results.end(); ++row) {
- uint32 index = atoi(row[0]);
+ for (auto fr_row : faction_results) {
+
+ uint32 index = atoul(fr_row[0]);
+ if (index > max_faction) {
+ Log(Logs::General, Logs::Error, "Faction '%u' is out-of-bounds for faction array size!", index);
+ continue;
+ }
+
+ // this should never hit since `id` is keyed..but, it alleviates any risk of lost pointers
+ if (faction_array[index] != nullptr) {
+ Log(Logs::General, Logs::Error, "Faction '%u' has already been assigned! (Duplicate Entry)", index);
+ continue;
+ }
+
faction_array[index] = new Faction;
- strn0cpy(faction_array[index]->name, row[1], 50);
- faction_array[index]->base = atoi(row[2]);
+ strn0cpy(faction_array[index]->name, fr_row[1], 50);
+ faction_array[index]->base = atoi(fr_row[2]);
faction_array[index]->min = MIN_PERSONAL_FACTION;
faction_array[index]->max = MAX_PERSONAL_FACTION;
+
+ faction_ids.push_back(index);
+ }
- // Load in the mimimum and maximum faction that can be earned for this faction
- query = StringFormat("SELECT `min` , `max` FROM `faction_base_data` WHERE client_faction_id = %u", index);
- auto baseResults = QueryDatabase(query);
- if (!baseResults.Success() || baseResults.RowCount() == 0) {
- Log(Logs::General, Logs::General, "Faction %d has no base data", (int)index);
- }
- else {
- for (auto modRow = baseResults.begin(); modRow != baseResults.end(); ++modRow) {
- faction_array[index]->min = atoi(modRow[0]);
- faction_array[index]->max = atoi(modRow[1]);
- Log(Logs::General, Logs::None, "Min(%d), Max(%d) for faction (%u)",faction_array[index]->min, faction_array[index]->max, index);
+ Log(Logs::General, Logs::Status, "%u Faction%s loaded...", faction_ids.size(), (faction_ids.size() == 1 ? "" : "s"));
+
+ // this can be removed once the 'io_work' branch has been merged
+ std::vector faction_id_strings;
+ for (auto id : faction_ids) {
+ faction_id_strings.push_back(fmt::format("'{}'", id));
+ }
+ const std::string faction_id_criteria(implode(",", faction_id_strings));
+
+ // code to activate (note above)
+ //const std::string faction_id_criteria(implode(",", std::pair('\'', '\''), faction_ids));
+
+ // load faction mins/maxes
+ query = fmt::format("SELECT `client_faction_id`, `min`, `max` FROM `faction_base_data` WHERE `client_faction_id` IN ({})", faction_id_criteria);
+
+ auto base_results = QueryDatabase(query);
+ if (base_results.Success()) {
+
+ for (auto br_row : base_results) {
+
+ uint32 index = atoul(br_row[0]);
+ if (index > max_faction) {
+ Log(Logs::General, Logs::Error, "Faction '%u' is out-of-bounds for faction array size in Base adjustment!", index);
+ continue;
}
+
+ if (faction_array[index] == nullptr) {
+ Log(Logs::General, Logs::Error, "Faction '%u' does not exist for Base adjustment!", index);
+ continue;
+ }
+
+ faction_array[index]->min = atoi(br_row[1]);
+ faction_array[index]->max = atoi(br_row[2]);
}
- // Load in modifiers to the faction based on characters race, class and diety.
- query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index);
- auto modResults = QueryDatabase(query);
- if (!modResults.Success())
- continue;
+ Log(Logs::General, Logs::Status, "%u Faction Base%s loaded...", base_results.RowCount(), (base_results.RowCount() == 1 ? "" : "s"));
+ }
+ else {
+ Log(Logs::General, Logs::Status, "Unable to load Faction Base data...");
+ }
+
+ // load race, class and diety modifiers
+ query = fmt::format("SELECT `faction_id`, `mod`, `mod_name` FROM `faction_list_mod` WHERE `faction_id` IN ({})", faction_id_criteria);
- for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow) {
- faction_array[index]->mods[modRow[1]] = atoi(modRow[0]);
+ auto modifier_results = QueryDatabase(query);
+ if (modifier_results.Success()) {
+
+ for (auto mr_row : modifier_results) {
+
+ uint32 index = atoul(mr_row[0]);
+ if (index > max_faction) {
+ Log(Logs::General, Logs::Error, "Faction '%u' is out-of-bounds for faction array size in Modifier adjustment!", index);
+ continue;
+ }
+
+ if (faction_array[index] == nullptr) {
+ Log(Logs::General, Logs::Error, "Faction '%u' does not exist for Modifier adjustment!", index);
+ continue;
+ }
+
+ faction_array[index]->mods[mr_row[2]] = atoi(mr_row[1]);
}
- }
+
+ Log(Logs::General, Logs::Status, "%u Faction Modifier%s loaded...", modifier_results.RowCount(), (modifier_results.RowCount() == 1 ? "" : "s"));
+ }
+ else {
+ Log(Logs::General, Logs::Status, "Unable to load Faction Modifier data...");
+ }
return true;
}