diff --git a/changelog.txt b/changelog.txt index 6ee721e64..913204cab 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 04/05/2016 == +Uleat: Moved database query code out of bot.cpp and into the new BotDatabase class + == 03/25/2016 == Uleat: Fix for heal rotation 'Stack Overflow' error Kayen: Defensive procs will now only proc once per attack round (instead of every attack chance). diff --git a/common/version.h b/common/version.h index 64c063917..248ce3beb 100644 --- a/common/version.h +++ b/common/version.h @@ -32,7 +32,7 @@ #define CURRENT_BINARY_DATABASE_VERSION 9096 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9002 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9003 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 12e5996f2..f25bac0de 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -1,6 +1,7 @@ 9000|2015_09_30_bots.sql|SHOW TABLES LIKE 'bot_data'|empty| 9001|2016_03_24_bots_command_settings.sql|SHOW TABLES LIKE 'bot_command_settings'|empty| 9002|2016_03_24_bots_command_rules.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE 'Bots:CommandSpellRank'|empty| +9003|2016_04_05_bots_pet_spell_id_field.sql|EXPLAIN `bot_pets`|contains|pet_id # 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/2016_04_05_bots_pet_spell_id_field.sql b/utils/sql/git/bots/required/2016_04_05_bots_pet_spell_id_field.sql new file mode 100644 index 000000000..d2d930160 --- /dev/null +++ b/utils/sql/git/bots/required/2016_04_05_bots_pet_spell_id_field.sql @@ -0,0 +1 @@ +ALTER TABLE `bot_pets` CHANGE COLUMN `pet_id` `spell_id` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `pets_index`; diff --git a/zone/bot.cpp b/zone/bot.cpp index 6cb27f8e7..21cdd6d46 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -94,14 +94,18 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm strcpy(this->name, this->GetCleanName()); memset(&m_Light, 0, sizeof(LightProfile_Struct)); + memset(&_botInspectMessage, 0, sizeof(InspectMessage_Struct)); } // This constructor is used when the bot is loaded out of the database -Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, nullptr, glm::vec4(), 0, false), rest_timer(1) { +Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, nullptr, glm::vec4(), 0, false), rest_timer(1) +{ this->_botOwnerCharacterID = botOwnerCharacterID; if(this->_botOwnerCharacterID > 0) this->SetBotOwner(entity_list.GetClientByCharID(this->_botOwnerCharacterID)); + auto bot_owner = GetBotOwner(); + _guildRank = 0; _guildId = 0; _lastTotalPlayTime = totalPlayTime; @@ -138,7 +142,13 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to SetPetChooser(false); SetRangerAutoWeaponSelect(false); SetHasBeenSummoned(false); - LoadStance(); + + bool stance_flag = false; + if (!botdb.LoadStance(this, stance_flag) && bot_owner) + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::LoadStance(), GetCleanName()); + if (!stance_flag && bot_owner) + bot_owner->Message(13, "Could not locate stance for '%s'", GetCleanName()); + SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive)); SetPauseAI(false); @@ -146,22 +156,36 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to rest_timer.Disable(); SetFollowDistance(BOT_DEFAULT_FOLLOW_DISTANCE); strcpy(this->name, this->GetCleanName()); - botdb.GetInspectMessage(this->GetBotID(), &_botInspectMessage); - LoadGuildMembership(&_guildId, &_guildRank, &_guildName); - std::string TempErrorMessage; - EquipBot(&TempErrorMessage); - if(!TempErrorMessage.empty()) { - if(GetBotOwner()) - GetBotOwner()->Message(13, TempErrorMessage.c_str()); + + memset(&_botInspectMessage, 0, sizeof(InspectMessage_Struct)); + if (!botdb.LoadInspectMessage(GetBotID(), _botInspectMessage) && bot_owner) + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::LoadInspectMessage(), GetCleanName()); + + if (!botdb.LoadGuildMembership(GetBotID(), _guildId, _guildRank, _guildName) && bot_owner) + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::LoadGuildMembership(), GetCleanName()); + + std::string error_message; + + EquipBot(&error_message); + if(!error_message.empty()) { + if(bot_owner) + bot_owner->Message(13, error_message.c_str()); + error_message.clear(); } for (int i = 0; i < MaxTimer; i++) timers[i] = 0; GenerateBaseStats(); - LoadTimers(); + + if (!botdb.LoadTimers(this) && bot_owner) + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::LoadTimers(), GetCleanName()); + LoadAAs(); - LoadBuffs(); + + if (!botdb.LoadBuffs(this) && bot_owner) + bot_owner->Message(13, "&s for '%s'", BotDatabase::fail::LoadBuffs(), GetCleanName()); + CalcBotStats(false); hp_regen = CalcHPRegen(); mana_regen = CalcManaRegen(); @@ -1530,272 +1554,89 @@ bool Bot::IsValidName(std::string& name) return true; } -bool Bot::IsBotNameAvailable(const char *botName, std::string* errorMessage) { - if (botName == "" || strlen(botName) > 15 || !database.CheckNameFilter(botName) || !database.CheckUsedName(botName)) - return false; - - std::string query = StringFormat("SELECT `id` FROM `vw_bot_character_mobs` WHERE `name` LIKE '%s'", botName); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return false; - } - - if (results.RowCount()) - return false; - - return true; //We made it with a valid name! -} - bool Bot::Save() { - if(this->GetBotID() == 0) { - // New bot record - std::string query = StringFormat( - "INSERT INTO `bot_data` (" - " `owner_id`," - " `spells_id`," - " `name`," - " `last_name`," - " `zone_id`," - " `gender`," - " `race`," - " `class`," - " `level`," - " `creation_day`," - " `last_spawn`," - " `time_spawned`," - " `size`," - " `face`," - " `hair_color`," - " `hair_style`," - " `beard`," - " `beard_color`," - " `eye_color_1`," - " `eye_color_2`," - " `drakkin_heritage`," - " `drakkin_tattoo`," - " `drakkin_details`," - " `ac`," - " `atk`," - " `hp`," - " `mana`," - " `str`," - " `sta`," - " `cha`," - " `dex`," - " `int`," - " `agi`," - " `wis`," - " `fire`," - " `cold`," - " `magic`," - " `poison`," - " `disease`," - " `corruption`," - " `show_helm`," - " `follow_distance`" - ")" - " VALUES (" - "'%u'," /*owner_id*/ - " '%u'," /*spells_id*/ - " '%s'," /*name*/ - " '%s'," /*last_name*/ - " '%i'," /*zone_id*/ - " '%i'," /*gender*/ - " '%i'," /*race*/ - " '%i'," /*class*/ - " '%u'," /*level*/ - " UNIX_TIMESTAMP(),"/*creation_day*/ - " UNIX_TIMESTAMP(),"/*last_spawn*/ - " 0," /*time_spawned*/ - " '%f'," /*size*/ - " '%i'," /*face*/ - " '%i'," /*hair_color*/ - " '%i'," /*hair_style*/ - " '%i'," /*beard*/ - " '%i'," /*beard_color*/ - " '%i'," /*eye_color_1*/ - " '%i'," /*eye_color_2*/ - " '%i'," /*drakkin_heritage*/ - " '%i'," /*drakkin_tattoo*/ - " '%i'," /*drakkin_details*/ - " '%i'," /*ac*/ - " '%i'," /*atk*/ - " '%i'," /*hp*/ - " '%i'," /*mana*/ - " '%i'," /*str*/ - " '%i'," /*sta*/ - " '%i'," /*cha*/ - " '%i'," /*dex*/ - " '%i'," /*int*/ - " '%i'," /*agi*/ - " '%i'," /*wis*/ - " '%i'," /*fire*/ - " '%i'," /*cold*/ - " '%i'," /*magic*/ - " '%i'," /*poison*/ - " '%i'," /*disease*/ - " '%i'," /*corruption*/ - " '1'," /*show_helm*/ - " '%i'" /*follow_distance*/ - ")", - this->_botOwnerCharacterID, - this->GetBotSpellID(), - this->GetCleanName(), - this->lastname, - _lastZoneId, - GetGender(), - GetRace(), - GetClass(), - this->GetLevel(), - GetSize(), - this->GetLuclinFace(), - GetHairColor(), - this->GetHairStyle(), - this->GetBeard(), - this->GetBeardColor(), - this->GetEyeColor1(), - this->GetEyeColor2(), - this->GetDrakkinHeritage(), - this->GetDrakkinTattoo(), - this->GetDrakkinDetails(), - GetAC(), - GetATK(), - GetHP(), - GetMana(), - GetSTR(), - GetSTA(), - GetCHA(), - GetDEX(), - GetINT(), - GetAGI(), - GetWIS(), - GetFR(), - GetCR(), - GetMR(), - GetPR(), - GetDR(), - GetCorrup(), - BOT_DEFAULT_FOLLOW_DISTANCE - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - auto botOwner = GetBotOwner(); - if (botOwner) - botOwner->Message(13, results.ErrorMessage().c_str()); - + auto bot_owner = GetBotOwner(); + if (!bot_owner) + return false; + + std::string error_message; + + if(!GetBotID()) { // New bot record + uint32 bot_id = 0; + if (!botdb.SaveNewBot(this, bot_id) || !bot_id) { + bot_owner->Message(13, "%s '%s'", BotDatabase::fail::SaveNewBot(), GetCleanName()); + return false; + } + SetBotID(bot_id); + } + else { // Update existing bot record + if (!botdb.SaveBot(this)) { + bot_owner->Message(13, "%s '%s'", BotDatabase::fail::SaveBot(), GetCleanName()); return false; } - - SetBotID(results.LastInsertedID()); - SaveBuffs(); - SavePet(); - SaveStance(); - SaveTimers(); - return true; } - // Update existing bot record - std::string query = StringFormat( - "UPDATE `bot_data`" - " SET" - " `owner_id` = '%u'," - " `spells_id` = '%u'," - " `name` = '%s'," - " `last_name` = '%s'," - " `zone_id` = '%i'," - " `gender` = '%i'," - " `race` = '%i'," - " `class` = '%i'," - " `level` = '%u'," - " `last_spawn` = UNIX_TIMESTAMP()," - " `time_spawned` = '%u'," - " `size` = '%f'," - " `face` = '%i'," - " `hair_color` = '%i'," - " `hair_style` = '%i'," - " `beard` = '%i'," - " `beard_color` = '%i'," - " `eye_color_1` = '%i'," - " `eye_color_2` = '%i'," - " `drakkin_heritage` = '%i'," - " `drakkin_tattoo` = '%i'," - " `drakkin_details` = '%i'," - " `ac` = '%i'," - " `atk` = '%i'," - " `hp` = '%i'," - " `mana` = '%i'," - " `str` = '%i'," - " `sta` = '%i'," - " `cha` = '%i'," - " `dex` = '%i'," - " `int` = '%i'," - " `agi` = '%i'," - " `wis` = '%i'," - " `fire` = '%i'," - " `cold` = '%i'," - " `magic` = '%i'," - " `poison` = '%i'," - " `disease` = '%i'," - " `corruption` = '%i'," - " `show_helm` = '%i'," - " `follow_distance` = '%i'" - " WHERE `bot_id` = '%u'", - _botOwnerCharacterID, - this->GetBotSpellID(), - this->GetCleanName(), - this->lastname, - _lastZoneId, - _baseGender, - _baseRace, - this->GetClass(), - this->GetLevel(), - GetTotalPlayTime(), - GetSize(), - this->GetLuclinFace(), - GetHairColor(), - this->GetHairStyle(), - this->GetBeard(), - this->GetBeardColor(), - this->GetEyeColor1(), - this->GetEyeColor2(), - this->GetDrakkinHeritage(), - GetDrakkinTattoo(), - GetDrakkinDetails(), - _baseAC, - _baseATK, - GetHP(), - GetMana(), - _baseSTR, - _baseSTA, - _baseCHA, - _baseDEX, - _baseINT, - _baseAGI, - _baseWIS, - _baseFR, - _baseCR, - _baseMR, - _basePR, - _baseDR, - _baseCorrup, - (GetShowHelm() ? 1 : 0), - GetFollowDistance(), - GetBotID() - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - auto botOwner = GetBotOwner(); - if (botOwner) - botOwner->Message(13, results.ErrorMessage().c_str()); - + // All of these continue to process if any fail + if (!botdb.SaveBuffs(this)) + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::SaveBuffs(), GetCleanName()); + if (!botdb.SaveTimers(this)) + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::SaveTimers(), GetCleanName()); + if (!botdb.SaveStance(this)) + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::SaveStance(), GetCleanName()); + + if (!SavePet()) + bot_owner->Message(13, "Failed to save pet for '%s'", GetCleanName()); + + return true; +} + +bool Bot::DeleteBot() +{ + auto bot_owner = GetBotOwner(); + if (!bot_owner) + return false; + + if (!DeletePet()) { + bot_owner->Message(13, "Failed to delete pet for '%s'", GetCleanName()); return false; } - SaveBuffs(); - SavePet(); - SaveStance(); - SaveTimers(); + + if (GetGroup()) + RemoveBotFromGroup(this, GetGroup()); + + std::string error_message; + + if (!botdb.RemoveMemberFromBotGroup(GetBotID())) { + bot_owner->Message(13, "%s - '%s'", BotDatabase::fail::RemoveMemberFromBotGroup(), GetCleanName()); + return false; + } + + if (!botdb.DeleteItems(GetBotID())) { + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::DeleteItems(), GetCleanName()); + return false; + } + + if (!botdb.DeleteTimers(GetBotID())) { + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::DeleteTimers(), GetCleanName()); + return false; + } + + if (!botdb.DeleteBuffs(GetBotID())) { + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::DeleteBuffs(), GetCleanName()); + return false; + } + + if (!botdb.DeleteStance(GetBotID())) { + bot_owner->Message(13, "%s for '%s'", BotDatabase::fail::DeleteStance(), GetCleanName()); + return false; + } + + if (!botdb.DeleteBot(GetBotID())) { + bot_owner->Message(13, "%s '%s'", BotDatabase::fail::DeleteBot(), GetCleanName()); + return false; + } + return true; } @@ -1810,461 +1651,139 @@ uint32 Bot::GetTotalPlayTime() { return Result; } -void Bot::SaveBuffs() +bool Bot::LoadPet() { - // Remove any existing buff saves - std::string query = StringFormat("DELETE FROM `bot_buffs` WHERE `bot_id` = %u", GetBotID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) - return; + if (GetPet()) + return true; + + auto bot_owner = GetBotOwner(); + if (!bot_owner) + return false; - for (int buffIndex = 0; buffIndex < BUFF_COUNT; buffIndex++) { - if (buffs[buffIndex].spellid <= 0 || buffs[buffIndex].spellid == SPELL_UNKNOWN) - continue; - - int isPersistent = buffs[buffIndex].persistant_buff ? 1 : 0; - query = StringFormat( - "INSERT INTO `bot_buffs` (" - "`bot_id`," - " `spell_id`," - " `caster_level`," - " `duration_formula`," - " `tics_remaining`," - " `poison_counters`," - " `disease_counters`," - " `curse_counters`," - " `corruption_counters`," - " `numhits`," - " `melee_rune`," - " `magic_rune`," - " `dot_rune`," - " `persistent`," - " `caston_x`," - " `caston_y`," - " `caston_z`," - " `extra_di_chance`" - ")" - " VALUES (" - "%u," /*bot_id*/ - " %u," /*spell_id*/ - " %u," /*caster_level*/ - " %u," /*duration_formula*/ - " %u," /*tics_remaining*/ - " %u," /*poison_counters*/ - " %u," /*disease_counters*/ - " %u," /*curse_counters*/ - " %u," /*corruption_counters*/ - " %u," /*numhits*/ - " %u," /*melee_rune*/ - " %u," /*magic_rune*/ - " %u," /*dot_rune*/ - " %u," /*persistent*/ - " %i," /*caston_x*/ - " %i," /*caston_y*/ - " %i," /*caston_z*/ - " %i" /*extra_di_chance*/ - ")", - GetBotID(), - buffs[buffIndex].spellid, - buffs[buffIndex].casterlevel, - spells[buffs[buffIndex].spellid].buffdurationformula, - buffs[buffIndex].ticsremaining, - CalculatePoisonCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, - CalculateDiseaseCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, - CalculateCurseCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, - CalculateCorruptionCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, - buffs[buffIndex].numhits, - buffs[buffIndex].melee_rune, - buffs[buffIndex].magic_rune, - buffs[buffIndex].dot_rune, - isPersistent, - buffs[buffIndex].caston_x, - buffs[buffIndex].caston_y, - buffs[buffIndex].caston_z, - buffs[buffIndex].ExtraDIChance - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) - return; + std::string error_message; + + uint32 pet_index = 0; + if (!botdb.LoadPetIndex(GetBotID(), pet_index)) { + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetIndex(), GetCleanName()); + return false; } + if (!pet_index) + return true; + + uint32 saved_pet_spell_id = 0; + if (!botdb.LoadPetSpellID(GetBotID(), saved_pet_spell_id)) { + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetSpellID(), GetCleanName()); + } + if (!saved_pet_spell_id || saved_pet_spell_id > SPDAT_RECORDS) { + bot_owner->Message(13, "Invalid spell id for %s's pet", GetCleanName()); + DeletePet(); + return false; + } + + std::string pet_name; + uint32 pet_mana = 0; + uint32 pet_hp = 0; + uint32 pet_spell_id = 0; + + if (!botdb.LoadPetStats(GetBotID(), pet_name, pet_mana, pet_hp, pet_spell_id)) { + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetStats(), GetCleanName()); + return false; + } + + MakePet(pet_spell_id, spells[pet_spell_id].teleport_zone, pet_name.c_str()); + if (!GetPet() || !GetPet()->IsNPC()) { + DeletePet(); + return false; + } + + NPC *pet_inst = GetPet()->CastToNPC(); + + SpellBuff_Struct pet_buffs[BUFF_COUNT]; + memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * BUFF_COUNT)); + if (!botdb.LoadPetBuffs(GetBotID(), pet_buffs)) + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetBuffs(), GetCleanName()); + + uint32 pet_items[EmuConstants::EQUIPMENT_SIZE]; + memset(pet_items, 0, (sizeof(uint32) * EmuConstants::EQUIPMENT_SIZE)); + if (!botdb.LoadPetItems(GetBotID(), pet_items)) + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetItems(), GetCleanName()); + + pet_inst->SetPetState(pet_buffs, pet_items); + pet_inst->CalcBonuses(); + pet_inst->SetHP(pet_hp); + pet_inst->SetMana(pet_mana); + + return true; } -void Bot::LoadBuffs() +bool Bot::SavePet() { - std::string query = StringFormat( - "SELECT" - " `spell_id`," - " `caster_level`," - " `duration_formula`," - " `tics_remaining`," - " `poison_counters`," - " `disease_counters`," - " `curse_counters`," - " `corruption_counters`," - " `numhits`," - " `melee_rune`," - " `magic_rune`," - " `dot_rune`," - " `persistent`," - " `caston_x`," - " `caston_y`," - " `caston_z`," - " `extra_di_chance`" - " FROM `bot_buffs`" - " WHERE `bot_id` = '%u'", - GetBotID() - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) - return; + if (!GetPet() /*|| dead*/) + return true; - int buffCount = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - if(buffCount == BUFF_COUNT) - break; - - buffs[buffCount].spellid = atoi(row[0]); - buffs[buffCount].casterlevel = atoi(row[1]); - //row[2] (duration_formula) can probably be removed - buffs[buffCount].ticsremaining = atoi(row[3]); + NPC *pet_inst = GetPet()->CastToNPC(); + if (pet_inst->IsFamiliar() || !pet_inst->GetPetSpellID() || pet_inst->GetPetSpellID() > SPDAT_RECORDS) + return false; - if(CalculatePoisonCounters(buffs[buffCount].spellid) > 0) - buffs[buffCount].counters = atoi(row[4]); - else if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0) - buffs[buffCount].counters = atoi(row[5]); - else if(CalculateCurseCounters(buffs[buffCount].spellid) > 0) - buffs[buffCount].counters = atoi(row[6]); - else if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0) - buffs[buffCount].counters = atoi(row[7]); - - buffs[buffCount].numhits = atoi(row[8]); - buffs[buffCount].melee_rune = atoi(row[9]); - buffs[buffCount].magic_rune = atoi(row[10]); - buffs[buffCount].dot_rune = atoi(row[11]); - buffs[buffCount].persistant_buff = atoi(row[12]) ? true : false; - buffs[buffCount].caston_x = atoi(row[13]); - buffs[buffCount].caston_y = atoi(row[14]); - buffs[buffCount].caston_z = atoi(row[15]); - buffs[buffCount].ExtraDIChance = atoi(row[16]); - buffs[buffCount].casterid = 0; - ++buffCount; - } - query = StringFormat("DELETE FROM `bot_buffs` WHERE `bot_id` = %u", GetBotID()); - results = database.QueryDatabase(query); -} + auto bot_owner = GetBotOwner(); + if (!bot_owner) + return false; -uint32 Bot::GetPetSaveId() -{ - std::string query = StringFormat("SELECT `pets_index` FROM `bot_pets` WHERE `bot_id` = %u", GetBotID()); - auto results = database.QueryDatabase(query); - if(!results.Success() || results.RowCount() == 0) - return 0; + char* pet_name = new char[64]; + SpellBuff_Struct pet_buffs[BUFF_COUNT]; + uint32 pet_items[EmuConstants::EQUIPMENT_SIZE]; - auto row = results.begin(); - return atoi(row[0]); -} - -void Bot::LoadPet() { - uint32 PetSaveId = GetPetSaveId(); - if(PetSaveId > 0 && !GetPet() && PetSaveId <= SPDAT_RECORDS) { - std::string petName; - uint32 petMana = 0; - uint32 petHitPoints = 0; - uint32 botPetId = 0; - LoadPetStats(&petName, &petMana, &petHitPoints, &botPetId, PetSaveId); - MakePet(botPetId, spells[botPetId].teleport_zone, petName.c_str()); - if(GetPet() && GetPet()->IsNPC()) { - NPC *pet = GetPet()->CastToNPC(); - SpellBuff_Struct petBuffs[BUFF_COUNT]; - memset(petBuffs, 0, sizeof(petBuffs)); - uint32 petItems[EmuConstants::EQUIPMENT_SIZE]; - - LoadPetBuffs(petBuffs, PetSaveId); - LoadPetItems(petItems, PetSaveId); - - pet->SetPetState(petBuffs, petItems); - pet->CalcBonuses(); - pet->SetHP(petHitPoints); - pet->SetMana(petMana); - } - DeletePetStats(PetSaveId); - } -} - -void Bot::LoadPetStats(std::string* petName, uint32* petMana, uint32* petHitPoints, uint32* botPetId, uint32 botPetSaveId) -{ - if(botPetSaveId == 0) - return; - - std::string query = StringFormat("SELECT `pet_id`, `name`, `mana`, `hp` FROM `bot_pets` WHERE `pets_index` = %u", botPetSaveId); - auto results = database.QueryDatabase(query); - if(!results.Success() || results.RowCount() == 0) - return; - - auto row = results.begin(); - *botPetId = atoi(row[0]); - *petName = std::string(row[1]); - *petMana = atoi(row[2]); - *petHitPoints = atoi(row[3]); -} - -void Bot::LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { - if(!petBuffs || botPetSaveId == 0) - return; - - std::string query = StringFormat("SELECT `spell_id`, `caster_level`, `duration` FROM `bot_pet_buffs` WHERE `pets_index` = %u;", botPetSaveId); - auto results = database.QueryDatabase(query); - if(!results.Success()) - return; - - int buffIndex = 0; - for (auto row = results.begin();row != results.end(); ++row) { - if(buffIndex == BUFF_COUNT) - break; - - petBuffs[buffIndex].spellid = atoi(row[0]); - petBuffs[buffIndex].level = atoi(row[1]); - petBuffs[buffIndex].duration = atoi(row[2]); - //Work around for loading the counters and setting them back to max. Need entry in DB for saved counters - if(CalculatePoisonCounters(petBuffs[buffIndex].spellid) > 0) - petBuffs[buffIndex].counters = CalculatePoisonCounters(petBuffs[buffIndex].spellid); - else if(CalculateDiseaseCounters(petBuffs[buffIndex].spellid) > 0) - petBuffs[buffIndex].counters = CalculateDiseaseCounters(petBuffs[buffIndex].spellid); - else if(CalculateCurseCounters(petBuffs[buffIndex].spellid) > 0) - petBuffs[buffIndex].counters = CalculateCurseCounters(petBuffs[buffIndex].spellid); - else if(CalculateCorruptionCounters(petBuffs[buffIndex].spellid) > 0) - petBuffs[buffIndex].counters = CalculateCorruptionCounters(petBuffs[buffIndex].spellid); - - buffIndex++; - } - query = StringFormat("DELETE FROM `bot_pet_buffs` WHERE `pets_index` = %u;", botPetSaveId); - results = database.QueryDatabase(query); -} - -void Bot::LoadPetItems(uint32* petItems, uint32 botPetSaveId) { - if(!petItems || botPetSaveId == 0) - return; - - std::string query = StringFormat("SELECT `item_id` FROM `bot_pet_inventories` WHERE `pets_index` = %u;", botPetSaveId); - auto results = database.QueryDatabase(query); - if(!results.Success()) - return; - - int itemIndex = 0; - for(auto row = results.begin(); row != results.end(); ++row) { - if(itemIndex == EmuConstants::EQUIPMENT_SIZE) - break; - - petItems[itemIndex] = atoi(row[0]); - itemIndex++; - } - query = StringFormat("DELETE FROM `bot_pet_inventories` WHERE `pets_index` = %u", botPetSaveId); - results = database.QueryDatabase(query); -} - -void Bot::SavePet() { - if(GetPet() && !GetPet()->IsFamiliar() && GetPet()->CastToNPC()->GetPetSpellID() /*&& !dead*/) { - NPC *pet = GetPet()->CastToNPC(); - uint16 petMana = pet->GetMana(); - uint16 petHitPoints = pet->GetHP(); - uint32 botPetId = pet->CastToNPC()->GetPetSpellID(); - char* tempPetName = new char[64]; - SpellBuff_Struct petBuffs[BUFF_COUNT]; - uint32 petItems[EmuConstants::EQUIPMENT_SIZE]; - pet->GetPetState(petBuffs, petItems, tempPetName); - uint32 existingBotPetSaveId = GetPetSaveId(); - if(existingBotPetSaveId > 0) { - // Remove any existing pet buffs - DeletePetBuffs(existingBotPetSaveId); - // Remove any existing pet items - DeletePetItems(existingBotPetSaveId); - } - // Save pet stats and get a new bot pet save id - uint32 botPetSaveId = SavePetStats(std::string(tempPetName), petMana, petHitPoints, botPetId); - // Save pet buffs - SavePetBuffs(petBuffs, botPetSaveId); - // Save pet items - SavePetItems(petItems, botPetSaveId); - if(tempPetName) - safe_delete_array(tempPetName); - } -} - -uint32 Bot::SavePetStats(std::string petName, uint32 petMana, uint32 petHitPoints, uint32 botPetId) -{ - std::string query = StringFormat( - "REPLACE INTO `bot_pets`" - " SET" - " `pet_id` = %u," - " `bot_id` = %u," - " `name` = '%s'," - " `mana` = %u," - " `hp` = %u", - botPetId, - GetBotID(), - petName.c_str(), - petMana, - petHitPoints - ); - auto results = database.QueryDatabase(query); - return results.LastInsertedID(); -} - -void Bot::SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) -{ - if(!petBuffs || botPetSaveId == 0) - return; + memset(pet_name, 0, 64); + memset(pet_buffs, 0, (sizeof(SpellBuff_Struct) * BUFF_COUNT)); + memset(pet_items, 0, (sizeof(uint32) * EmuConstants::EQUIPMENT_SIZE)); - int buffIndex = 0; - while(buffIndex < BUFF_COUNT) { - if(petBuffs[buffIndex].spellid > 0 && petBuffs[buffIndex].spellid != SPELL_UNKNOWN) { - std::string query = StringFormat( - "INSERT INTO `bot_pet_buffs` (" - "`pets_index`," - " `spell_id`," - " `caster_level`," - " `duration`" - ")" - " VALUES (" - "%u," - " %u," - " %u," - " %u" - ")", - botPetSaveId, - petBuffs[buffIndex].spellid, - petBuffs[buffIndex].level, - petBuffs[buffIndex].duration - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) - break; - } - buffIndex++; - } -} - -void Bot::SavePetItems(uint32* petItems, uint32 botPetSaveId) { - if(!petItems || botPetSaveId == 0) - return; - - for (int itemIndex = 0; itemIndex < EmuConstants::EQUIPMENT_SIZE; itemIndex++) { - if(petItems[itemIndex] == 0) - continue; - - std::string query = StringFormat("INSERT INTO `bot_pet_inventories` (`pets_index`, `item_id`) VALUES (%u, %u)", botPetSaveId, petItems[itemIndex]); - auto results = database.QueryDatabase(query); - if(!results.Success()) - break; - } -} - -void Bot::DeletePetBuffs(uint32 botPetSaveId) { - if(botPetSaveId == 0) - return; - - std::string query = StringFormat("DELETE FROM `bot_pet_buffs` WHERE `pets_index` = %u", botPetSaveId); - auto results = database.QueryDatabase(query); -} - -void Bot::DeletePetItems(uint32 botPetSaveId) { - if(botPetSaveId == 0) - return; - - std::string query = StringFormat("DELETE FROM `bot_pet_inventories` WHERE `pets_index` = %u", botPetSaveId); - auto results = database.QueryDatabase(query); -} - -void Bot::DeletePetStats(uint32 botPetSaveId) { - if(botPetSaveId == 0) - return; - - std::string query = StringFormat("DELETE FROM `bot_pets` WHERE `pets_index` = %u", botPetSaveId); - auto results = database.QueryDatabase(query); -} - -void Bot::LoadStance() -{ - std::string query = StringFormat("SELECT `stance_id` FROM `bot_stances` WHERE `bot_id` = %u", GetBotID()); - auto results = database.QueryDatabase(query); - if(!results.Success() || results.RowCount() == 0) { - Log.Out(Logs::General, Logs::Error, "Error in Bot::LoadStance()"); - SetDefaultBotStance(); - return; - } - auto row = results.begin(); - SetBotStance((BotStanceType)atoi(row[0])); -} - -void Bot::SaveStance() { - if(_baseBotStance == _botStance) - return; - - std::string query = StringFormat("REPLACE INTO `bot_stances` (`bot_id`, `stance_id`) VALUES (%u, %u)", GetBotID(), GetBotStance()); - auto results = database.QueryDatabase(query); - if(!results.Success()) - Log.Out(Logs::General, Logs::Error, "Error in Bot::SaveStance()"); -} - -void Bot::LoadTimers() -{ - std::string query = StringFormat( - "SELECT" - " IfNull(bt.`timer_id`, 0) As timer_id," - " IfNull(bt.`timer_value`, 0) As timer_value," - " IfNull(MAX(sn.`recast_time`), 0) AS MaxTimer" - " FROM `bot_timers` bt, `spells_new` sn" - " WHERE bt.`bot_id` = %u AND sn.`EndurTimerIndex` = (" - "SELECT case" - " WHEN timer_id > %i THEN timer_id - %i" - " ELSE timer_id END AS timer_id" - " FROM `bot_timers` WHERE `timer_id` = bt.`timer_id` AND `bot_id` = bt.`bot_id`" // double-check validity - ")" - " AND sn.`classes%i` <= %i", - GetBotID(), - (DisciplineReuseStart - 1), - (DisciplineReuseStart - 1), - GetClass(), - GetLevel() - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - Log.Out(Logs::General, Logs::Error, "Error in Bot::LoadTimers()"); - return; - } + pet_inst->GetPetState(pet_buffs, pet_items, pet_name); - int timerID = 0; - uint32 value = 0; - uint32 maxValue = 0; - for (auto row = results.begin(); row != results.end(); ++row) { - timerID = atoi(row[0]) - 1; - value = atoi(row[1]); - maxValue = atoi(row[2]); - if(timerID >= 0 && timerID < MaxTimer && value < (Timer::GetCurrentTime() + maxValue)) - timers[timerID] = value; + std::string error_message; + + if (!botdb.SavePetStats(GetBotID(), pet_name, pet_inst->GetMana(), pet_inst->GetHP(), pet_inst->GetPetSpellID())) { + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::SavePetStats(), GetCleanName()); + safe_delete_array(pet_name); + return false; } + safe_delete_array(pet_name); + + if (!botdb.SavePetBuffs(GetBotID(), pet_buffs)) + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::SavePetBuffs(), GetCleanName()); + if (!botdb.SavePetItems(GetBotID(), pet_items)) + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::SavePetItems(), GetCleanName()); + + return true; } -void Bot::SaveTimers() { - bool hadError = false; - std::string query = StringFormat("DELETE FROM `bot_timers` WHERE `bot_id` = %u", GetBotID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) - hadError = true; +bool Bot::DeletePet() +{ + auto bot_owner = GetBotOwner(); + if (!bot_owner) + return false; + + std::string error_message; - for(int timerIndex = 0; timerIndex < MaxTimer; timerIndex++) { - if(timers[timerIndex] <= Timer::GetCurrentTime()) - continue; - - query = StringFormat("REPLACE INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES (%u, %u, %u)", GetBotID(), timerIndex + 1, timers[timerIndex]); - results = database.QueryDatabase(query); - if(!results.Success()) - hadError = true; + if (!botdb.DeletePetItems(GetBotID())) { + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::DeletePetItems(), GetCleanName()); + return false; + } + if (!botdb.DeletePetBuffs(GetBotID())) { + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::DeletePetBuffs(), GetCleanName()); + return false; + } + if (!botdb.DeletePetStats(GetBotID())) { + bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::DeletePetStats(), GetCleanName()); + return false; } - if(hadError) - Log.Out(Logs::General, Logs::Error, "Error in Bot::SaveTimers()"); + if (!GetPet() || !GetPet()->IsNPC()) + return true; + NPC* pet_inst = GetPet()->CastToNPC(); + pet_inst->SetOwnerID(0); + + return true; } bool Bot::Process() { @@ -3299,44 +2818,7 @@ void Bot::Depop() { NPC::Depop(false); } -bool Bot::DeleteBot(std::string* errorMessage) { - bool hadError = false; - if(this->GetBotID() == 0) - return false; - - // TODO: These queries need to be ran together as a transaction.. ie, if one or more fail then they all will fail to commit to the database. - std::string query = StringFormat("DELETE FROM `bot_inventories` WHERE `bot_id` = '%u'", this->GetBotID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - hadError = true; - } - - query = StringFormat("DELETE FROM `bot_buffs` WHERE `bot_id` = '%u'", this->GetBotID()); - results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - hadError = true; - } - - query = StringFormat("DELETE FROM `bot_stances` WHERE `bot_id` = '%u'", this->GetBotID()); - results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - hadError = true; - } - - query = StringFormat("DELETE FROM `bot_data` WHERE `bot_id` = '%u'", this->GetBotID()); - results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - hadError = true; - } - - return !hadError; -} - -void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { +void Bot::Spawn(Client* botCharacterOwner) { if(GetBotID() > 0 && _botOwnerCharacterID > 0 && botCharacterOwner && botCharacterOwner->CharacterID() == _botOwnerCharacterID) { // Rename the bot name to make sure that Mob::GetName() matches Mob::GetCleanName() so we dont have a bot named "Jesuschrist001" strcpy(name, GetCleanName()); @@ -3381,236 +2863,46 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { } } -// Saves the specified item as an inventory record in the database for this bot. -void Bot::SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, std::string *errorMessage) -{ - uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN || itemID <= NO_ITEM) - return; - - if (inst && inst->IsType(ItemClassCommon)) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; ++i) { - ItemInst* auginst = inst->GetItem(i); - augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } - } - - std::string query = StringFormat( - "REPLACE INTO `bot_inventories` (" - "`bot_id`," - " `slot_id`," - " `item_id`," - " `inst_charges`," - " `inst_color`," - " `inst_no_drop`," - " `inst_custom_data`," - " `ornament_icon`," - " `ornament_id_file`," - " `ornament_hero_model`," - " `augment_1`," - " `augment_2`," - " `augment_3`," - " `augment_4`," - " `augment_5`," - " `augment_6`" - ")" - " VALUES (" - "%lu," /*bot_id*/ - " %lu," /*slot_id*/ - " %lu," /*item_id*/ - " %lu," /*inst_charges*/ - " %lu," /*inst_color*/ - " %lu," /*inst_no_drop*/ - " '%s'," /*inst_custom_data*/ - " %lu," /*ornament_icon*/ - " %lu," /*ornament_id_file*/ - " %lu," /*ornament_hero_model*/ - " %lu," /*augment_1*/ - " %lu," /*augment_2*/ - " %lu," /*augment_3*/ - " %lu," /*augment_4*/ - " %lu," /*augment_5*/ - " %lu" /*augment_6*/ - ")", - (unsigned long)this->GetBotID(), - (unsigned long)slotID, - (unsigned long)itemID, - (unsigned long)inst->GetCharges(), - (unsigned long)inst->GetColor(), - (unsigned long)(inst->IsAttuned()? 1: 0), - inst->GetCustomDataString().c_str(), - (unsigned long)inst->GetOrnamentationIcon(), - (unsigned long)inst->GetOrnamentationIDFile(), - (unsigned long)inst->GetOrnamentHeroModel(), - (unsigned long)augslot[0], - (unsigned long)augslot[1], - (unsigned long)augslot[2], - (unsigned long)augslot[3], - (unsigned long)augslot[4], - (unsigned long)augslot[5] - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) - *errorMessage = std::string(results.ErrorMessage()); -} - // Deletes the inventory record for the specified item from the database for this bot. -void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) { - if(this->GetBotID() == 0) +void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) +{ + if(!GetBotID()) return; - std::string query = StringFormat("DELETE FROM `bot_inventories` WHERE `bot_id` = %i AND `slot_id` = %i", this->GetBotID(), slotID); - auto results = database.QueryDatabase(query); - if(!results.Success()) - *errorMessage = std::string(results.ErrorMessage()); + if(!botdb.DeleteItemBySlot(GetBotID(), slotID)) + *errorMessage = BotDatabase::fail::DeleteItemBySlot(); m_inv.DeleteItem(slotID); UpdateEquipmentLight(); } // Retrieves all the inventory records from the database for this bot. -void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) +void Bot::GetBotItems(Inventory &inv, std::string* errorMessage) { - if(this->GetBotID() == 0) - return; + if(!GetBotID()) + return; - std::string query = StringFormat( - "SELECT" - " `slot_id`," - " `item_id`," - " `inst_charges`," - " `inst_color`," - " `inst_no_drop`," - " `inst_custom_data`," - " `ornament_icon`," - " `ornament_id_file`," - " `ornament_hero_model`," - " `augment_1`," - " `augment_2`," - " `augment_3`," - " `augment_4`, " - " `augment_5`," - " `augment_6`" - " FROM `bot_inventories`" - " WHERE `bot_id` = %i" - " ORDER BY `slot_id`", - this->GetBotID() - ); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); + if (!botdb.LoadItems(GetBotID(), inv)) { + *errorMessage = BotDatabase::fail::LoadItems(); return; } - for (auto row = results.begin(); row != results.end(); ++row) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - uint16 charges = atoi(row[2]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoul(row[9]); - aug[1] = (uint32)atoul(row[10]); - aug[2] = (uint32)atoul(row[11]); - aug[3] = (uint32)atoul(row[12]); - aug[4] = (uint32)atoul(row[13]); - aug[5] = (uint32)atoul(row[14]); - ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4], aug[5]); - if (!inst) { - Log.Out(Logs::General, Logs::Error, "Warning: bot_id %i has an invalid item_id %i in inventory slot %i", this->GetBotID(), item_id, slot_id); - continue; - } - - if (charges == 255) - inst->SetCharges(-1); - else - inst->SetCharges(charges); - - uint32 color = atoul(row[3]); - if (color > 0) - inst->SetColor(color); - - bool instnodrop = (row[4] && (uint16)atoi(row[4])) ? true : false; - if (instnodrop || (((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) || slot_id == 9999) && inst->GetItem()->Attuneable)) - inst->SetAttuned(true); - - if (row[5]) { - std::string data_str(row[5]); - std::string idAsString; - std::string value; - bool use_id = true; - - for (int i = 0; i < data_str.length(); ++i) { - if (data_str[i] == '^') { - if (!use_id) { - inst->SetCustomData(idAsString, value); - idAsString.clear(); - value.clear(); - } - - use_id = !use_id; - continue; - } - - char v = data_str[i]; - if (use_id) - idAsString.push_back(v); - else - value.push_back(v); - } - } - - uint32 ornament_icon = (uint32)atoul(row[6]); - inst->SetOrnamentIcon(ornament_icon); - - uint32 ornament_idfile = (uint32)atoul(row[7]); - inst->SetOrnamentationIDFile(ornament_idfile); - - uint32 ornament_hero_model = (uint32)atoul(row[8]); - inst->SetOrnamentHeroModel(ornament_hero_model); - - int16 put_slot_id = INVALID_INDEX; - if (slot_id < 8000 || slot_id > 8999) - put_slot_id = inv.PutItem(slot_id, *inst); - - safe_delete(inst); - - if (put_slot_id == INVALID_INDEX) - Log.Out(Logs::General, Logs::Error, "Warning: Invalid slot_id for item in inventory: bot_id=%i, item_id=%i, slot_id=%i",this->GetBotID(), item_id, slot_id); - } - UpdateEquipmentLight(); } // Returns the inventory record for this bot from the database for the specified equipment slot. -uint32 Bot::GetBotItemBySlot(uint32 slotID) { - if(this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN) - return 0; +uint32 Bot::GetBotItemBySlot(uint32 slotID) +{ + uint32 item_id = 0; + if(!GetBotID()) + return item_id; - std::string query = StringFormat("SELECT `item_id` FROM `bot_inventories` WHERE `bot_id` = %i AND `slot_id` = %i", GetBotID(), slotID); - auto results = database.QueryDatabase(query); - if(!results.Success() || results.RowCount() != 1) - return 0; + if (!botdb.LoadItemBySlot(GetBotID(), slotID, item_id)) { + if (GetBotOwner() && GetBotOwner()->IsClient()) + GetBotOwner()->CastToClient()->Message(13, "%s", BotDatabase::fail::LoadItemBySlot()); + } - auto row = results.begin(); - return atoi(row[0]); -} - -// Returns the number of inventory records the bot has in the database. -uint32 Bot::GetBotItemsCount(std::string *errorMessage) { - if(this->GetBotID() == 0) - return 0; - - std::string query = StringFormat("SELECT COUNT(*) FROM `bot_inventories` WHERE `bot_id` = %i", this->GetBotID()); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return 0; - } - - if(results.RowCount() != 1) - return 0; - - auto row = results.begin(); - return atoi(row[0]); + return item_id; } void Bot::SetLevel(uint8 in_level, bool command) { @@ -3687,160 +2979,16 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { } } -uint32 Bot::GetBotIDByBotName(std::string botName) { - if(botName.empty()) - return 0; - - std::string query = StringFormat("SELECT `bot_id` FROM `bot_data` WHERE `name` = '%s'", botName.c_str()); - auto results = database.QueryDatabase(query); - if(!results.Success() || results.RowCount() == 0) - return 0; - - auto row = results.begin(); - return atoi(row[0]); -} - -Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) +Bot* Bot::LoadBot(uint32 botID) { - if(botID == 0) - return nullptr; + Bot* loaded_bot = nullptr; + if (!botID) + return loaded_bot; - std::string query = StringFormat( - "SELECT" - " `owner_id`," - " `spells_id`," - " `name`," - " `last_name`," - " `title`," /*planned use[4]*/ - " `suffix`," /*planned use[5]*/ - " `zone_id`," - " `gender`," - " `race`," - " `class`," - " `level`," - " `deity`," /*planned use[11]*/ - " `creation_day`," /*not in-use[12]*/ - " `last_spawn`," /*not in-use[13]*/ - " `time_spawned`," - " `size`," - " `face`," - " `hair_color`," - " `hair_style`," - " `beard`," - " `beard_color`," - " `eye_color_1`," - " `eye_color_2`," - " `drakkin_heritage`," - " `drakkin_tattoo`," - " `drakkin_details`," - " `ac`," /*not in-use[26]*/ - " `atk`," /*not in-use[27]*/ - " `hp`," - " `mana`," - " `str`," /*not in-use[30]*/ - " `sta`," /*not in-use[31]*/ - " `cha`," /*not in-use[32]*/ - " `dex`," /*not in-use[33]*/ - " `int`," /*not in-use[34]*/ - " `agi`," /*not in-use[35]*/ - " `wis`," /*not in-use[36]*/ - " `fire`," /*not in-use[37]*/ - " `cold`," /*not in-use[38]*/ - " `magic`," /*not in-use[39]*/ - " `poison`," /*not in-use[40]*/ - " `disease`," /*not in-use[41]*/ - " `corruption`," /*not in-use[42]*/ - " `show_helm`,"//43 - " `follow_distance`"//44 - " FROM `bot_data`" - " WHERE `bot_id` = '%u'", - botID - ); - - auto results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return nullptr; - } - - if (results.RowCount() == 0) - return nullptr; + if (!botdb.LoadBot(botID, loaded_bot)) // TODO: Consider update to message handler + return loaded_bot; - // TODO: Consider removing resists and basic attributes from the load query above since we're using defaultNPCType values instead - auto row = results.begin(); - NPCType defaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[10]), atoi(row[8]), atoi(row[9]), atoi(row[7])); - NPCType tempNPCStruct = FillNPCTypeStruct( - atoi(row[1]), - std::string(row[2]), - std::string(row[3]), - atoi(row[10]), - atoi(row[8]), - atoi(row[9]), - atoi(row[7]), - atof(row[15]), - atoi(row[16]), - atoi(row[18]), - atoi(row[17]), - atoi(row[21]), - atoi(row[22]), - atoi(row[20]), - atoi(row[19]), - atoi(row[23]), - atoi(row[24]), - atoi(row[25]), - atoi(row[28]), - atoi(row[29]), - defaultNPCTypeStruct.MR, - defaultNPCTypeStruct.CR, - defaultNPCTypeStruct.DR, - defaultNPCTypeStruct.FR, - defaultNPCTypeStruct.PR, - defaultNPCTypeStruct.Corrup, - defaultNPCTypeStruct.AC, - defaultNPCTypeStruct.STR, - defaultNPCTypeStruct.STA, - defaultNPCTypeStruct.DEX, - defaultNPCTypeStruct.AGI, - defaultNPCTypeStruct.INT, - defaultNPCTypeStruct.WIS, - defaultNPCTypeStruct.CHA, - defaultNPCTypeStruct.ATK - ); - - Bot* loadedBot = new Bot(botID, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct); - if (loadedBot) { - loadedBot->SetShowHelm((atoi(row[43]) > 0 ? true : false)); - loadedBot->SetFollowDistance(atoi(row[44])); - } - - return loadedBot; -} - -std::list Bot::GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage) -{ - std::list groupedBots; - if(groupId == 0) - return groupedBots; - - std::string query = StringFormat( - "SELECT g.`mob_id` AS bot_id" - " FROM `vw_groups` AS g" - " JOIN `bot_data` AS b" - " ON g.`mob_id` = b.`bot_id`" - " AND g.`mob_type` = 'B'" - " WHERE g.`group_id` = %u", - groupId - ); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return groupedBots; - } - - for (auto row = results.begin(); row != results.end(); ++row) - groupedBots.push_back(atoi(row[0])); - - return groupedBots; + return loaded_bot; } // Load and spawn all zoned bots by bot owner character @@ -3850,18 +2998,18 @@ void Bot::LoadAndSpawnAllZonedBots(Client* botOwner) { Group* g = botOwner->GetGroup(); if(g) { uint32 TempGroupId = g->GetID(); - std::string errorMessage; - std::list ActiveBots = Bot::GetGroupedBotsByGroupId(botOwner->GetGroup()->GetID(), &errorMessage); - if(errorMessage.empty() && !ActiveBots.empty()) { + std::list ActiveBots; + if (!botdb.LoadGroupedBotsByGroupID(TempGroupId, ActiveBots)) { + botOwner->Message(13, "%s", BotDatabase::fail::LoadGroupedBotsByGroupID()); + return; + } + + if(!ActiveBots.empty()) { for(std::list::iterator itr = ActiveBots.begin(); itr != ActiveBots.end(); ++itr) { - Bot* activeBot = Bot::LoadBot(*itr, &errorMessage); - if(!errorMessage.empty()) { - safe_delete(activeBot); - break; - } + Bot* activeBot = Bot::LoadBot(*itr); if(activeBot) { - activeBot->Spawn(botOwner, &errorMessage); + activeBot->Spawn(botOwner); g->UpdatePlayer(activeBot); if(g->GetLeader()) activeBot->SetFollowID(g->GetLeader()->GetID()); @@ -3893,74 +3041,7 @@ bool Bot::GroupHasBot(Group* group) { return Result; } -std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage) { - std::list ownersBots; - if(botOwnerCharacterID == 0) - return ownersBots; - - std::string query = StringFormat("SELECT `bot_id`, `name`, `class`, `level`, `race`, `gender` FROM `bot_data` WHERE `owner_id` = '%u'", botOwnerCharacterID); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return ownersBots; - } - - for (auto row = results.begin(); row != results.end(); ++row) { - BotsAvailableList availableBot; - availableBot.BotID = atoi(row[0]); - strcpy(availableBot.BotName, row[1]); - availableBot.BotClass = atoi(row[2]); - availableBot.BotLevel = atoi(row[3]); - availableBot.BotRace = atoi(row[4]); - availableBot.BotGender = atoi(row[5]); - ownersBots.push_back(availableBot); - } - return ownersBots; -} - -std::list Bot::ListSpawnedBots(uint32 characterID, std::string* errorMessage) { - std::list spawnedBots; - //if(characterID == 0) - return spawnedBots; - - // Dead table..function needs to be updated or removed (no calls listed to Bot::ListSpawnedBots()) - std::string query = StringFormat("SELECT bot_name, zone_name FROM botleader WHERE leaderid = %i", characterID); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return spawnedBots; - } - - for(auto row = results.begin(); row != results.end(); ++row) { - SpawnedBotsList spawnedBotsList; - spawnedBotsList.BotLeaderCharID = characterID; - strcpy(spawnedBotsList.BotName, row[0]); - strcpy(spawnedBotsList.ZoneName, row[1]); - spawnedBots.push_back(spawnedBotsList); - } - - return spawnedBots; -} - -uint32 Bot::AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage) { - if(botOwnerCharacterID == 0) - return 0; - - std::string query = StringFormat("SELECT value FROM quest_globals WHERE name = 'bot_spawn_limit' AND charid = %i", botOwnerCharacterID); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return 0; - } - - if (results.RowCount() != 1) - return 0; - - auto row = results.begin(); - return atoi(row[0]); -} - -uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { +uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID) { uint32 Result = 0; if(botOwnerCharacterID > 0) { std::list SpawnedBots = entity_list.GetBotsByBotOwnerCharacterID(botOwnerCharacterID); @@ -3969,43 +3050,6 @@ uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessag return Result; } -uint32 Bot::CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { - if(botOwnerCharacterID == 0) - return 0; - - std::string query = StringFormat("SELECT COUNT(`bot_id`) FROM `bot_data` WHERE `owner_id` = %i", botOwnerCharacterID); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return 0; - } - - if (results.RowCount() != 1) - return 0; - - auto row = results.begin(); - return atoi(row[0]); -} - -uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) { - - if(botID == 0) - return 0; - - std::string query = StringFormat("SELECT `owner_id` FROM `bot_data` WHERE `bot_id` = %u", botID); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - *errorMessage = std::string(results.ErrorMessage()); - return 0; - } - - if (results.RowCount() != 1) - return 0; - - auto row = results.begin(); - return atoi(row[0]); -} - void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { // This essentially performs a '#bot update,' with appearance packets, based on the current methods. // This should not be called outside of Client::SetEXP() due to it's lack of rule checks. @@ -4225,11 +3269,14 @@ void Bot::BotTradeSwapItem(Client* client, int16 lootSlot, const ItemInst* inst, } } -void Bot::BotTradeAddItem(uint32 id, const ItemInst* inst, int16 charges, uint32 equipableSlots, uint16 lootSlot, std::string* errorMessage, bool addToDb) { +void Bot::BotTradeAddItem(uint32 id, const ItemInst* inst, int16 charges, uint32 equipableSlots, uint16 lootSlot, std::string* errorMessage, bool addToDb) +{ if(addToDb) { - this->SetBotItemInSlot(lootSlot, id, inst, errorMessage); - if(!errorMessage->empty()) + if (!botdb.SaveItemBySlot(this, lootSlot, inst)) { + *errorMessage = BotDatabase::fail::SaveItemBySlot(); return; + } + m_inv.PutItem(lootSlot, *inst); } @@ -6298,7 +5345,7 @@ bool Bot::IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined) { } void Bot::EquipBot(std::string* errorMessage) { - GetBotItems(errorMessage, m_inv); + GetBotItems(m_inv, errorMessage); const ItemInst* inst = 0; const Item_Struct* item = 0; for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { @@ -6345,16 +5392,22 @@ void Bot::ProcessGuildInvite(Client* guildOfficer, Bot* botToGuild) { guildOfficer->Message(13, "You dont have permission to invite."); return; } - SetBotGuildMembership(botToGuild->GetBotID(), guildOfficer->GuildID(), GUILD_MEMBER); + + if (!botdb.SaveGuildMembership(botToGuild->GetBotID(), guildOfficer->GuildID(), GUILD_MEMBER)) { + guildOfficer->Message(13, "%s for '%s'", BotDatabase::fail::SaveGuildMembership(), botToGuild->GetCleanName()); + return; + } + ServerPacket* pack = new ServerPacket(ServerOP_GuildCharRefresh, sizeof(ServerGuildCharRefresh_Struct)); ServerGuildCharRefresh_Struct *s = (ServerGuildCharRefresh_Struct *) pack->pBuffer; s->guild_id = guildOfficer->GuildID(); s->old_guild_id = GUILD_NONE; s->char_id = botToGuild->GetBotID(); worldserver.SendPacket(pack); + safe_delete(pack); } else { - guildOfficer->Message(13, "Player is in a guild."); + guildOfficer->Message(13, "Bot is in a guild."); return; } } @@ -6365,14 +5418,17 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { if(guildOfficer && !botName.empty()) { Bot* botToUnGuild = entity_list.GetBotByBotName(botName); if(botToUnGuild) { - SetBotGuildMembership(botToUnGuild->GetBotID(), 0, 0); - Result = true; - } else { - uint32 botId = GetBotIDByBotName(botName); - if(botId > 0) { - SetBotGuildMembership(botId, 0, 0); + if (botdb.SaveGuildMembership(botToUnGuild->GetBotID(), 0, 0)) + Result = true; + } else { + uint32 ownerId = 0; + if (!botdb.LoadOwnerID(botName, ownerId)) + guildOfficer->Message(13, "%s for '%s'", BotDatabase::fail::LoadOwnerID(), botName.c_str()); + uint32 botId = 0; + if (!botdb.LoadBotID(ownerId, botName, botId)) + guildOfficer->Message(13, "%s for '%s'", BotDatabase::fail::LoadBotID(), botName.c_str()); + if (botId && botdb.SaveGuildMembership(botId, 0, 0)) Result = true; - } } if(Result) { @@ -6388,47 +5444,6 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { return Result; } -void Bot::SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank) { - if(botId == 0) - return; - - if(guildid > 0) { - std::string query = StringFormat("REPLACE INTO `bot_guild_members` SET `bot_id` = %u, `guild_id` = %u, `rank` = %u", botId, guildid, rank); - auto results = database.QueryDatabase(query); - return; - } - - std::string query = StringFormat("DELETE FROM `bot_guild_members` WHERE `bot_id` = %u", botId); - auto results = database.QueryDatabase(query); -} - -void Bot::LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName) -{ - if(guildId == nullptr || guildRank == nullptr || guildName == nullptr) - return; - - std::string query = StringFormat( - "SELECT" - " gm.`guild_id`," - " gm.`rank`," - " g.`name`" - " FROM `vw_guild_members` AS gm" - " JOIN `guilds` AS g" - " ON gm.`guild_id` = g.`id`" - " WHERE gm.`char_id` = %u" - " AND gm.`mob_type` = 'B'", - GetBotID() - ); - auto results = database.QueryDatabase(query); - if(!results.Success() || results.RowCount() == 0) - return; - - auto row = results.begin(); - *guildId = atoi(row[0]); - *guildRank = atoi(row[1]); - *guildName = std::string(row[2]); -} - int32 Bot::CalcMaxMana() { switch(GetCasterClass()) { case 'I': @@ -8996,23 +8011,6 @@ uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets) { return needHealed; } -uint32 Bot::GetEquipmentColor(uint8 material_slot) const -{ - int16 slotid = 0; - uint32 botid = this->GetBotID(); - slotid = Inventory::CalcSlotFromMaterial(material_slot); - if (slotid == INVALID_INDEX) - return 0; - - std::string query = StringFormat("SELECT `inst_color` FROM `bot_inventories` WHERE `bot_id` = %u AND `slot_id` = %u", botid, slotid); - auto results = database.QueryDatabase(query); - if (!results.Success() || results.RowCount() != 1) - return 0; - - auto row = results.begin(); - return atoul(row[0]); -} - int Bot::GetRawACNoShield(int &shield_ac) { int ac = itembonuses.AC + spellbonuses.AC; shield_ac = 0; @@ -9417,26 +8415,13 @@ bool Bot::DyeArmor(int16 slot_id, uint32 rgb, bool all_flag, bool save_flag) } if (save_flag) { - std::string where_clause; + int16 save_slot = slot_id; if (all_flag) - where_clause = StringFormat(" WHERE `slot_id` IN ('%u', '%u', '%u', '%u', '%u', '%u', '%u')", - MainHead, MainArms, MainWrist1, MainHands, MainChest, MainLegs, MainFeet); - else - where_clause = StringFormat(" WHERE `slot_id` = '%u'", slot_id); + save_slot = -2; - std::string query = StringFormat( - "UPDATE `bot_inventories`" - " SET `inst_color` = '%u'" - " %s" - " AND `bot_id` = '%u'", - rgb, - where_clause.c_str(), - GetBotID() - ); - - auto results = database.QueryDatabase(query); - if (!results.Success() && GetOwner() && GetOwner()->IsClient()) { - GetOwner()->CastToClient()->Message(15, "Failed to save dye armor changes for %s due to unknown cause", GetCleanName()); + if (!botdb.SaveEquipmentColor(GetBotID(), save_slot, rgb)) { + if (GetBotOwner() && GetBotOwner()->IsClient()) + GetBotOwner()->CastToClient()->Message(13, "%s", BotDatabase::fail::SaveEquipmentColor()); return false; } } @@ -9444,44 +8429,24 @@ bool Bot::DyeArmor(int16 slot_id, uint32 rgb, bool all_flag, bool save_flag) return true; } -std::string Bot::CreateSayLink(Client* c, const char* message, const char* name) { - int sayid = 0; - int sz = strlen(message); - char *escaped_string = new char[sz * 2]; - database.DoEscapeString(escaped_string, message, sz); - std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string); - auto results = database.QueryDatabase(query); - if (results.Success()) { - if (results.RowCount() >= 1) { - for (auto row = results.begin();row != results.end(); ++row) - sayid = atoi(row[0]); - } else { - std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string); - results = database.QueryDatabase(insert_query); - if (!results.Success()) { - Log.Out(Logs::General, Logs::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); - } else { - results = database.QueryDatabase(query); - if (results.Success()) { - if (results.RowCount() >= 1) - for(auto row = results.begin(); row != results.end(); ++row) - sayid = atoi(row[0]); - } - else - Log.Out(Logs::General, Logs::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); - } - } - } +std::string Bot::CreateSayLink(Client* c, const char* message, const char* name) +{ + int saylink_size = strlen(message); + char* escaped_string = new char[saylink_size * 2]; + + database.DoEscapeString(escaped_string, message, saylink_size); + + uint32 saylink_id = database.LoadSaylinkID(escaped_string); safe_delete_array(escaped_string); Client::TextLink linker; linker.SetLinkType(linker.linkItemData); linker.SetProxyItemID(SAYLINK_ITEM_ID); - linker.SetProxyAugment1ID(sayid); + linker.SetProxyAugment1ID(saylink_id); linker.SetProxyText(name); - auto say_link = linker.GenerateLink(); - return say_link; + auto saylink = linker.GenerateLink(); + return saylink; } #endif diff --git a/zone/bot.h b/zone/bot.h index 89759cd91..49d82f72a 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -226,9 +226,7 @@ public: static bool IsValidRaceClassCombo(uint16 r, uint8 c); bool IsValidName(); static bool IsValidName(std::string& name); - static bool IsBotNameAvailable(const char *botName, std::string* errorMessage); - bool DeleteBot(std::string* errorMessage); - void Spawn(Client* botCharacterOwner, std::string* errorMessage); + void Spawn(Client* botCharacterOwner); virtual void SetLevel(uint8 in_level, bool command = false); virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); virtual bool Process(); @@ -388,19 +386,12 @@ public: void BotTradeAddItem(uint32 id, const ItemInst* inst, int16 charges, uint32 equipableSlots, uint16 lootSlot, std::string* errorMessage, bool addToDb = true); void EquipBot(std::string* errorMessage); bool CheckLoreConflict(const Item_Struct* item); - uint32 GetEquipmentColor(uint8 material_slot) const; virtual void UpdateEquipmentLight() { m_Light.Type.Equipment = m_inv.FindBrightestLightType(); m_Light.Level.Equipment = m_Light.TypeToLevel(m_Light.Type.Equipment); } // Static Class Methods //static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped - static uint32 GetBotIDByBotName(std::string botName); - static Bot* LoadBot(uint32 botID, std::string* errorMessage); - static std::list GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage); - static std::list ListSpawnedBots(uint32 characterID, std::string* errorMessage); - static uint32 SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage); - static uint32 CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage); - static uint32 AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage); - static uint32 GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage); + static Bot* LoadBot(uint32 botID); + static uint32 SpawnedBotCount(uint32 botOwnerCharacterID); static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp); //static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage); static std::string ClassIdToString(uint16 classId); @@ -411,7 +402,6 @@ public: static void ProcessBotGroupDisband(Client* c, std::string botName); static void BotOrderCampAll(Client* c); static void ProcessBotInspectionRequest(Bot* inspectedBot, Client* client); - static std::list GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage); static void LoadAndSpawnAllZonedBots(Client* botOwner); static bool GroupHasBot(Group* group); static Bot* GetFirstBotInGroup(Group* group); @@ -604,15 +594,37 @@ public: // Class Destructors virtual ~Bot(); - // Publicized protected/private functions - virtual void BotRangedAttack(Mob* other); // protected - uint32 GetBotItemsCount(std::string* errorMessage); // private - void BotRemoveEquipItem(int slot); // private - void RemoveBotItemBySlot(uint32 slotID, std::string* errorMessage); // private + // Publicized protected functions + virtual void BotRangedAttack(Mob* other); + // Publicized private functions + static NPCType FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack); + void BotRemoveEquipItem(int slot); + void RemoveBotItemBySlot(uint32 slotID, std::string* errorMessage); + uint32 GetTotalPlayTime(); + + // New accessors for BotDatabase access + bool DeleteBot(); + uint32* GetTimers() { return timers; } + uint32 GetLastZoneID() { return _lastZoneId; } + int32 GetBaseAC() { return _baseAC; } + int32 GetBaseATK() { return _baseATK; } + int32 GetBaseSTR() { return _baseSTR; } + int32 GetBaseSTA() { return _baseSTA; } + int32 GetBaseCHA() { return _baseCHA; } + int32 GetBaseDEX() { return _baseDEX; } + int32 GetBaseINT() { return _baseINT; } + int32 GetBaseAGI() { return _baseAGI; } + int32 GetBaseWIS() { return _baseWIS; } + int32 GetBaseFR() { return _baseFR; } + int32 GetBaseCR() { return _baseCR; } + int32 GetBaseMR() { return _baseMR; } + int32 GetBasePR() { return _basePR; } + int32 GetBaseDR() { return _baseDR; } + int32 GetBaseCorrup() { return _baseCorrup; } + protected: virtual void PetAIProcess(); - static NPCType FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack); virtual void BotMeditate(bool isSitting); virtual bool CheckBotDoubleAttack(bool Triple = false); virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id); @@ -622,8 +634,6 @@ protected: virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); virtual float GetMaxMeleeRangeToTarget(Mob* target); - static void SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank); - private: // Class Members uint32 _botID; @@ -701,30 +711,14 @@ private: void SetBotID(uint32 botID); // Private "Inventory" Methods - void GetBotItems(std::string* errorMessage, Inventory &inv); + void GetBotItems(Inventory &inv, std::string* errorMessage); void BotAddEquipItem(int slot, uint32 id); uint32 GetBotItemBySlot(uint32 slotID); - void SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, std::string* errorMessage); - uint32 GetTotalPlayTime(); - void SaveBuffs(); // Saves existing buffs to the database to persist zoning and camping - void LoadBuffs(); // Retrieves saved buffs from the database on spawning - void LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId); - void SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId); - void LoadPetItems(uint32* petItems, uint32 botPetSaveId); - void SavePetItems(uint32* petItems, uint32 botPetSaveId); - void LoadPetStats(std::string* petName, uint32* petMana, uint32* petHitPoints, uint32* botPetId, uint32 botPetSaveId); - uint32 SavePetStats(std::string petName, uint32 petMana, uint32 petHitPoints, uint32 botPetId); - void LoadPet(); // Load and spawn bot pet if there is one - void SavePet(); // Save and depop bot pet if there is one - uint32 GetPetSaveId(); - void DeletePetBuffs(uint32 botPetSaveId); - void DeletePetItems(uint32 botPetSaveId); - void DeletePetStats(uint32 botPetSaveId); - void LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName); - void LoadStance(); - void SaveStance(); - void LoadTimers(); - void SaveTimers(); + + // Private "Pet" Methods + bool LoadPet(); // Load and spawn bot pet if there is one + bool SavePet(); // Save and depop bot pet if there is one + bool DeletePet(); }; #endif // BOTS diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index c9d7d31bb..0cd0b8072 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1413,7 +1413,7 @@ int bot_command_init(void) } std::map>> bot_command_settings; - botdb.GetCommandSettings(bot_command_settings); + botdb.LoadBotCommandSettings(bot_command_settings); auto working_bcl = bot_command_list; for (auto working_bcl_iter : working_bcl) { @@ -1857,13 +1857,13 @@ namespace MyBots return; std::string group_name = name; - std::string error_message; - uint32 group_id = botdb.GetGroupIDForLoadGroup(bot_owner->CharacterID(), group_name, error_message); - if (!group_id || error_message.size()) + + uint32 botgroup_id = 0; + if (!botdb.LoadBotGroupIDForLoadBotGroup(bot_owner->CharacterID(), group_name, botgroup_id) || !botgroup_id) return; - std::map> group_list = botdb.LoadGroup(group_name, error_message); - if (group_list.find(group_id) == group_list.end() || !group_list[group_id].size() || error_message.size()) + std::map> botgroup_list; + if (!botdb.LoadBotGroup(group_name, botgroup_list) || botgroup_list.find(botgroup_id) == botgroup_list.end() || !botgroup_list[botgroup_id].size()) return; std::list selectable_bot_list; @@ -1872,7 +1872,7 @@ namespace MyBots return; selectable_bot_list.remove(nullptr); - for (auto group_iter : group_list[group_id]) { + for (auto group_iter : botgroup_list[botgroup_id]) { for (auto bot_iter : selectable_bot_list) { if (bot_iter->GetBotID() != group_iter) continue; @@ -4185,34 +4185,45 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep) return; } - std::string TempErrorMessage; + std::string error_message; - if (!Bot::IsBotNameAvailable(bot_name.c_str(), &TempErrorMessage)) { - if (!TempErrorMessage.empty()) - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); + bool available_flag = false; + if (!botdb.QueryNameAvailablity(bot_name, available_flag)) { + c->Message(m_fail, "%s", BotDatabase::fail::QueryNameAvailablity()); + return; + } + if (!available_flag) { c->Message(m_fail, "The name %s is already being used. Please choose a different name", bot_name.c_str()); return; } - uint32 mbc = RuleI(Bots, CreationLimit); - if (Bot::CreatedBotCount(c->CharacterID(), &TempErrorMessage) >= mbc) { - if (!TempErrorMessage.empty()) - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); - c->Message(m_fail, "You have reached the maximum limit of %i bots", mbc); + uint32 max_bot_count = RuleI(Bots, CreationLimit); + + uint32 bot_count = 0; + if (!botdb.QueryBotCount(c->CharacterID(), bot_count)) { + c->Message(m_fail, "%s", BotDatabase::fail::QueryBotCount()); + return; + } + if (bot_count >= max_bot_count) { + c->Message(m_fail, "You have reached the maximum limit of %i bots", max_bot_count); return; } - auto clone_id = botdb.Clone(c->CharacterID(), my_bot->GetBotID(), bot_name.c_str()); - if (!clone_id) { - c->Message(m_fail, "Clone creation of bot '%s' failed...", my_bot->GetCleanName()); + uint32 clone_id = 0; + if (!botdb.CreateCloneBot(c->CharacterID(), my_bot->GetBotID(), bot_name, clone_id) || !clone_id) { + c->Message(m_fail, "%s '%s'", BotDatabase::fail::CreateCloneBot(), bot_name.c_str()); return; } - if (!botdb.CloneInventory(c->CharacterID(), my_bot->GetBotID(), clone_id)) { - c->Message(m_fail, "Inventory import for bot clone '%s' failed...", bot_name.c_str()); - return; - } + BotStanceType clone_stance = BotStancePassive; + if (!botdb.LoadStance(my_bot->GetBotID(), clone_stance)) + c->Message(m_fail, "%s for bot '%s'", BotDatabase::fail::LoadStance(), my_bot->GetCleanName()); + if (!botdb.SaveStance(clone_id, clone_stance)) + c->Message(m_fail, "%s for clone '%s'", BotDatabase::fail::SaveStance(), bot_name.c_str()); + if (!botdb.CreateCloneBotInventory(c->CharacterID(), my_bot->GetBotID(), clone_id)) + c->Message(m_fail, "%s for clone '%s'", BotDatabase::fail::CreateCloneBotInventory(), bot_name.c_str()); + c->Message(m_action, "Bot '%s' was successfully cloned to bot '%s'", my_bot->GetCleanName(), bot_name.c_str()); } @@ -4276,11 +4287,10 @@ void bot_subcommand_bot_delete(Client *c, const Seperator *sep) return; } - std::string TempErrorMessage; + std::string error_message; - my_bot->DeleteBot(&TempErrorMessage); - if (!TempErrorMessage.empty()) { - c->Message(m_unknown, "Failed to delete '%s' due to database error: %s", my_bot->GetCleanName(), TempErrorMessage.c_str()); + if (!my_bot->DeleteBot()) { + c->Message(m_unknown, "Failed to delete '%s' due to database error", my_bot->GetCleanName()); return; } @@ -4401,14 +4411,14 @@ void bot_subcommand_bot_dye_armor(Client *c, const Seperator *sep) } if (ab_type == ActionableBots::ABT_All) { - bool action_success = false; - if (dye_all) - action_success = botdb.SetAllArmorColors(c->CharacterID(), rgb_value); - else - action_success = botdb.SetAllArmorColorBySlot(c->CharacterID(), slot_id, rgb_value); - - if (!action_success) - c->Message(m_unknown, "Failed to save dye armor changes for your bots due to unknown cause"); + if (dye_all) { + if (!botdb.SaveAllArmorColors(c->CharacterID(), rgb_value)) + c->Message(m_fail, "%s", BotDatabase::fail::SaveAllArmorColors()); + } + else { + if (!botdb.SaveAllArmorColorBySlot(c->CharacterID(), slot_id, rgb_value)) + c->Message(m_fail, "%s", BotDatabase::fail::SaveAllArmorColorBySlot()); + } } } @@ -4551,8 +4561,8 @@ void bot_subcommand_bot_follow_distance(Client *c, const Seperator *sep) continue; bot_iter->SetFollowDistance(bfd); - if (ab_type != ActionableBots::ABT_All && !botdb.SetFollowDistance(c->CharacterID(), bot_iter->GetBotID(), bfd)) { - c->Message(m_unknown, "DATABASE ERROR: Could not change follow distance for bot %s", bot_iter->GetCleanName()); + if (ab_type != ActionableBots::ABT_All && !botdb.SaveFollowDistance(c->CharacterID(), bot_iter->GetBotID(), bfd)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::SaveFollowDistance(), bot_iter->GetCleanName()); return; } @@ -4560,8 +4570,8 @@ void bot_subcommand_bot_follow_distance(Client *c, const Seperator *sep) } if (ab_type == ActionableBots::ABT_All) { - if (!botdb.SetAllFollowDistances(c->CharacterID(), bfd)) { - c->Message(m_unknown, "Failed to save follow distance changes for your bots due to unknown cause"); + if (!botdb.SaveAllFollowDistances(c->CharacterID(), bfd)) { + c->Message(m_fail, "%s", BotDatabase::fail::SaveAllFollowDistances()); return; } @@ -4717,32 +4727,34 @@ void bot_subcommand_bot_inspect_message(Client *c, const Seperator *sep) if (ab_type == ActionableBots::ABT_None) return; - const auto cms = &c->GetInspectMessage(); + const auto client_message_struct = &c->GetInspectMessage(); int bot_count = 0; for (auto bot_iter : sbl) { if (!bot_iter) continue; - auto bms = &bot_iter->GetInspectMessage(); - memset(bms, 0, sizeof(InspectMessage_Struct)); + auto bot_message_struct = &bot_iter->GetInspectMessage(); + memset(bot_message_struct, 0, sizeof(InspectMessage_Struct)); if (set_flag) - memcpy(bms, cms, sizeof(InspectMessage_Struct)); + memcpy(bot_message_struct, client_message_struct, sizeof(InspectMessage_Struct)); - if (ab_type != ActionableBots::ABT_All) - botdb.SetInspectMessage(bot_iter->GetBotID(), bms); + if (ab_type != ActionableBots::ABT_All && !botdb.SaveInspectMessage(bot_iter->GetBotID(), *bot_message_struct)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::SaveInspectMessage(), bot_iter->GetCleanName()); + return; + } ++bot_count; } if (ab_type == ActionableBots::ABT_All) { - InspectMessage_Struct bms; - memset(&bms, 0, sizeof(InspectMessage_Struct)); + InspectMessage_Struct bot_message_struct; + memset(&bot_message_struct, 0, sizeof(InspectMessage_Struct)); if (set_flag) - memcpy(&bms, cms, sizeof(InspectMessage_Struct)); + memcpy(&bot_message_struct, client_message_struct, sizeof(InspectMessage_Struct)); - if (!botdb.SetAllInspectMessages(c->CharacterID(), &bms)) { - c->Message(m_fail, "Failed to save inspect message changes for your bots due to unknown cause"); + if (!botdb.SaveAllInspectMessages(c->CharacterID(), bot_message_struct)) { + c->Message(m_fail, "%s", BotDatabase::fail::SaveAllInspectMessages()); return; } @@ -4796,29 +4808,27 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) return; } - std::string TempErrorMessage; - auto dbbl = Bot::GetBotList(c->CharacterID(), &TempErrorMessage); - - if (!TempErrorMessage.empty()) { - c->Message(m_fail, "Failed to load 'BotsAvailableList' due to unknown cause"); + std::list bots_list; + if (!botdb.LoadBotsList(c->CharacterID(), bots_list)) { + c->Message(m_fail, "%s", BotDatabase::fail::LoadBotsList()); return; } - if (dbbl.empty()) { + if (bots_list.empty()) { c->Message(m_fail, "You have no bots"); return; } int bot_count = 0; - for (auto dbbl_iter : dbbl) { + for (auto bots_iter : bots_list) { if (filter_mask) { - if ((filter_mask & MaskClass) && filter_value[FilterClass] != dbbl_iter.BotClass) + if ((filter_mask & MaskClass) && filter_value[FilterClass] != bots_iter.Class) continue; - if ((filter_mask & MaskRace) && filter_value[FilterRace] != dbbl_iter.BotRace) + if ((filter_mask & MaskRace) && filter_value[FilterRace] != bots_iter.Race) continue; if (filter_mask & MaskName) { std::string name_criteria = sep->arg[name_criteria_arg]; std::transform(name_criteria.begin(), name_criteria.end(), name_criteria.begin(), ::tolower); - std::string name_check = dbbl_iter.BotName; + std::string name_check = bots_iter.Name; std::transform(name_check.begin(), name_check.end(), name_check.begin(), ::tolower); if (name_check.find(name_criteria) == std::string::npos) continue; @@ -4826,11 +4836,11 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) } c->Message(m_message, "%s is a level %u %s %s %s", - dbbl_iter.BotName, - dbbl_iter.BotLevel, - Bot::RaceIdToString(dbbl_iter.BotRace).c_str(), - ((dbbl_iter.BotGender == FEMALE) ? ("Female") : ((dbbl_iter.BotGender == MALE) ? ("Male") : ("Neuter"))), - Bot::ClassIdToString(dbbl_iter.BotClass).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_count; @@ -4839,7 +4849,7 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep) c->Message(m_fail, "You have no bots meeting this criteria"); } else { - c->Message(m_action, "%i of %i bot%s shown", bot_count, dbbl.size(), ((bot_count != 1) ? ("s") : (""))); + 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") : (""))); } } @@ -4946,32 +4956,26 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep) return; } - std::string TempErrorMessage; - - int sbc = Bot::SpawnedBotCount(c->CharacterID(), &TempErrorMessage); - if (!TempErrorMessage.empty()) { - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); - return; - } + int spawned_bot_count = Bot::SpawnedBotCount(c->CharacterID()); int rule_limit = RuleI(Bots, SpawnLimit); - if (sbc >= rule_limit && !c->GetGM()) { + if (spawned_bot_count >= rule_limit && !c->GetGM()) { c->Message(m_fail, "You can not have more than %i spawned bots", rule_limit); return; } if (RuleB(Bots, QuestableSpawnLimit) && !c->GetGM()) { - int abc = Bot::AllowedBotSpawns(c->CharacterID(), &TempErrorMessage); - if (!TempErrorMessage.empty()) { - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); + int allowed_bot_count = 0; + if (!botdb.LoadQuestableSpawnCount(c->CharacterID(), allowed_bot_count)) { + c->Message(m_fail, "%s", BotDatabase::fail::LoadQuestableSpawnCount()); return; } - if (!abc) { + if (!allowed_bot_count) { c->Message(m_fail, "You are not currently allowed any spawned bots"); return; } - if (sbc >= abc) { - c->Message(m_fail, "You have reached your current limit of %i spawned bots", abc); + if (spawned_bot_count >= allowed_bot_count) { + c->Message(m_fail, "You have reached your current limit of %i spawned bots", allowed_bot_count); return; } } @@ -4982,10 +4986,12 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep) } std::string bot_name = sep->arg[1]; - auto bot_id = Bot::GetBotIDByBotName(bot_name); - if (Bot::GetBotOwnerCharacterID(bot_id, &TempErrorMessage) != c->CharacterID()) { - if (!TempErrorMessage.empty()) - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); + uint32 bot_id = 0; + if (!botdb.LoadBotID(c->CharacterID(), bot_name, bot_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotID(), bot_name.c_str()); + return; + } + if (!bot_id) { c->Message(m_fail, "You don't own a bot named '%s'", bot_name.c_str()); return; } @@ -5020,18 +5026,13 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep) // return; //} - auto my_bot = Bot::LoadBot(bot_id, &TempErrorMessage); - if (!TempErrorMessage.empty()) { - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); - safe_delete(my_bot); - return; - } + auto my_bot = Bot::LoadBot(bot_id); if (!my_bot) { c->Message(m_fail, "No valid bot '%s' (id: %i) exists", bot_name.c_str(), bot_id); return; } - my_bot->Spawn(c, &TempErrorMessage); // 'TempErrorMessage' not used... + my_bot->Spawn(c); static const char* bot_spawn_message[16] = { "A solid weapon is my ally!", // WARRIOR / 'generic' @@ -5267,8 +5268,8 @@ void bot_subcommand_bot_toggle_helm(Client *c, const Seperator *sep) bot_iter->SetShowHelm(helm_state); if (ab_type != ActionableBots::ABT_All) { - if (!botdb.SetHelmAppearance(c->CharacterID(), bot_iter->GetBotID(), bot_iter->GetShowHelm())) { - c->Message(m_unknown, "DATABASE ERROR: Could not change helm appearance for bot %s", bot_iter->GetCleanName()); + if (!botdb.SaveHelmAppearance(c->CharacterID(), bot_iter->GetBotID(), bot_iter->GetShowHelm())) { + c->Message(m_unknown, "%s for '%s'", bot_iter->GetCleanName()); return; } @@ -5287,15 +5288,14 @@ void bot_subcommand_bot_toggle_helm(Client *c, const Seperator *sep) } if (ab_type == ActionableBots::ABT_All) { - bool action_success = false; std::string query; - if (toggle_helm) - action_success = botdb.ToggleAllHelmAppearances(c->CharacterID()); - else - action_success = botdb.SetAllHelmAppearances(c->CharacterID(), helm_state); - if (!action_success) { - c->Message(m_unknown, "Failed to save helm changes for your bots due to unknown cause"); - return; + if (toggle_helm) { + if (!botdb.ToggleAllHelmAppearances(c->CharacterID())) + c->Message(m_fail, "%s", BotDatabase::fail::ToggleAllHelmAppearances()); + } + else { + if (!botdb.SaveAllHelmAppearances(c->CharacterID(), helm_state)) + c->Message(m_fail, "%s", BotDatabase::fail::SaveAllHelmAppearances()); } c->Message(m_action, "%s all of your bot show helm flags", toggle_helm ? "Toggled" : (helm_state ? "Set" : "Cleared")); @@ -5451,10 +5451,12 @@ void bot_subcommand_botgroup_add_member(Client *c, const Seperator *sep) return; } - std::string error_message; - if (botdb.GetGroupIDByMemberID(new_member->GetBotID(), error_message)) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); + uint32 botgroup_id = 0; + if (!botdb.LoadBotGroupIDByMemberID(new_member->GetBotID(), botgroup_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotGroupIDByMemberID(), new_member->GetCleanName()); + return; + } + if (botgroup_id) { c->Message(m_fail, "%s is already a current member of a bot-group", new_member->GetCleanName()); return; } @@ -5467,34 +5469,44 @@ void bot_subcommand_botgroup_add_member(Client *c, const Seperator *sep) return; } - auto group_leader = sbl.front(); - if (!group_leader) { + auto botgroup_leader = sbl.front(); + if (!botgroup_leader) { c->Message(m_unknown, "Error: Group leader bot dereferenced to nullptr"); return; } - Group* group_inst = group_leader->GetGroup(); - if (!group_inst || group_inst->GetLeader() != group_leader) { - c->Message(m_fail, "%s is not the current leader of a group", group_leader->GetCleanName()); + Group* group_inst = botgroup_leader->GetGroup(); + if (!group_inst || group_inst->GetLeader() != botgroup_leader) { + c->Message(m_fail, "%s is not the current leader of a group", botgroup_leader->GetCleanName()); return; } - if (!botdb.GetGroupIDByLeaderID(group_leader->GetBotID(), error_message)) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "%s is not the current leader of a bot-group", group_leader->GetCleanName()); + botgroup_id = 0; + if (!botdb.LoadBotGroupIDByLeaderID(botgroup_leader->GetBotID(), botgroup_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotGroupIDByLeaderID(), botgroup_leader->GetCleanName()); + return; + } + if (!botgroup_id) { + c->Message(m_fail, "%s is not the current leader of a bot-group", botgroup_leader->GetCleanName()); return; } if (!Bot::AddBotToGroup(new_member, group_inst)) { - c->Message(m_fail, "Could not add %s as a new member to %s's group", new_member->GetCleanName(), group_leader->GetCleanName()); + c->Message(m_fail, "Could not add %s as a new member to %s's group", new_member->GetCleanName(), botgroup_leader->GetCleanName()); return; } database.SetGroupID(new_member->GetName(), group_inst->GetID(), new_member->GetBotID()); - botdb.AddMemberToBotGroup(group_leader->GetBotID(), new_member->GetBotID(), error_message); - std::string botgroup_name = botdb.GetGroupNameByLeaderID(group_leader->GetBotID(), error_message); + if (!botdb.AddMemberToBotGroup(botgroup_leader->GetBotID(), new_member->GetBotID())) { + c->Message(m_fail, "%s - %s->%s", BotDatabase::fail::AddMemberToBotGroup(), new_member->GetCleanName(), botgroup_leader->GetCleanName()); + Bot::RemoveBotFromGroup(new_member, botgroup_leader->GetGroup()); + return; + } + + std::string botgroup_name; + if (!botdb.LoadBotGroupNameByLeaderID(botgroup_leader->GetBotID(), botgroup_name)) + c->Message(m_fail, "%s", BotDatabase::fail::LoadBotGroupNameByLeaderID()); c->Message(m_action, "Successfully added %s to bot-group %s", new_member->GetCleanName(), botgroup_name.c_str()); } @@ -5508,13 +5520,19 @@ void bot_subcommand_botgroup_create(Client *c, const Seperator *sep) return; } - std::string group_name_arg = sep->arg[1]; - if (group_name_arg.empty()) { + std::string botgroup_name_arg = sep->arg[1]; + if (botgroup_name_arg.empty()) { c->Message(m_fail, "You must specify a [name] for this bot-group to use this command"); return; } - if (botdb.DoesBotGroupExist(group_name_arg)) { - c->Message(m_fail, "The [name] %s already exists for a bot-group. Please choose another", group_name_arg.c_str()); + + bool extant_flag = false; + if (!botdb.QueryBotGroupExistence(botgroup_name_arg, extant_flag)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::QueryBotGroupExistence(), botgroup_name_arg.c_str()); + return; + } + if (extant_flag) { + c->Message(m_fail, "The [name] %s already exists for a bot-group. Please choose another", botgroup_name_arg.c_str()); return; } @@ -5527,50 +5545,55 @@ void bot_subcommand_botgroup_create(Client *c, const Seperator *sep) return; } - auto group_leader = sbl.front(); - if (!group_leader) { + auto botgroup_leader = sbl.front(); + if (!botgroup_leader) { c->Message(m_unknown, "Error: Group leader bot dereferenced to nullptr"); return; } - if (group_leader->HasGroup()) { - c->Message(m_fail, "%s is already a current member of a group", group_leader->GetCleanName()); + if (botgroup_leader->HasGroup()) { + c->Message(m_fail, "%s is already a current member of a group", botgroup_leader->GetCleanName()); return; } - std::string error_message; - if (botdb.GetGroupIDByLeaderID(group_leader->GetBotID(), error_message)) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "%s is already the current leader of a bot-group", group_leader->GetCleanName()); + uint32 botgroup_id = 0; + if (!botdb.LoadBotGroupIDByLeaderID(botgroup_leader->GetBotID(), botgroup_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotGroupIDByLeaderID(), botgroup_leader->GetCleanName()); return; } - if (botdb.GetGroupIDByMemberID(group_leader->GetBotID(), error_message)) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "%s is already a current member of a bot-group", group_leader->GetCleanName()); + if (botgroup_id) { + c->Message(m_fail, "%s is already the current leader of a bot-group", botgroup_leader->GetCleanName()); return; } - Group* group_inst = new Group(group_leader); + botgroup_id = 0; + if (!botdb.LoadBotGroupIDByMemberID(botgroup_leader->GetBotID(), botgroup_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotGroupIDByMemberID(), botgroup_leader->GetCleanName()); + return; + } + if (botgroup_id) { + c->Message(m_fail, "%s is already a current member of a bot-group", botgroup_leader->GetCleanName()); + return; + } + + Group* group_inst = new Group(botgroup_leader); if (!group_inst) { c->Message(m_unknown, "Could not create a new group instance"); return; } - if (!botdb.CreateBotGroup(group_name_arg, group_leader->GetBotID(), error_message)) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Could not create bot-group %s", group_name_arg.c_str()); + if (!botdb.CreateBotGroup(botgroup_name_arg, botgroup_leader->GetBotID())) { + c->Message(m_fail, "%s '%s'", BotDatabase::fail::CreateBotGroup(), botgroup_name_arg.c_str()); safe_delete(group_inst); return; } - entity_list.AddGroup(group_inst); - database.SetGroupID(group_leader->GetCleanName(), group_inst->GetID(), group_leader->GetBotID()); - database.SetGroupLeaderName(group_inst->GetID(), group_leader->GetCleanName()); - group_leader->SetFollowID(c->GetID()); - c->Message(m_action, "Successfully created bot-group %s with %s as its leader", group_name_arg.c_str(), group_leader->GetCleanName()); + entity_list.AddGroup(group_inst); + database.SetGroupID(botgroup_leader->GetCleanName(), group_inst->GetID(), botgroup_leader->GetBotID()); + database.SetGroupLeaderName(group_inst->GetID(), botgroup_leader->GetCleanName()); + botgroup_leader->SetFollowID(c->GetID()); + + c->Message(m_action, "Successfully created bot-group '%s' with '%s' as its leader", botgroup_name_arg.c_str(), botgroup_leader->GetCleanName()); } void bot_subcommand_botgroup_delete(Client *c, const Seperator *sep) @@ -5582,36 +5605,48 @@ void bot_subcommand_botgroup_delete(Client *c, const Seperator *sep) return; } - std::string group_name_arg = sep->arg[1]; - if (group_name_arg.empty()) { + std::string botgroup_name_arg = sep->arg[1]; + if (botgroup_name_arg.empty()) { c->Message(m_fail, "You must specify a [name] for this bot-group to use this command"); return; } - std::string error_message; - uint32 group_id = botdb.GetGroupIDForLoadGroup(c->CharacterID(), group_name_arg, error_message); - if (!group_id) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Could not locate group id for %s", group_name_arg.c_str()); + uint32 botgroup_id = 0; + if (!botdb.LoadBotGroupIDForLoadBotGroup(c->CharacterID(), botgroup_name_arg, botgroup_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotGroupIDForLoadBotGroup(), botgroup_name_arg.c_str()); + return; + } + if (!botgroup_id) { + c->Message(m_fail, "Could not locate group id for '%s'", botgroup_name_arg.c_str()); return; } - uint32 leader_id = botdb.GetLeaderIDByGroupID(group_id, error_message); + uint32 leader_id = 0; + if (!botdb.LoadLeaderIDByBotGroupID(botgroup_id, leader_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadLeaderIDByBotGroupID(), botgroup_name_arg.c_str()); + return; + } if (!leader_id) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Could not locate leader id for %s", group_name_arg.c_str()); + c->Message(m_fail, "Could not locate leader id for '%s'", botgroup_name_arg.c_str()); return; } std::list gbl; std::list sbl; MyBots::PopulateSBL_BySpawnedBots(c, sbl); - std::map> groups_list = botdb.LoadGroup(group_name_arg, error_message); + + std::map> member_list; + if (!botdb.LoadBotGroup(botgroup_name_arg, member_list)) { + c->Message(m_fail, "%s '%s'", BotDatabase::fail::LoadBotGroup(), botgroup_name_arg.c_str()); + return; + } + if (member_list.find(botgroup_id) == member_list.end() || member_list[botgroup_id].empty()) { + c->Message(m_fail, "Could not locate member list for bot-group '%s'", botgroup_name_arg.c_str()); + return; + } for (auto bot_iter : sbl) { - for (auto group_iter : groups_list[group_id]) { + for (auto group_iter : member_list[botgroup_id]) { if (bot_iter->GetBotID() == group_iter) { gbl.push_back(bot_iter); break; @@ -5625,14 +5660,12 @@ void bot_subcommand_botgroup_delete(Client *c, const Seperator *sep) Bot::RemoveBotFromGroup(group_member, group_member->GetGroup()); } - if (!botdb.DeleteBotGroup(leader_id, error_message)) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Failed to delete bot-group %s", group_name_arg.c_str()); + if (!botdb.DeleteBotGroup(leader_id)) { + c->Message(m_fail, "%s '%s'", BotDatabase::fail::DeleteBotGroup(), botgroup_name_arg.c_str()); return; } - c->Message(m_action, "Successfully deleted bot-group %s", group_name_arg.c_str()); + c->Message(m_action, "Successfully deleted bot-group %s", botgroup_name_arg.c_str()); } void bot_subcommand_botgroup_list(Client *c, const Seperator *sep) @@ -5644,18 +5677,19 @@ void bot_subcommand_botgroup_list(Client *c, const Seperator *sep) return; } - std::string error_message; - auto groups_list = botdb.GetGroupsListByOwnerID(c->CharacterID(), error_message); - if (groups_list.empty()) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); + std::list> botgroups_list; + if (!botdb.LoadBotGroupsListByOwnerID(c->CharacterID(), botgroups_list)) { + c->Message(m_fail, "%s", BotDatabase::fail::LoadBotGroupsListByOwnerID()); + return; + } + if (botgroups_list.empty()) { c->Message(m_fail, "You have no saved bot-groups"); return; } int botgroup_count = 0; - for (auto groups_iter : groups_list) - c->Message(m_message, "(%i) Bot-group name: %s | Leader: %s", (++botgroup_count), groups_iter.first.c_str(), groups_iter.second.c_str()); + for (auto botgroups_iter : botgroups_list) + c->Message(m_message, "(%i) Bot-group name: %s | Leader: %s", (++botgroup_count), botgroups_iter.first.c_str(), botgroups_iter.second.c_str()); c->Message(m_action, "%i bot-groups listed", botgroup_count); } @@ -5669,13 +5703,19 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep) return; } - std::string group_name_arg = sep->arg[1]; - if (group_name_arg.empty()) { + std::string botgroup_name_arg = sep->arg[1]; + if (botgroup_name_arg.empty()) { c->Message(m_fail, "You must specify the [name] of a bot-group to load to use this command"); return; } - if (!botdb.DoesBotGroupExist(group_name_arg)) { - c->Message(m_fail, "Bot-group %s does not exist", group_name_arg.c_str()); + + bool extant_flag = false; + if (!botdb.QueryBotGroupExistence(botgroup_name_arg, extant_flag)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::QueryBotGroupExistence(), botgroup_name_arg.c_str()); + return; + } + if (!extant_flag) { + c->Message(m_fail, "Bot-group %s does not exist", botgroup_name_arg.c_str()); return; } @@ -5699,33 +5739,28 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep) } } - std::string error_message; - uint32 group_id = botdb.GetGroupIDForLoadGroup(c->CharacterID(), group_name_arg, error_message); - if (!group_id) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Can not resolve bot-group %s to a valid id", group_name_arg.c_str()); + uint32 botgroup_id = 0; + if (!botdb.LoadBotGroupIDForLoadBotGroup(c->CharacterID(), botgroup_name_arg, botgroup_id) || !botgroup_id) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotGroupIDForLoadBotGroup(), botgroup_name_arg.c_str()); return; } - std::map> group_list = botdb.LoadGroup(group_name_arg, error_message); - if (group_list.empty()) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Database returned an empty list for got-group %s", group_name_arg.c_str()); + std::map> member_list; + if (!botdb.LoadBotGroup(botgroup_name_arg, member_list)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadBotGroup(), botgroup_name_arg.c_str()); + return; + } + if (member_list.find(botgroup_id) == member_list.end() || member_list[botgroup_id].empty()) { + c->Message(m_fail, "Database returned an empty list for bot-group '%s'", botgroup_name_arg.c_str()); return; } - int spawned_bot_count = Bot::SpawnedBotCount(c->CharacterID(), &error_message); - if (!error_message.empty()) { - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - return; - } + int spawned_bot_count = Bot::SpawnedBotCount(c->CharacterID()); if (RuleB(Bots, QuestableSpawnLimit)) { - const int allowed_bot_count = Bot::AllowedBotSpawns(c->CharacterID(), &error_message); - if (!error_message.empty()) { - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); + int allowed_bot_count = 0; + if (!botdb.LoadQuestableSpawnCount(c->CharacterID(), allowed_bot_count)) { + c->Message(m_fail, "%s", BotDatabase::fail::LoadQuestableSpawnCount()); return; } @@ -5734,63 +5769,59 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep) return; } - if (spawned_bot_count >= allowed_bot_count || (spawned_bot_count + group_list.begin()->second.size()) > allowed_bot_count) { + if (spawned_bot_count >= allowed_bot_count || (spawned_bot_count + member_list.begin()->second.size()) > allowed_bot_count) { c->Message(m_fail, "You can not spawn more than %i bot%s (quest-limit)", allowed_bot_count, ((allowed_bot_count == 1) ? ("") : ("s"))); return; } } const int allowed_bot_limit = RuleI(Bots, SpawnLimit); - if (spawned_bot_count >= allowed_bot_limit || (spawned_bot_count + group_list.begin()->second.size()) > allowed_bot_limit) { + if (spawned_bot_count >= allowed_bot_limit || (spawned_bot_count + member_list.begin()->second.size()) > allowed_bot_limit) { c->Message(m_fail, "You can not spawn more than %i bot%s (hard-limit)", allowed_bot_limit, ((allowed_bot_limit == 1) ? ("") : ("s"))); return; } - uint32 leader_id = botdb.GetLeaderIDByGroupName(group_name_arg, error_message); + uint32 leader_id = 0; + if (!botdb.LoadLeaderIDByBotGroupName(botgroup_name_arg, leader_id)) { + c->Message(m_fail, "%s for '%s'", BotDatabase::fail::LoadLeaderIDByBotGroupName(), botgroup_name_arg.c_str()); + return; + } if (!leader_id) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Can not locate bot-group leader id for %s", group_name_arg.c_str()); + c->Message(m_fail, "Can not locate bot-group leader id for '%s'", botgroup_name_arg.c_str()); return; } - auto group_leader = Bot::LoadBot(leader_id, &error_message); - if (!group_leader) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Could not spawn bot-group leader for %s", group_name_arg.c_str()); - safe_delete(group_leader); + auto botgroup_leader = Bot::LoadBot(leader_id); + if (!botgroup_leader) { + c->Message(m_fail, "Could not spawn bot-group leader for '%s'", botgroup_name_arg.c_str()); + safe_delete(botgroup_leader); return; } - group_leader->Spawn(c, &error_message); - // No error_message code in Bot::Spawn() + botgroup_leader->Spawn(c); - Group* group_inst = new Group(group_leader); + Group* group_inst = new Group(botgroup_leader); entity_list.AddGroup(group_inst); - database.SetGroupID(group_leader->GetCleanName(), group_inst->GetID(), group_leader->GetBotID()); - database.SetGroupLeaderName(group_inst->GetID(), group_leader->GetCleanName()); - group_leader->SetFollowID(c->GetID()); + database.SetGroupID(botgroup_leader->GetCleanName(), group_inst->GetID(), botgroup_leader->GetBotID()); + database.SetGroupLeaderName(group_inst->GetID(), botgroup_leader->GetCleanName()); + botgroup_leader->SetFollowID(c->GetID()); - group_list[group_id].remove(0); - group_list[group_id].remove(group_leader->GetBotID()); - for (auto member_iter : group_list[group_id]) { - auto group_member = Bot::LoadBot(member_iter, &error_message); - if (!group_member) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); + member_list[botgroup_id].remove(0); + member_list[botgroup_id].remove(botgroup_leader->GetBotID()); + for (auto member_iter : member_list[botgroup_id]) { + auto botgroup_member = Bot::LoadBot(member_iter); + if (!botgroup_member) { c->Message(m_fail, "Could not load bot id %i", member_iter); - safe_delete(group_member); + safe_delete(botgroup_member); return; } - group_member->Spawn(c, &error_message); - // No error_message code in Bot::Spawn() - Bot::AddBotToGroup(group_member, group_inst); + botgroup_member->Spawn(c); + Bot::AddBotToGroup(botgroup_member, group_inst); } - c->Message(m_action, "Successfully loaded bot-group %s", group_name_arg.c_str()); + c->Message(m_action, "Successfully loaded bot-group %s", botgroup_name_arg.c_str()); } void bot_subcommand_botgroup_remove_member(Client *c, const Seperator *sep) @@ -5827,11 +5858,8 @@ void bot_subcommand_botgroup_remove_member(Client *c, const Seperator *sep) return; } - std::string error_message; - if (!botdb.RemoveMemberFromBotGroup(group_member->GetBotID(), error_message)) { - if (!error_message.empty()) - c->Message(m_unknown, "Database Error: %s", error_message.c_str()); - c->Message(m_fail, "Could not remove %s from their bot-group", group_member->GetCleanName()); + if (!botdb.RemoveMemberFromBotGroup(group_member->GetBotID())) { + c->Message(m_fail, "%s - '%s'", BotDatabase::fail::RemoveMemberFromBotGroup(), group_member->GetCleanName()); return; } @@ -6786,13 +6814,6 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) return; } - std::string TempErrorMessage; - my_bot->GetBotItemsCount(&TempErrorMessage); // database check to avoid false 'vacancy' reporting? - if (!TempErrorMessage.empty()) { - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); - return; - } - const ItemInst* inst = nullptr; const Item_Struct* item = nullptr; bool is2Hweapon = false; @@ -6801,6 +6822,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) Client::TextLink linker; linker.SetLinkType(linker.linkItemInst); + uint32 inventory_count = 0; for (int i = EmuConstants::EQUIPMENT_BEGIN; i <= (EmuConstants::EQUIPMENT_END + 1); ++i) { if ((i == MainSecondary) && is2Hweapon) continue; @@ -6819,7 +6841,16 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep) linker.SetItemInst(inst); item_link = linker.GenerateLink(); c->Message(m_message, "Using %s in my %s (slot %i)", item_link.c_str(), GetBotEquipSlotName(i), (i == 22 ? 9999 : i)); + + ++inventory_count; } + + uint32 database_count = 0; + if (!botdb.QueryInventoryCount(my_bot->GetBotID(), database_count)) + c->Message(m_unknown, "%s", BotDatabase::fail::QueryInventoryCount()); + + if (inventory_count != database_count) + c->Message(m_unknown, "Inventory-database item count mismatch: inv = '%u', db = '%u'", inventory_count, database_count); } void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) @@ -6847,13 +6878,6 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) return; } - std::string TempErrorMessage; - my_bot->GetBotItemsCount(&TempErrorMessage); // added same check as in bot_subcommand_inventory_list() - same note - if (!TempErrorMessage.empty()) { - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); - return; - } - int slotId = atoi(sep->arg[1]); if (!sep->IsNumber(1) || ((slotId > EmuConstants::EQUIPMENT_END || slotId < EmuConstants::EQUIPMENT_BEGIN) && slotId != 9999)) { c->Message(m_fail, "Valid slots are 0-21 or 9999"); @@ -6884,14 +6908,15 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep) return; } + std::string error_message; if (itm) { c->PushItemOnCursor(*itminst, true); if ((slotId == MainRange) || (slotId == MainAmmo) || (slotId == MainPrimary) || (slotId == MainSecondary)) my_bot->SetBotArcher(false); - my_bot->RemoveBotItemBySlot(slotId, &TempErrorMessage); - if (!TempErrorMessage.empty()) { - c->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); + my_bot->RemoveBotItemBySlot(slotId, &error_message); + if (!error_message.empty()) { + c->Message(m_unknown, "Database Error: %s", error_message.c_str()); return; } @@ -7183,11 +7208,12 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } - std::string TempErrorMessage; - - if (!Bot::IsBotNameAvailable(bot_name.c_str(), &TempErrorMessage)) { - if (!TempErrorMessage.empty()) - bot_owner->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); + bool available_flag = false; + if (!botdb.QueryNameAvailablity(bot_name, available_flag)) { + bot_owner->Message(m_fail, "%s for '%s'", BotDatabase::fail::QueryNameAvailablity(), bot_name.c_str()); + return bot_id; + } + if (!available_flag) { bot_owner->Message(m_fail, "The name %s is already being used. Please choose a different name", bot_name.c_str()); return bot_id; } @@ -7203,11 +7229,15 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } - uint32 mbc = RuleI(Bots, CreationLimit); - if (Bot::CreatedBotCount(bot_owner->CharacterID(), &TempErrorMessage) >= mbc) { - if (!TempErrorMessage.empty()) - bot_owner->Message(m_unknown, "Database Error: %s", TempErrorMessage.c_str()); - bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots", mbc); + uint32 max_bot_count = RuleI(Bots, CreationLimit); + + uint32 bot_count = 0; + if (!botdb.QueryBotCount(bot_owner->CharacterID(), bot_count)) { + bot_owner->Message(m_fail, "%s", BotDatabase::fail::QueryBotCount()); + return bot_id; + } + if (bot_count >= max_bot_count) { + bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots", max_bot_count); return bot_id; } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 3e7e18c1e..d031ee0cf 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -26,12 +26,13 @@ #include "bot_database.h" #include "bot.h" + BotDatabase botdb; BotDatabase::BotDatabase() { - + } BotDatabase::BotDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port) @@ -57,11 +58,11 @@ bool BotDatabase::Connect(const char* host, const char* user, const char* passwd } } -bool BotDatabase::GetCommandSettings(std::map>> &bot_command_settings) +bool BotDatabase::LoadBotCommandSettings(std::map>> &bot_command_settings) { bot_command_settings.clear(); - std::string query = "SELECT `bot_command`, `access`, `aliases` FROM `bot_command_settings`"; + query = "SELECT `bot_command`, `access`, `aliases` FROM `bot_command_settings`"; auto results = QueryDatabase(query); if (!results.Success()) return false; @@ -82,57 +83,1664 @@ bool BotDatabase::GetCommandSettings(std::map 15 || !database.CheckNameFilter(bot_name.c_str()) || !database.CheckUsedName(bot_name.c_str())) + return false; + + query = StringFormat("SELECT `id` FROM `vw_bot_character_mobs` WHERE `name` LIKE '%s' LIMIT 1", bot_name.c_str()); auto results = QueryDatabase(query); if (!results.Success()) return false; + if (results.RowCount()) + return true; + + available_flag = true; + + return true; +} + +bool BotDatabase::QueryBotCount(const uint32 owner_id, uint32& bot_count) +{ + if (!owner_id) + return false; + + query = StringFormat("SELECT COUNT(`bot_id`) FROM `bot_data` WHERE `owner_id` = '%i'", owner_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + bot_count = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadQuestableSpawnCount(const uint32 owner_id, int& spawn_count) +{ + if (!owner_id) + return false; + + query = StringFormat("SELECT `value` FROM `quest_globals` WHERE `name` = 'bot_spawn_limit' AND `charid` = '%i' LIMIT 1", owner_id); + auto results = database.QueryDatabase(query); // use 'database' for non-bot table calls + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; auto row = results.begin(); - memset(message, '\0', sizeof(InspectMessage_Struct)); + spawn_count = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list& bots_list) +{ + if (!owner_id) + return false; + + query = StringFormat("SELECT `bot_id`, `name`, `class`, `level`, `race`, `gender` FROM `bot_data` WHERE `owner_id` = '%u'", owner_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + for (auto row = results.begin(); row != results.end(); ++row) { - memcpy(message, row[0], sizeof(InspectMessage_Struct)); + BotsAvailableList bot_entry; + + bot_entry.ID = atoi(row[0]); + + memset(&bot_entry.Name, 0, sizeof(bot_entry.Name)); + std::string bot_name = row[1]; + if (bot_name.size() > 63) + bot_name = bot_name.substr(0, 63); + if (!bot_name.empty()) + strcpy(bot_entry.Name, bot_name.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]); + + bots_list.push_back(bot_entry); } return true; } -bool BotDatabase::SetInspectMessage(uint32 bot_id, const InspectMessage_Struct* message) +bool BotDatabase::LoadOwnerID(const std::string& bot_name, uint32& owner_id) { - std::string query = StringFormat("REPLACE INTO `bot_inspect_messages` (bot_id, inspect_message) VALUES (%u, '%s')", bot_id, EscapeString(message->text).c_str()); - auto results = QueryDatabase(query); + if (bot_name.empty()) + return false; - return results.Success(); + query = StringFormat("SELECT `owner_id` FROM `bot_data` WHERE `name` = '%s' LIMIT 1", bot_name.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + owner_id = atoi(row[0]); + + return true; } -bool BotDatabase::SetAllInspectMessages(uint32 owner_id, const InspectMessage_Struct* message) +bool BotDatabase::LoadOwnerID(const uint32 bot_id, uint32& owner_id) { - std::string query = StringFormat( - "UPDATE `bot_inspect_messages`" - " SET `inspect_message` = '%s'" - " WHERE `bot_id`" - " IN (SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u')", - EscapeString(message->text).c_str(), owner_id + if (!bot_id) + return false; + + query = StringFormat("SELECT `owner_id` FROM `bot_data` WHERE `bot_id` = '%u' LIMIT 1", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + owner_id = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadBotID(const uint32 owner_id, const std::string& bot_name, uint32& bot_id) +{ + if (!owner_id || bot_name.empty()) + return false; + + query = StringFormat("SELECT `bot_id` FROM `bot_data` WHERE `name` = '%s' LIMIT 1", bot_name.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + bot_id = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) +{ + if (!bot_id || loaded_bot) + return false; + + query = StringFormat( + "SELECT" + " `owner_id`," + " `spells_id`," + " `name`," + " `last_name`," + " `title`," /* planned use[4] */ + " `suffix`," /* planned use[5] */ + " `zone_id`," + " `gender`," + " `race`," + " `class`," + " `level`," + " `deity`," /* planned use[11] */ + " `creation_day`," /* not in-use[12] */ + " `last_spawn`," /* not in-use[13] */ + " `time_spawned`," + " `size`," + " `face`," + " `hair_color`," + " `hair_style`," + " `beard`," + " `beard_color`," + " `eye_color_1`," + " `eye_color_2`," + " `drakkin_heritage`," + " `drakkin_tattoo`," + " `drakkin_details`," + " `ac`," /* not in-use[26] */ + " `atk`," /* not in-use[27] */ + " `hp`," + " `mana`," + " `str`," /* not in-use[30] */ + " `sta`," /* not in-use[31] */ + " `cha`," /* not in-use[32] */ + " `dex`," /* not in-use[33] */ + " `int`," /* not in-use[34] */ + " `agi`," /* not in-use[35] */ + " `wis`," /* not in-use[36] */ + " `fire`," /* not in-use[37] */ + " `cold`," /* not in-use[38] */ + " `magic`," /* not in-use[39] */ + " `poison`," /* not in-use[40] */ + " `disease`," /* not in-use[41] */ + " `corruption`," /* not in-use[42] */ + " `show_helm`," // 43 + " `follow_distance`" // 44 + " FROM `bot_data`" + " WHERE `bot_id` = '%u'" + " LIMIT 1", + bot_id + ); + + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + // TODO: Consider removing resists and basic attributes from the load query above since we're using defaultNPCType values instead + auto row = results.begin(); + NPCType defaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[10]), atoi(row[8]), atoi(row[9]), atoi(row[7])); + NPCType tempNPCStruct = Bot::FillNPCTypeStruct( + atoi(row[1]), + std::string(row[2]), + std::string(row[3]), + atoi(row[10]), + atoi(row[8]), + atoi(row[9]), + atoi(row[7]), + atof(row[15]), + atoi(row[16]), + atoi(row[18]), + atoi(row[17]), + atoi(row[21]), + atoi(row[22]), + atoi(row[20]), + atoi(row[19]), + atoi(row[23]), + atoi(row[24]), + atoi(row[25]), + atoi(row[28]), + atoi(row[29]), + defaultNPCTypeStruct.MR, + defaultNPCTypeStruct.CR, + defaultNPCTypeStruct.DR, + defaultNPCTypeStruct.FR, + defaultNPCTypeStruct.PR, + defaultNPCTypeStruct.Corrup, + defaultNPCTypeStruct.AC, + defaultNPCTypeStruct.STR, + defaultNPCTypeStruct.STA, + defaultNPCTypeStruct.DEX, + defaultNPCTypeStruct.AGI, + defaultNPCTypeStruct.INT, + defaultNPCTypeStruct.WIS, + defaultNPCTypeStruct.CHA, + defaultNPCTypeStruct.ATK + ); + + 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->SetFollowDistance(atoi(row[44])); + } + + return true; +} + +bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id) +{ + if (!bot_inst) + return false; + + query = StringFormat( + "INSERT INTO `bot_data` (" + " `owner_id`," + " `spells_id`," + " `name`," + " `last_name`," + " `zone_id`," + " `gender`," + " `race`," + " `class`," + " `level`," + " `creation_day`," + " `last_spawn`," + " `time_spawned`," + " `size`," + " `face`," + " `hair_color`," + " `hair_style`," + " `beard`," + " `beard_color`," + " `eye_color_1`," + " `eye_color_2`," + " `drakkin_heritage`," + " `drakkin_tattoo`," + " `drakkin_details`," + " `ac`," + " `atk`," + " `hp`," + " `mana`," + " `str`," + " `sta`," + " `cha`," + " `dex`," + " `int`," + " `agi`," + " `wis`," + " `fire`," + " `cold`," + " `magic`," + " `poison`," + " `disease`," + " `corruption`," + " `show_helm`," + " `follow_distance`" + ")" + " VALUES (" + "'%u'," /* owner_id */ + " '%u'," /* spells_id */ + " '%s'," /* name */ + " '%s'," /* last_name */ + " '%i'," /* zone_id */ + " '%i'," /* gender */ + " '%i'," /* race */ + " '%i'," /* class */ + " '%u'," /* level */ + " UNIX_TIMESTAMP()," /* creation_day */ + " UNIX_TIMESTAMP()," /* last_spawn */ + " 0," /* time_spawned */ + " '%f'," /* size */ + " '%i'," /* face */ + " '%i'," /* hair_color */ + " '%i'," /* hair_style */ + " '%i'," /* beard */ + " '%i'," /* beard_color */ + " '%i'," /* eye_color_1 */ + " '%i'," /* eye_color_2 */ + " '%i'," /* drakkin_heritage */ + " '%i'," /* drakkin_tattoo */ + " '%i'," /* drakkin_details */ + " '%i'," /* ac */ + " '%i'," /* atk */ + " '%i'," /* hp */ + " '%i'," /* mana */ + " '%i'," /* str */ + " '%i'," /* sta */ + " '%i'," /* cha */ + " '%i'," /* dex */ + " '%i'," /* int */ + " '%i'," /* agi */ + " '%i'," /* wis */ + " '%i'," /* fire */ + " '%i'," /* cold */ + " '%i'," /* magic */ + " '%i'," /* poison */ + " '%i'," /* disease */ + " '%i'," /* corruption */ + " '1'," /* show_helm */ + " '%i'" /* follow_distance */ + ")", + bot_inst->GetBotOwnerCharacterID(), + bot_inst->GetBotSpellID(), + bot_inst->GetCleanName(), + bot_inst->GetLastName(), + bot_inst->GetLastZoneID(), + bot_inst->GetGender(), + bot_inst->GetRace(), + bot_inst->GetClass(), + bot_inst->GetLevel(), + bot_inst->GetSize(), + bot_inst->GetLuclinFace(), + bot_inst->GetHairColor(), + bot_inst->GetHairStyle(), + bot_inst->GetBeard(), + bot_inst->GetBeardColor(), + bot_inst->GetEyeColor1(), + bot_inst->GetEyeColor2(), + bot_inst->GetDrakkinHeritage(), + bot_inst->GetDrakkinTattoo(), + bot_inst->GetDrakkinDetails(), + bot_inst->GetAC(), + bot_inst->GetATK(), + bot_inst->GetHP(), + bot_inst->GetMana(), + bot_inst->GetSTR(), + bot_inst->GetSTA(), + bot_inst->GetCHA(), + bot_inst->GetDEX(), + bot_inst->GetINT(), + bot_inst->GetAGI(), + bot_inst->GetWIS(), + bot_inst->GetFR(), + bot_inst->GetCR(), + bot_inst->GetMR(), + bot_inst->GetPR(), + bot_inst->GetDR(), + bot_inst->GetCorrup(), + BOT_DEFAULT_FOLLOW_DISTANCE ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + bot_id = results.LastInsertedID(); + + return true; } -bool BotDatabase::SetAllArmorColorBySlot(uint32 owner_id, int16 slot_id, uint32 rgb_value) +bool BotDatabase::SaveBot(Bot* bot_inst) +{ + if (!bot_inst) + return false; + + query = StringFormat( + "UPDATE `bot_data`" + " SET" + " `owner_id` = '%u'," + " `spells_id` = '%u'," + " `name` = '%s'," + " `last_name` = '%s'," + " `zone_id` = '%i'," + " `gender` = '%i'," + " `race` = '%i'," + " `class` = '%i'," + " `level` = '%u'," + " `last_spawn` = UNIX_TIMESTAMP()," + " `time_spawned` = '%u'," + " `size` = '%f'," + " `face` = '%i'," + " `hair_color` = '%i'," + " `hair_style` = '%i'," + " `beard` = '%i'," + " `beard_color` = '%i'," + " `eye_color_1` = '%i'," + " `eye_color_2` = '%i'," + " `drakkin_heritage` = '%i'," + " `drakkin_tattoo` = '%i'," + " `drakkin_details` = '%i'," + " `ac` = '%i'," + " `atk` = '%i'," + " `hp` = '%i'," + " `mana` = '%i'," + " `str` = '%i'," + " `sta` = '%i'," + " `cha` = '%i'," + " `dex` = '%i'," + " `int` = '%i'," + " `agi` = '%i'," + " `wis` = '%i'," + " `fire` = '%i'," + " `cold` = '%i'," + " `magic` = '%i'," + " `poison` = '%i'," + " `disease` = '%i'," + " `corruption` = '%i'," + " `show_helm` = '%i'," + " `follow_distance` = '%i'" + " WHERE `bot_id` = '%u'", + bot_inst->GetBotOwnerCharacterID(), + bot_inst->GetBotSpellID(), + bot_inst->GetCleanName(), + bot_inst->GetLastName(), + bot_inst->GetLastZoneID(), + bot_inst->GetBaseGender(), + bot_inst->GetBaseRace(), + bot_inst->GetClass(), + bot_inst->GetLevel(), + bot_inst->GetTotalPlayTime(), + bot_inst->GetBaseSize(), + bot_inst->GetLuclinFace(), + bot_inst->GetHairColor(), + bot_inst->GetHairStyle(), + bot_inst->GetBeard(), + bot_inst->GetBeardColor(), + bot_inst->GetEyeColor1(), + bot_inst->GetEyeColor2(), + bot_inst->GetDrakkinHeritage(), + bot_inst->GetDrakkinTattoo(), + bot_inst->GetDrakkinDetails(), + bot_inst->GetBaseAC(), + bot_inst->GetBaseATK(), + bot_inst->GetHP(), + bot_inst->GetMana(), + bot_inst->GetBaseSTR(), + bot_inst->GetBaseSTA(), + bot_inst->GetBaseCHA(), + bot_inst->GetBaseDEX(), + bot_inst->GetBaseINT(), + bot_inst->GetBaseAGI(), + bot_inst->GetBaseWIS(), + bot_inst->GetBaseFR(), + bot_inst->GetBaseCR(), + bot_inst->GetBaseMR(), + bot_inst->GetBasePR(), + bot_inst->GetBaseDR(), + bot_inst->GetBaseCorrup(), + ((bot_inst->GetShowHelm()) ? (1) : (0)), + bot_inst->GetFollowDistance(), + bot_inst->GetBotID() + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::DeleteBot(const uint32 bot_id) +{ + if (!bot_id) + return false; + + query = StringFormat("DELETE FROM `bot_data` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadBuffs(Bot* bot_inst) +{ + if (!bot_inst) + return false; + + query = StringFormat( + "SELECT" + " `spell_id`," + " `caster_level`," + " `duration_formula`," + " `tics_remaining`," + " `poison_counters`," + " `disease_counters`," + " `curse_counters`," + " `corruption_counters`," + " `numhits`," + " `melee_rune`," + " `magic_rune`," + " `dot_rune`," + " `persistent`," + " `caston_x`," + " `caston_y`," + " `caston_z`," + " `extra_di_chance`" + " FROM `bot_buffs`" + " WHERE `bot_id` = '%u'", + bot_inst->GetBotID() + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + Buffs_Struct* bot_buffs = bot_inst->GetBuffs(); + if (!bot_buffs) + return false; + + int buff_count = 0; + for (auto row = results.begin(); row != results.end() && buff_count < BUFF_COUNT; ++row) { + bot_buffs[buff_count].spellid = atoi(row[0]); + bot_buffs[buff_count].casterlevel = atoi(row[1]); + //row[2] (duration_formula) can probably be removed + bot_buffs[buff_count].ticsremaining = atoi(row[3]); + + if (CalculatePoisonCounters(bot_buffs[buff_count].spellid) > 0) + bot_buffs[buff_count].counters = atoi(row[4]); + else if (CalculateDiseaseCounters(bot_buffs[buff_count].spellid) > 0) + bot_buffs[buff_count].counters = atoi(row[5]); + else if (CalculateCurseCounters(bot_buffs[buff_count].spellid) > 0) + bot_buffs[buff_count].counters = atoi(row[6]); + else if (CalculateCorruptionCounters(bot_buffs[buff_count].spellid) > 0) + bot_buffs[buff_count].counters = atoi(row[7]); + + bot_buffs[buff_count].numhits = atoi(row[8]); + bot_buffs[buff_count].melee_rune = atoi(row[9]); + bot_buffs[buff_count].magic_rune = atoi(row[10]); + bot_buffs[buff_count].dot_rune = atoi(row[11]); + bot_buffs[buff_count].persistant_buff = ((atoi(row[12])) ? (true) : (false)); + bot_buffs[buff_count].caston_x = atoi(row[13]); + bot_buffs[buff_count].caston_y = atoi(row[14]); + bot_buffs[buff_count].caston_z = atoi(row[15]); + bot_buffs[buff_count].ExtraDIChance = atoi(row[16]); + bot_buffs[buff_count].casterid = 0; + ++buff_count; + } + + return true; +} + +bool BotDatabase::SaveBuffs(Bot* bot_inst) +{ + if (!bot_inst) + return false; + + if (!DeleteBuffs(bot_inst->GetBotID())) + return false; + + Buffs_Struct* bot_buffs = bot_inst->GetBuffs(); + if (!bot_buffs) + return false; + + for (int buff_index = 0; buff_index < BUFF_COUNT; ++buff_index) { + if (bot_buffs[buff_index].spellid <= 0 || bot_buffs[buff_index].spellid == SPELL_UNKNOWN) + continue; + + query = StringFormat( + "INSERT INTO `bot_buffs` (" + "`bot_id`," + " `spell_id`," + " `caster_level`," + " `duration_formula`," + " `tics_remaining`," + " `poison_counters`," + " `disease_counters`," + " `curse_counters`," + " `corruption_counters`," + " `numhits`," + " `melee_rune`," + " `magic_rune`," + " `dot_rune`," + " `persistent`," + " `caston_x`," + " `caston_y`," + " `caston_z`," + " `extra_di_chance`" + ")" + " VALUES (" + "'%u'," /* bot_id */ + " '%u'," /* spell_id */ + " '%u'," /* caster_level */ + " '%u'," /* duration_formula */ + " '%u'," /* tics_remaining */ + " '%u'," /* poison_counters */ + " '%u'," /* disease_counters */ + " '%u'," /* curse_counters */ + " '%u'," /* corruption_counters */ + " '%u'," /* numhits */ + " '%u'," /* melee_rune */ + " '%u'," /* magic_rune */ + " '%u'," /* dot_rune */ + " '%u'," /* persistent */ + " '%i'," /* caston_x */ + " '%i'," /* caston_y */ + " '%i'," /* caston_z */ + " '%i'" /* extra_di_chance */ + ")", + bot_inst->GetBotID(), + bot_buffs[buff_index].spellid, + bot_buffs[buff_index].casterlevel, + spells[bot_buffs[buff_index].spellid].buffdurationformula, + bot_buffs[buff_index].ticsremaining, + ((CalculatePoisonCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), + ((CalculateDiseaseCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), + ((CalculateCurseCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), + ((CalculateCorruptionCounters(bot_buffs[buff_index].spellid) > 0) ? (bot_buffs[buff_index].counters) : (0)), + bot_buffs[buff_index].numhits, + bot_buffs[buff_index].melee_rune, + bot_buffs[buff_index].magic_rune, + bot_buffs[buff_index].dot_rune, + ((bot_buffs[buff_index].persistant_buff) ? (1) : (0)), + bot_buffs[buff_index].caston_x, + bot_buffs[buff_index].caston_y, + bot_buffs[buff_index].caston_z, + bot_buffs[buff_index].ExtraDIChance + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteBuffs(bot_inst->GetBotID()); + return false; + } + } + + return true; +} + +bool BotDatabase::DeleteBuffs(const uint32 bot_id) +{ + if (!bot_id) + return false; + + query = StringFormat("DELETE FROM `bot_buffs` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadStance(const uint32 bot_id, BotStanceType& bot_stance) +{ + if (!bot_id) + return false; + + query = StringFormat("SELECT `stance_id` FROM `bot_stances` WHERE `bot_id` = '%u' LIMIT 1", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + bot_stance = (BotStanceType)atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadStance(Bot* bot_inst, bool& stance_flag) +{ + if (!bot_inst) + return false; + + bot_inst->SetDefaultBotStance(); + + query = StringFormat("SELECT `stance_id` FROM `bot_stances` WHERE `bot_id` = '%u' LIMIT 1", bot_inst->GetBotID()); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + bot_inst->SetBotStance((BotStanceType)atoi(row[0])); + stance_flag = true; + + return true; +} + +bool BotDatabase::SaveStance(const uint32 bot_id, const BotStanceType bot_stance) +{ + if (!bot_id) + return false; + + if (!DeleteStance(bot_id)) + return false; + + query = StringFormat("INSERT INTO `bot_stances` (`bot_id`, `stance_id`) VALUES ('%u', '%u')", bot_id, bot_stance); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteStance(bot_id); + return false; + } + + return true; +} + +bool BotDatabase::SaveStance(Bot* bot_inst) +{ + if (!bot_inst) + return false; + + if (!DeleteStance(bot_inst->GetBotID())) + return false; + + query = StringFormat("INSERT INTO `bot_stances` (`bot_id`, `stance_id`) VALUES ('%u', '%u')", bot_inst->GetBotID(), bot_inst->GetBotStance()); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteStance(bot_inst->GetBotID()); + return false; + } + + return true; +} + +bool BotDatabase::DeleteStance(const uint32 bot_id) +{ + if (!bot_id) + return false; + + query = StringFormat("DELETE FROM `bot_stances` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadTimers(Bot* bot_inst) +{ + if (!bot_inst) + return false; + + query = StringFormat( + "SELECT" + " IfNull(bt.`timer_id`, '0') As timer_id," + " IfNull(bt.`timer_value`, '0') As timer_value," + " IfNull(MAX(sn.`recast_time`), '0') AS MaxTimer" + " FROM `bot_timers` bt, `spells_new` sn" + " WHERE bt.`bot_id` = '%u' AND sn.`EndurTimerIndex` = (" + "SELECT case" + " WHEN timer_id > '%i' THEN timer_id - '%i'" + " ELSE timer_id END AS timer_id" + " FROM `bot_timers` WHERE `timer_id` = bt.`timer_id` AND `bot_id` = bt.`bot_id`" // double-check validity + ")" + " AND sn.`classes%i` <= '%i'", + bot_inst->GetBotID(), + (DisciplineReuseStart - 1), + (DisciplineReuseStart - 1), + bot_inst->GetClass(), + bot_inst->GetLevel() + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + uint32* bot_timers = bot_inst->GetTimers(); + if (!bot_timers) + return false; + + int timer_id = 0; + uint32 timer_value = 0; + uint32 max_value = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + timer_id = atoi(row[0]) - 1; + timer_value = atoi(row[1]); + max_value = atoi(row[2]); + + if (timer_id >= 0 && timer_id < MaxTimer && timer_value < (Timer::GetCurrentTime() + max_value)) + bot_timers[timer_id] = timer_value; + } + + return true; +} + +bool BotDatabase::SaveTimers(Bot* bot_inst) +{ + if (!bot_inst) + return false; + + if (!DeleteTimers(bot_inst->GetBotID())) + return false; + + uint32* bot_timers = bot_inst->GetTimers(); + if (!bot_timers) + return false; + + for (int timer_index = 0; timer_index < MaxTimer; ++timer_index) { + if (bot_timers[timer_index] <= Timer::GetCurrentTime()) + continue; + + query = StringFormat("INSERT INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES ('%u', '%u', '%u')", bot_inst->GetBotID(), (timer_index + 1), bot_timers[timer_index]); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteTimers(bot_inst->GetBotID()); + return false; + } + } + + return true; +} + +bool BotDatabase::DeleteTimers(const uint32 bot_id) +{ + if (!bot_id) + return false; + + query = StringFormat("DELETE FROM `bot_timers` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadGuildMembership(const uint32 bot_id, uint32& guild_id, uint8& guild_rank, std::string& guild_name) +{ + if (!bot_id) + return false; + + query = StringFormat( + "SELECT" + " gm.`guild_id`," + " gm.`rank`," + " g.`name`" + " FROM `vw_guild_members` AS gm" + " JOIN `guilds` AS g" + " ON gm.`guild_id` = g.`id`" + " WHERE gm.`char_id` = '%u'" + " AND gm.`mob_type` = 'B'" + " LIMIT 1", + bot_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + guild_id = atoi(row[0]); + guild_rank = atoi(row[1]); + guild_name = row[2]; + + return true; +} + +bool BotDatabase::SaveGuildMembership(const uint32 bot_id, const uint32 guild_id, const uint8 guild_rank) +{ + if (!bot_id || !guild_id) + return false; + + if (!DeleteGuildMembership(bot_id)) + return false; + + query = StringFormat("INSERT INTO `bot_guild_members` SET `bot_id` = '%u', `guild_id` = '%u', `rank` = '%u'", bot_id, guild_id, guild_rank); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteGuildMembership(bot_id); + return false; + } + + return true; +} + +bool BotDatabase::DeleteGuildMembership(const uint32 bot_id) +{ + if (!bot_id) + return false; + + query = StringFormat("DELETE FROM `bot_guild_members` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + + +/* Bot inventory functions */ +bool BotDatabase::QueryInventoryCount(const uint32 bot_id, uint32& item_count) +{ + if (!bot_id) + return false; + + query = StringFormat("SELECT COUNT(`inventories_index`) FROM `bot_inventories` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + item_count = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadItems(const uint32 bot_id, Inventory& inventory_inst) +{ + if (!bot_id) + return false; + + query = StringFormat( + "SELECT" + " `slot_id`," + " `item_id`," + " `inst_charges`," + " `inst_color`," + " `inst_no_drop`," + " `inst_custom_data`," + " `ornament_icon`," + " `ornament_id_file`," + " `ornament_hero_model`," + " `augment_1`," + " `augment_2`," + " `augment_3`," + " `augment_4`, " + " `augment_5`," + " `augment_6`" + " FROM `bot_inventories`" + " WHERE `bot_id` = '%i'" + " ORDER BY `slot_id`", + bot_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + if ((slot_id < EmuConstants::EQUIPMENT_BEGIN || slot_id > EmuConstants::EQUIPMENT_END) && slot_id != MainPowerSource) + continue; + + uint32 item_id = atoi(row[1]); + uint16 item_charges = (uint16)atoi(row[2]); + + ItemInst* item_inst = database.CreateItem( + item_id, + item_charges, + (uint32)atoul(row[9]), + (uint32)atoul(row[10]), + (uint32)atoul(row[11]), + (uint32)atoul(row[12]), + (uint32)atoul(row[13]), + (uint32)atoul(row[14]) + ); + if (!item_inst) { + Log.Out(Logs::General, Logs::Error, "Warning: bot_id '%i' has an invalid item_id '%i' in inventory slot '%i'", bot_id, item_id, slot_id); + continue; + } + + if (item_charges == 255) + item_inst->SetCharges(-1); + else + item_inst->SetCharges(item_charges); + + uint32 item_color = atoul(row[3]); + if (item_color > 0) + item_inst->SetColor(item_color); + + if (item_inst->GetItem()->Attuneable) { + if (atoi(row[4])) + item_inst->SetAttuned(true); + else if (((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) || slot_id == 9999)) + item_inst->SetAttuned(true); + } + + if (row[5]) { + std::string data_str(row[5]); + std::string idAsString; + std::string value; + bool use_id = true; + + for (int i = 0; i < data_str.length(); ++i) { + if (data_str[i] == '^') { + if (!use_id) { + item_inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } + + use_id = !use_id; + continue; + } + + char v = data_str[i]; + if (use_id) + idAsString.push_back(v); + else + value.push_back(v); + } + } + + item_inst->SetOrnamentIcon((uint32)atoul(row[6])); + item_inst->SetOrnamentationIDFile((uint32)atoul(row[7])); + item_inst->SetOrnamentHeroModel((uint32)atoul(row[8])); + + if (inventory_inst.PutItem(slot_id, *item_inst) == INVALID_INDEX) + Log.Out(Logs::General, Logs::Error, "Warning: Invalid slot_id for item in inventory: bot_id = '%i', item_id = '%i', slot_id = '%i'", bot_id, item_id, slot_id); + + safe_delete(item_inst); + } + + return true; +} + +bool BotDatabase::SaveItems(Bot* bot_inst) +{ + return false; +} + +bool BotDatabase::DeleteItems(const uint32 bot_id) +{ + if (!bot_id) + return false; + + query = StringFormat("DELETE FROM `bot_inventories` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadItemBySlot(Bot* bot_inst) +{ + return false; +} + +bool BotDatabase::LoadItemBySlot(const uint32 bot_id, const uint32 slot_id, uint32& item_id) +{ + if (!bot_id || (slot_id > EmuConstants::EQUIPMENT_END && slot_id != MainPowerSource)) + return false; + + query = StringFormat("SELECT `item_id` FROM `bot_inventories` WHERE `bot_id` = '%i' AND `slot_id` = '%i' LIMIT 1", bot_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + item_id = atoi(row[0]); + + return true; +} + +bool BotDatabase::SaveItemBySlot(Bot* bot_inst, const uint32 slot_id, const ItemInst* item_inst) +{ + if (!bot_inst || !bot_inst->GetBotID() || (slot_id > EmuConstants::EQUIPMENT_END && slot_id != MainPowerSource)) + return false; + + if (!DeleteItemBySlot(bot_inst->GetBotID(), slot_id)) + return false; + + if (!item_inst || !item_inst->GetID()) + return true; + + uint32 augment_id[] = { 0, 0, 0, 0, 0, 0 }; + for (int augment_iter = 0; augment_iter < EmuConstants::ITEM_COMMON_SIZE; ++augment_iter) + augment_id[augment_iter] = item_inst->GetAugmentItemID(augment_iter); + + query = StringFormat( + "INSERT INTO `bot_inventories` (" + "`bot_id`," + " `slot_id`," + " `item_id`," + " `inst_charges`," + " `inst_color`," + " `inst_no_drop`," + " `inst_custom_data`," + " `ornament_icon`," + " `ornament_id_file`," + " `ornament_hero_model`," + " `augment_1`," + " `augment_2`," + " `augment_3`," + " `augment_4`," + " `augment_5`," + " `augment_6`" + ")" + " VALUES (" + "'%lu'," /* bot_id */ + " '%lu'," /* slot_id */ + " '%lu'," /* item_id */ + " '%lu'," /* inst_charges */ + " '%lu'," /* inst_color */ + " '%lu'," /* inst_no_drop */ + " '%s'," /* inst_custom_data */ + " '%lu'," /* ornament_icon */ + " '%lu'," /* ornament_id_file */ + " '%lu'," /* ornament_hero_model */ + " '%lu'," /* augment_1 */ + " '%lu'," /* augment_2 */ + " '%lu'," /* augment_3 */ + " '%lu'," /* augment_4 */ + " '%lu'," /* augment_5 */ + " '%lu'" /* augment_6 */ + ")", + (unsigned long)bot_inst->GetBotID(), + (unsigned long)slot_id, + (unsigned long)item_inst->GetID(), + (unsigned long)item_inst->GetCharges(), + (unsigned long)item_inst->GetColor(), + (unsigned long)(item_inst->IsAttuned() ? 1 : 0), + item_inst->GetCustomDataString().c_str(), + (unsigned long)item_inst->GetOrnamentationIcon(), + (unsigned long)item_inst->GetOrnamentationIDFile(), + (unsigned long)item_inst->GetOrnamentHeroModel(), + (unsigned long)augment_id[0], + (unsigned long)augment_id[1], + (unsigned long)augment_id[2], + (unsigned long)augment_id[3], + (unsigned long)augment_id[4], + (unsigned long)augment_id[5] + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteItemBySlot(bot_inst->GetBotID(), slot_id); + return false; + } + + return true; +} + +bool BotDatabase::DeleteItemBySlot(const uint32 bot_id, const uint32 slot_id) +{ + if (!bot_id || (slot_id > EmuConstants::EQUIPMENT_END && slot_id != MainPowerSource)) + return false; + + query = StringFormat("DELETE FROM `bot_inventories` WHERE `bot_id` = '%u' AND `slot_id` = '%u'", bot_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadEquipmentColor(const uint32 bot_id, const uint8 material_slot_id, uint32& rgb) +{ + if (!bot_id) + return false; + + int16 slot_id = Inventory::CalcSlotFromMaterial(material_slot_id); + if (slot_id == INVALID_INDEX) + return false; + + query = StringFormat("SELECT `inst_color` FROM `bot_inventories` WHERE `bot_id` = '%u' AND `slot_id` = '%u' LIMIT 1", bot_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + rgb = atoul(row[0]); + + return true; +} + +bool BotDatabase::SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, const uint32 rgb) +{ + if (!bot_id) + return false; + + bool all_flag = (slot_id == -2); + if ((slot_id < EmuConstants::EQUIPMENT_BEGIN || slot_id > EmuConstants::EQUIPMENT_END) && slot_id != MainPowerSource && !all_flag) + return false; + + std::string where_clause; + if (all_flag) + where_clause = StringFormat(" AND `slot_id` IN ('%u', '%u', '%u', '%u', '%u', '%u', '%u')", MainHead, MainArms, MainWrist1, MainHands, MainChest, MainLegs, MainFeet); + else + where_clause = StringFormat(" AND `slot_id` = '%u'", slot_id); + + query = StringFormat( + "UPDATE `bot_inventories`" + " SET `inst_color` = '%u'" + " WHERE `bot_id` = '%u'" + " %s", + rgb, + where_clause.c_str(), + bot_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + + +/* Bot pet functions */ +bool BotDatabase::LoadPetIndex(const uint32 bot_id, uint32& pet_index) +{ + if (!bot_id) + return false; + + query = StringFormat("SELECT `pets_index` FROM `bot_pets` WHERE `bot_id` = '%u' LIMIT 1", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + pet_index = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadPetSpellID(const uint32 bot_id, uint32& pet_spell_id) +{ + if (!bot_id) + return false; + + query = StringFormat("SELECT `spell_id` FROM `bot_pets` WHERE `bot_id` = '%u' LIMIT 1", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + pet_spell_id = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadPetStats(const uint32 bot_id, std::string& pet_name, uint32& pet_mana, uint32& pet_hp, uint32& pet_spell_id) +{ + if (!bot_id) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + query = StringFormat("SELECT `spell_id`, `name`, `mana`, `hp` FROM `bot_pets` WHERE `pets_index` = '%u' LIMIT 1", saved_pet_index); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + pet_spell_id = atoi(row[0]); + pet_name = row[1]; + pet_mana = atoi(row[2]); + pet_hp = atoi(row[3]); + + return true; +} + +bool BotDatabase::SavePetStats(const uint32 bot_id, const std::string& pet_name, const uint32 pet_mana, const uint32 pet_hp, const uint32 pet_spell_id) +{ + if (!bot_id || pet_name.empty() || !pet_spell_id || pet_spell_id > SPDAT_RECORDS) + return false; + + if (!DeletePetItems(bot_id)) + return false; + if (!DeletePetBuffs(bot_id)) + return false; + if (!DeletePetStats(bot_id)) + return false; + + query = StringFormat( + "INSERT INTO `bot_pets` (" + "`spell_id`," + " `bot_id`," + " `name`," + " `mana`," + " `hp`" + ")" + " VALUES (" + "'%u'," + " '%u'," + " '%u'," + " '%u'," + " '%u'" + ")", + pet_spell_id, + bot_id, + pet_name.c_str(), + pet_mana, + pet_hp + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeletePetStats(bot_id); + return false; + } + + return true; +} + +bool BotDatabase::DeletePetStats(const uint32 bot_id) +{ + if (!bot_id) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + query = StringFormat("DELETE FROM `bot_pets` WHERE `pets_index` = '%u'", saved_pet_index); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadPetBuffs(const uint32 bot_id, SpellBuff_Struct* pet_buffs) +{ + if (!bot_id) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + query = StringFormat("SELECT `spell_id`, `caster_level`, `duration` FROM `bot_pet_buffs` WHERE `pets_index` = '%u'", saved_pet_index); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + int buff_index = 0; + for (auto row = results.begin(); row != results.end() && buff_index < BUFF_COUNT; ++row) { + pet_buffs[buff_index].spellid = atoi(row[0]); + pet_buffs[buff_index].level = atoi(row[1]); + pet_buffs[buff_index].duration = atoi(row[2]); + + // Work around for loading the counters and setting them back to max. Need entry in DB for saved counters + if (CalculatePoisonCounters(pet_buffs[buff_index].spellid) > 0) + pet_buffs[buff_index].counters = CalculatePoisonCounters(pet_buffs[buff_index].spellid); + else if (CalculateDiseaseCounters(pet_buffs[buff_index].spellid) > 0) + pet_buffs[buff_index].counters = CalculateDiseaseCounters(pet_buffs[buff_index].spellid); + else if (CalculateCurseCounters(pet_buffs[buff_index].spellid) > 0) + pet_buffs[buff_index].counters = CalculateCurseCounters(pet_buffs[buff_index].spellid); + else if (CalculateCorruptionCounters(pet_buffs[buff_index].spellid) > 0) + pet_buffs[buff_index].counters = CalculateCorruptionCounters(pet_buffs[buff_index].spellid); + + ++buff_index; + } + + return true; +} + +bool BotDatabase::SavePetBuffs(const uint32 bot_id, const SpellBuff_Struct* pet_buffs, bool delete_flag) +{ + // Only use 'delete_flag' if not invoked after a botdb.SavePetStats() call + + if (!bot_id || !pet_buffs) + return false; + + if (delete_flag && !DeletePetBuffs(bot_id)) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + for (int buff_index = 0; buff_index < BUFF_COUNT; ++buff_index) { + if (!pet_buffs[buff_index].spellid || pet_buffs[buff_index].spellid == SPELL_UNKNOWN) + continue; + + query = StringFormat( + "INSERT INTO `bot_pet_buffs` (" + "`pets_index`," + " `spell_id`," + " `caster_level`," + " `duration`" + ")" + " VALUES (" + "'%u'," + " '%u'," + " '%u'," + " '%u'" + ")", + saved_pet_index, + pet_buffs[buff_index].spellid, + pet_buffs[buff_index].level, + pet_buffs[buff_index].duration + ); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeletePetBuffs(bot_id); + return false; + } + } + + return true; +} + +bool BotDatabase::DeletePetBuffs(const uint32 bot_id) +{ + if (!bot_id) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + query = StringFormat("DELETE FROM `bot_pet_buffs` WHERE `pets_index` = '%u'", saved_pet_index); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::LoadPetItems(const uint32 bot_id, uint32* pet_items) +{ + if (!bot_id || !pet_items) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + query = StringFormat("SELECT `item_id` FROM `bot_pet_inventories` WHERE `pets_index` = '%u'", saved_pet_index); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + int item_index = 0; + for (auto row = results.begin(); row != results.end() && item_index < EmuConstants::EQUIPMENT_SIZE; ++row) { + pet_items[item_index] = atoi(row[0]); + ++item_index; + } + + return true; +} + +bool BotDatabase::SavePetItems(const uint32 bot_id, const uint32* pet_items, bool delete_flag) +{ + // Only use 'delete_flag' if not invoked after a botdb.SavePetStats() call + + if (!bot_id || !pet_items) + return false; + + if (delete_flag && !DeletePetItems(bot_id)) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + for (int item_index = 0; item_index < EmuConstants::EQUIPMENT_SIZE; ++item_index) { + if (!pet_items[item_index]) + continue; + + query = StringFormat("INSERT INTO `bot_pet_inventories` (`pets_index`, `item_id`) VALUES ('%u', '%u')", saved_pet_index, pet_items[item_index]); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeletePetItems(bot_id); + return false; + } + } + + return true; +} + +bool BotDatabase::DeletePetItems(const uint32 bot_id) +{ + if (!bot_id) + return false; + + uint32 saved_pet_index = 0; + if (!LoadPetIndex(bot_id, saved_pet_index)) + return false; + if (!saved_pet_index) + return true; + + query = StringFormat("DELETE FROM `bot_pet_inventories` WHERE `pets_index` = '%u'", saved_pet_index); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + + +/* Bot command functions */ +bool BotDatabase::LoadInspectMessage(const uint32 bot_id, InspectMessage_Struct& inspect_message) +{ + if (!bot_id) + return false; + + query = StringFormat("SELECT `inspect_message` FROM `bot_inspect_messages` WHERE `bot_id` = '%u' LIMIT 1", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + std::string bot_message = row[0]; + if (bot_message.size() > 255) + bot_message = bot_message.substr(0, 255); + if (bot_message.empty()) + return true; + + memcpy(inspect_message.text, bot_message.c_str(), bot_message.size()); + + return true; +} + +bool BotDatabase::SaveInspectMessage(const uint32 bot_id, const InspectMessage_Struct& inspect_message) +{ + if (!bot_id) + return false; + + if (!DeleteInspectMessage(bot_id)) + return false; + + std::string bot_message = inspect_message.text; + if (bot_message.size() > 255) + bot_message = bot_message.substr(0, 255); + if (bot_message.empty()) + return true; + + query = StringFormat("INSERT INTO `bot_inspect_messages` (`bot_id`, `inspect_message`) VALUES ('%u', '%s')", bot_id, bot_message.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteInspectMessage(bot_id); + return false; + } + + return true; +} + +bool BotDatabase::DeleteInspectMessage(const uint32 bot_id) +{ + if (!bot_id) + return false; + + query = StringFormat("DELETE FROM `bot_inspect_messages` WHERE `bot_id` = '%u'", bot_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::SaveAllInspectMessages(const uint32 owner_id, const InspectMessage_Struct& inspect_message) { if (!owner_id) return false; - std::string query = StringFormat( + if (!DeleteAllInspectMessages(owner_id)) + return false; + + std::string bot_message = inspect_message.text; + if (bot_message.size() > 255) + bot_message = bot_message.substr(0, 255); + if (bot_message.empty()) + return true; + + query = StringFormat("INSERT INTO `bot_inspect_messages` (`bot_id`, `inspect_message`) SELECT `bot_id`, '%s' inspect_message FROM `bot_data` WHERE `owner_id` = '%u'", bot_message.c_str(), owner_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteAllInspectMessages(owner_id); + return false; + } + + return true; +} + +bool BotDatabase::DeleteAllInspectMessages(const uint32 owner_id) +{ + if (!owner_id) + return false; + + query = StringFormat("DELETE FROM `bot_inspect_messages` WHERE `bot_id` IN (SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u')", owner_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + return true; +} + +bool BotDatabase::SaveAllArmorColorBySlot(const uint32 owner_id, const int16 slot_id, const uint32 rgb_value) +{ + if (!owner_id) + return false; + + query = StringFormat( "UPDATE `bot_inventories` bi" " INNER JOIN `bot_data` bd" " ON bd.`owner_id` = '%u'" " SET bi.`inst_color` = '%u'" " WHERE bi.`bot_id` = bd.`bot_id`" - " AND bi.`slot_id` IN (%u, %u, %u, %u, %u, %u, %u, %u)" + " AND bi.`slot_id` IN ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')" " AND bi.`slot_id` = '%i'", owner_id, rgb_value, @@ -140,37 +1748,41 @@ bool BotDatabase::SetAllArmorColorBySlot(uint32 owner_id, int16 slot_id, uint32 slot_id ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -bool BotDatabase::SetAllArmorColors(uint32 owner_id, uint32 rgb_value) +bool BotDatabase::SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_value) { if (!owner_id) return false; - std::string query = StringFormat( + query = StringFormat( "UPDATE `bot_inventories` bi" " INNER JOIN `bot_data` bd" " ON bd.`owner_id` = '%u'" " SET bi.`inst_color` = '%u'" " WHERE bi.`bot_id` = bd.`bot_id`" - " AND bi.`slot_id` IN (%u, %u, %u, %u, %u, %u, %u, %u)", + " AND bi.`slot_id` IN ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", owner_id, rgb_value, MainHead, MainChest, MainArms, MainWrist1, MainWrist2, MainHands, MainLegs, MainFeet ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -bool BotDatabase::SetHelmAppearance(uint32 owner_id, uint32 bot_id, bool show_flag) +bool BotDatabase::SaveHelmAppearance(const uint32 owner_id, const uint32 bot_id, const bool show_flag) { if (!owner_id || !bot_id) return false; - std::string query = StringFormat( + query = StringFormat( "UPDATE `bot_data`" " SET `show_helm` = '%u'" " WHERE `owner_id` = '%u'" @@ -180,16 +1792,18 @@ bool BotDatabase::SetHelmAppearance(uint32 owner_id, uint32 bot_id, bool show_fl bot_id ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -bool BotDatabase::SetAllHelmAppearances(uint32 owner_id, bool show_flag) +bool BotDatabase::SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag) { if (!owner_id) return false; - std::string query = StringFormat( + query = StringFormat( "UPDATE `bot_data`" " SET `show_helm` = '%u'" " WHERE `owner_id` = '%u'", @@ -197,16 +1811,18 @@ bool BotDatabase::SetAllHelmAppearances(uint32 owner_id, bool show_flag) owner_id ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -bool BotDatabase::ToggleHelmAppearance(uint32 owner_id, uint32 bot_id) +bool BotDatabase::ToggleHelmAppearance(const uint32 owner_id, const uint32 bot_id) { if (!owner_id || !bot_id) return false; - std::string query = StringFormat( + query = StringFormat( "UPDATE `bot_data`" " SET `show_helm` = (`show_helm` XOR '1')" " WHERE `owner_id` = '%u'" @@ -215,32 +1831,36 @@ bool BotDatabase::ToggleHelmAppearance(uint32 owner_id, uint32 bot_id) bot_id ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -bool BotDatabase::ToggleAllHelmAppearances(uint32 owner_id) +bool BotDatabase::ToggleAllHelmAppearances(const uint32 owner_id) { if (!owner_id) return false; - std::string query = StringFormat( + query = StringFormat( "UPDATE `bot_data`" " SET `show_helm` = (`show_helm` XOR '1')" " WHERE `owner_id` = '%u'", owner_id ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -bool BotDatabase::SetFollowDistance(uint32 owner_id, uint32 bot_id, uint32 follow_distance) +bool BotDatabase::SaveFollowDistance(const uint32 owner_id, const uint32 bot_id, const uint32 follow_distance) { if (!owner_id || !bot_id || !follow_distance) return false; - std::string query = StringFormat( + query = StringFormat( "UPDATE `bot_data`" " SET `follow_distance` = '%u'" " WHERE `owner_id` = '%u'" @@ -250,16 +1870,18 @@ bool BotDatabase::SetFollowDistance(uint32 owner_id, uint32 bot_id, uint32 follo bot_id ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -bool BotDatabase::SetAllFollowDistances(uint32 owner_id, uint32 follow_distance) +bool BotDatabase::SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance) { if (!owner_id || !follow_distance) return false; - std::string query = StringFormat( + query = StringFormat( "UPDATE `bot_data`" " SET `follow_distance` = '%u'" " WHERE `owner_id` = '%u'", @@ -267,16 +1889,18 @@ bool BotDatabase::SetAllFollowDistances(uint32 owner_id, uint32 follow_distance) owner_id ); auto results = QueryDatabase(query); + if (!results.Success()) + return false; - return results.Success(); + return true; } -uint32 BotDatabase::Clone(uint32 owner_id, uint32 bot_id, const char* clone_name) +bool BotDatabase::CreateCloneBot(const uint32 owner_id, const uint32 bot_id, const std::string& clone_name, uint32& clone_id) { - if (!owner_id || !bot_id || !clone_name) - return 0; + if (!owner_id || !bot_id || clone_name.empty()) + return false; - std::string data_query = StringFormat( + query = StringFormat( "INSERT INTO `bot_data`" " (" "`owner_id`," @@ -376,23 +2000,25 @@ uint32 BotDatabase::Clone(uint32 owner_id, uint32 bot_id, const char* clone_name " bd.`owner_id` = '%u'" " AND" " bd.`bot_id` = '%u'", - clone_name, + clone_name.c_str(), owner_id, bot_id ); - auto results = QueryDatabase(data_query); + auto results = QueryDatabase(query); if (!results.Success()) - return 0; + return false; - return results.LastInsertedID(); + clone_id = results.LastInsertedID(); + + return true; } -bool BotDatabase::CloneInventory(uint32 owner_id, uint32 bot_id, uint32 clone_id) +bool BotDatabase::CreateCloneBotInventory(const uint32 owner_id, const uint32 bot_id, const uint32 clone_id) { if (!owner_id || !bot_id || !clone_id) return false; - std::string inv_query = StringFormat( + query = StringFormat( "INSERT INTO `bot_inventories`" " (" "bot_id," @@ -439,297 +2065,436 @@ bool BotDatabase::CloneInventory(uint32 owner_id, uint32 bot_id, uint32 clone_id owner_id, bot_id ); - auto results = QueryDatabase(inv_query); + auto results = QueryDatabase(query); + if (!results.Success()) { + DeleteItems(clone_id); + return false; + } - return results.Success(); + return true; } -// Bot-group functions -bool BotDatabase::DoesBotGroupExist(std::string& group_name) +/* Bot bot-group functions */ +bool BotDatabase::QueryBotGroupExistence(const std::string& group_name, bool& extant_flag) { if (group_name.empty()) return false; - std::string query = StringFormat("SELECT `group_name` FROM `vw_bot_groups` WHERE `group_name` LIKE '%s' LIMIT 1", group_name.c_str()); + query = StringFormat("SELECT `group_name` FROM `vw_bot_groups` WHERE `group_name` LIKE '%s' LIMIT 1", group_name.c_str()); auto results = QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) + if (!results.Success()) return false; - - auto row = results.begin(); - if (!group_name.compare(row[0])) + if (!results.RowCount()) return true; + + extant_flag = true; + + return true; +} + +bool BotDatabase::LoadBotGroupIDByBotGroupName(const std::string& group_name, uint32& botgroup_id) +{ + if (group_name.empty()) + return false; + + query = StringFormat("SELECT `groups_index` FROM `bot_groups` WHERE `group_name` = '%s' LIMIT 1", group_name.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + botgroup_id = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadBotGroupIDByLeaderID(const uint32 leader_id, uint32& botgroup_id) +{ + if (!leader_id) + return false; + + query = StringFormat("SELECT `groups_index` FROM `bot_groups` WHERE `group_leader_id` = '%u' LIMIT 1", leader_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + botgroup_id = atoi(row[0]); - return false; + return true; } -uint32 BotDatabase::GetGroupIDByGroupName(std::string& group_name, std::string& error_message) -{ - if (group_name.empty()) - return 0; - - std::string query = StringFormat("SELECT `groups_index` FROM `bot_groups` WHERE `group_name` = '%s'", group_name.c_str()); - auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return 0; - } - auto row = results.begin(); - - return atoi(row[0]); -} - -uint32 BotDatabase::GetLeaderIDByGroupName(std::string& group_name, std::string& error_message) -{ - if (group_name.empty()) - return 0; - - std::string query = StringFormat("SELECT `group_leader_id` FROM `bot_groups` WHERE `group_name` = '%s'", group_name.c_str()); - auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return 0; - } - auto row = results.begin(); - - return atoi(row[0]); -} - -std::string BotDatabase::GetGroupNameByGroupID(uint32 group_id, std::string& error_message) -{ - if (!group_id) - return std::string(); - - std::string query = StringFormat("SELECT `group_name` FROM `bot_groups` WHERE `groups_index` = '%u'", group_id); - auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return std::string(); - } - auto row = results.begin(); - - return std::string(row[0]); -} - -std::string BotDatabase::GetGroupNameByLeaderID(uint32 leader_id, std::string& error_message) -{ - if (!leader_id) - return std::string(); - - std::string query = StringFormat("SELECT `group_name` FROM `bot_groups` WHERE `group_leader_id` = '%u'", leader_id); - auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return std::string(); - } - auto row = results.begin(); - - return std::string(row[0]); -} - -uint32 BotDatabase::GetGroupIDByLeaderID(uint32 leader_id, std::string& error_message) -{ - if (!leader_id) - return 0; - - std::string query = StringFormat("SELECT `groups_index` FROM `bot_groups` WHERE `group_leader_id` = '%u'", leader_id); - auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return 0; - } - auto row = results.begin(); - - return atoi(row[0]); -} - -uint32 BotDatabase::GetLeaderIDByGroupID(uint32 group_id, std::string& error_message) -{ - if (!group_id) - return 0; - - std::string query = StringFormat("SELECT `group_leader_id` FROM `bot_groups` WHERE `groups_index` = '%u'", group_id); - auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return 0; - } - auto row = results.begin(); - - return atoi(row[0]); -} - -uint32 BotDatabase::GetGroupIDByMemberID(uint32 member_id, std::string& error_message) +bool BotDatabase::LoadBotGroupIDByMemberID(const uint32 member_id, uint32& botgroup_id) { if (!member_id) - return 0; + return false; - std::string query = StringFormat("SELECT `groups_index` FROM `bot_group_members` WHERE `bot_id` = '%u'", member_id); + query = StringFormat("SELECT `groups_index` FROM `bot_group_members` WHERE `bot_id` = '%u' LIMIT 1", member_id); auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return 0; - } - auto row = results.begin(); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; - return atoi(row[0]); + auto row = results.begin(); + botgroup_id = atoi(row[0]); + + return true; } -bool BotDatabase::CreateBotGroup(std::string& group_name, uint32 leader_id, std::string& error_message) +bool BotDatabase::LoadLeaderIDByBotGroupName(const std::string& group_name, uint32& leader_id) +{ + if (group_name.empty()) + return false; + + query = StringFormat("SELECT `group_leader_id` FROM `bot_groups` WHERE `group_name` = '%s' LIMIT 1", group_name.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + leader_id = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadLeaderIDByBotGroupID(const uint32 group_id, uint32& leader_id) +{ + if (!group_id) + return false; + + query = StringFormat("SELECT `group_leader_id` FROM `bot_groups` WHERE `groups_index` = '%u' LIMIT 1", group_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + leader_id = atoi(row[0]); + + return true; +} + +bool BotDatabase::LoadBotGroupNameByBotGroupID(const uint32 group_id, std::string& botgroup_name) +{ + if (!group_id) + false; + + query = StringFormat("SELECT `group_name` FROM `bot_groups` WHERE `groups_index` = '%u' LIMIT 1", group_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + botgroup_name = row[0]; + + return true; +} + +bool BotDatabase::LoadBotGroupNameByLeaderID(const uint32 leader_id, std::string& botgroup_name) +{ + if (!leader_id) + return false; + + query = StringFormat("SELECT `group_name` FROM `bot_groups` WHERE `group_leader_id` = '%u' LIMIT 1", leader_id); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + auto row = results.begin(); + botgroup_name = row[0]; + + return true; +} + +bool BotDatabase::CreateBotGroup(const std::string& group_name, const uint32 leader_id) { if (group_name.empty() || !leader_id) return false; - if (DoesBotGroupExist(group_name)) + bool extant_flag = false; + if (!QueryBotGroupExistence(group_name, extant_flag)) return false; + if (extant_flag) + return true; - std::string query = StringFormat("INSERT INTO `bot_groups` (`group_leader_id`, `group_name`) VALUES ('%u', '%s')", leader_id, group_name.c_str()); + query = StringFormat("INSERT INTO `bot_groups` (`group_leader_id`, `group_name`) VALUES ('%u', '%s')", leader_id, group_name.c_str()); auto results = QueryDatabase(query); if (!results.Success()) { - error_message = results.ErrorMessage(); + DeleteBotGroup(leader_id); return false; } - auto group_id = results.LastInsertedID(); - if (!group_id) + auto botgroup_id = results.LastInsertedID(); + if (!botgroup_id) { + DeleteBotGroup(leader_id); return false; + } - query = StringFormat("INSERT INTO `bot_group_members` (`groups_index`, `bot_id`) VALUES ('%u', '%u')", group_id, leader_id); + query = StringFormat("INSERT INTO `bot_group_members` (`groups_index`, `bot_id`) VALUES ('%u', '%u')", botgroup_id, leader_id); results = QueryDatabase(query); if (!results.Success()) { - error_message = results.ErrorMessage(); + RemoveMemberFromBotGroup(leader_id); return false; } return true; } -bool BotDatabase::DeleteBotGroup(uint32 leader_id, std::string& error_message) +bool BotDatabase::DeleteBotGroup(const uint32 leader_id) { if (!leader_id) return false; - uint32 group_id = GetGroupIDByLeaderID(leader_id, error_message); - if (!group_id || !error_message.empty()) + uint32 botgroup_id = 0; + if (!LoadBotGroupIDByLeaderID(leader_id, botgroup_id)) return false; + if (!botgroup_id) + return true; - std::string query = StringFormat("DELETE FROM `bot_group_members` WHERE `groups_index` = '%u'", group_id); + query = StringFormat("DELETE FROM `bot_group_members` WHERE `groups_index` = '%u'", botgroup_id); auto results = QueryDatabase(query); - if (!results.Success()) { - error_message = results.ErrorMessage(); + if (!results.Success()) return false; - } - query = StringFormat("DELETE FROM `bot_groups` WHERE `groups_index` = '%u'", group_id); + query = StringFormat("DELETE FROM `bot_groups` WHERE `groups_index` = '%u'", botgroup_id); results = QueryDatabase(query); - if (!results.Success()) { - error_message = results.ErrorMessage(); + if (!results.Success()) return false; - } return true; } -bool BotDatabase::AddMemberToBotGroup(uint32 leader_id, uint32 member_id, std::string& error_message) +bool BotDatabase::AddMemberToBotGroup(const uint32 leader_id, const uint32 member_id) { if (!leader_id || !member_id) return false; - uint32 group_id = GetGroupIDByLeaderID(leader_id, error_message); - if (!group_id || !error_message.empty()) + uint32 botgroup_id = 0; + if (!LoadBotGroupIDByLeaderID(leader_id, botgroup_id)) return false; + if (!botgroup_id) + return true; - std::string query = StringFormat("INSERT INTO `bot_group_members` (`groups_index`, `bot_id`) VALUES ('%u', '%u')", group_id, member_id); + query = StringFormat("INSERT INTO `bot_group_members` (`groups_index`, `bot_id`) VALUES ('%u', '%u')", botgroup_id, member_id); auto results = QueryDatabase(query); if (!results.Success()) { - error_message = results.ErrorMessage(); + RemoveMemberFromBotGroup(member_id); return false; } return true; } -bool BotDatabase::RemoveMemberFromBotGroup(uint32 member_id, std::string& error_message) +bool BotDatabase::RemoveMemberFromBotGroup(const uint32 member_id) { if (!member_id) return false; - if (GetGroupIDByLeaderID(member_id, error_message)) - return DeleteBotGroup(member_id, error_message); - - if (!error_message.empty()) + uint32 botgroup_id = 0; + if (!LoadBotGroupIDByLeaderID(member_id, botgroup_id)) return false; + if (botgroup_id) + return DeleteBotGroup(member_id); - std::string query = StringFormat("DELETE FROM `bot_group_members` WHERE `bot_id` = '%u'", member_id); + query = StringFormat("DELETE FROM `bot_group_members` WHERE `bot_id` = '%u'", member_id); auto results = QueryDatabase(query); - if (!results.Success()) { - error_message = results.ErrorMessage(); + if (!results.Success()) return false; - } return true; } -uint32 BotDatabase::GetGroupIDForLoadGroup(uint32 owner_id, std::string& group_name, std::string& error_message) +bool BotDatabase::LoadBotGroupIDForLoadBotGroup(const uint32 owner_id, const std::string& group_name, uint32& botgroup_id) { if (!owner_id || group_name.empty()) - return 0; + return false; - std::string query = StringFormat("SELECT `groups_index`, `group_name` FROM `vw_bot_groups` WHERE `owner_id` = '%u'", owner_id); + query = StringFormat("SELECT `groups_index`, `group_name` FROM `vw_bot_groups` WHERE `owner_id` = '%u' LIMIT 1", owner_id); auto results = QueryDatabase(query); - if (!results.Success() || !results.RowCount()) { - error_message = results.ErrorMessage(); - return 0; - } + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; - for (auto row = results.begin(); row != results.end(); ++row) { - if (!group_name.compare(row[1])) - return atoi(row[0]); - } - - return 0; + auto row = results.begin(); + if (!group_name.compare(row[1])) + botgroup_id = atoi(row[0]); + + return true; } -std::map> BotDatabase::LoadGroup(std::string& group_name, std::string& error_message) +bool BotDatabase::LoadBotGroup(const std::string& group_name, std::map>& member_list) { - std::map> group_list; if (group_name.empty()) - return group_list; + return false; - uint32 group_id = GetGroupIDByGroupName(group_name, error_message); - if (!group_id || !error_message.empty()) - return group_list; + uint32 botgroup_id = 0; + if (!LoadBotGroupIDByBotGroupName(group_name, botgroup_id)) + return false; + if (!botgroup_id) + return true; - std::string query = StringFormat("SELECT `bot_id` FROM `bot_group_members` WHERE `groups_index` = '%u'", group_id); + query = StringFormat("SELECT `bot_id` FROM `bot_group_members` WHERE `groups_index` = '%u' LIMIT 6", botgroup_id); auto results = QueryDatabase(query); - if (!results.Success()) { - error_message = results.ErrorMessage(); - return group_list; - } + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; for (auto row = results.begin(); row != results.end(); ++row) - group_list[group_id].push_back(atoi(row[0])); + member_list[botgroup_id].push_back(atoi(row[0])); - return group_list; + return true; } -std::list> BotDatabase::GetGroupsListByOwnerID(uint32 owner_id, std::string& error_message) +bool BotDatabase::LoadBotGroupsListByOwnerID(const uint32 owner_id, std::list>& botgroups_list) { - std::list> groups_list; if (!owner_id) - return groups_list; + return false; - std::string query = StringFormat("SELECT `group_name`, `group_leader_name` FROM `vw_bot_groups` WHERE `owner_id` = '%u'", owner_id); + query = StringFormat("SELECT `group_name`, `group_leader_name` FROM `vw_bot_groups` WHERE `owner_id` = '%u'", owner_id); auto results = QueryDatabase(query); - if (!results.Success()) { - error_message = results.ErrorMessage(); - return groups_list; - } + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; for (auto row = results.begin(); row != results.end(); ++row) - groups_list.push_back(std::pair(row[0], row[1])); - - return groups_list; + botgroups_list.push_back(std::pair(row[0], row[1])); + + return true; } + +/* Bot group functions */ +bool BotDatabase::LoadGroupedBotsByGroupID(const uint32 group_id, std::list& group_list) +{ + if (!group_id) + return false; + + query = StringFormat( + "SELECT g.`mob_id` AS bot_id" + " FROM `vw_groups` AS g" + " JOIN `bot_data` AS b" + " ON g.`mob_id` = b.`bot_id`" + " AND g.`mob_type` = 'B'" + " WHERE g.`group_id` = '%u'", + group_id + ); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + if (!results.RowCount()) + return true; + + for (auto row = results.begin(); row != results.end(); ++row) + group_list.push_back(atoi(row[0])); + + return true; +} + + +/* Bot miscellaneous functions */ + + +/* fail::Bot functions */ +const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; } +const char* BotDatabase::fail::QueryBotCount() { return "Failed to query bot count"; } +const char* BotDatabase::fail::LoadQuestableSpawnCount() { return "Failed to load questable spawn count"; } +const char* BotDatabase::fail::LoadBotsList() { return "Failed to bots list"; } +const char* BotDatabase::fail::LoadOwnerID() { return "Failed to load owner id"; } +const char* BotDatabase::fail::LoadBotID() { return "Failed to load bot id"; } +const char* BotDatabase::fail::LoadBot() { return "Failed to load bot"; } +const char* BotDatabase::fail::SaveNewBot() { return "Failed to save new bot"; } +const char* BotDatabase::fail::SaveBot() { return "Failed to save bot"; } +const char* BotDatabase::fail::DeleteBot() { return "Failed to delete bot"; } +const char* BotDatabase::fail::LoadBuffs() { return "Failed to load buffs"; } +const char* BotDatabase::fail::SaveBuffs() { return "Failed to save buffs"; } +const char* BotDatabase::fail::DeleteBuffs() { return "Failed to delete buffs"; } +const char* BotDatabase::fail::LoadStance() { return "Failed to load stance"; } +const char* BotDatabase::fail::SaveStance() { return "Failed to save stance"; } +const char* BotDatabase::fail::DeleteStance() { return "Failed to delete stance"; } +const char* BotDatabase::fail::LoadTimers() { return "Failed to load timers"; } +const char* BotDatabase::fail::SaveTimers() { return "Failed to save timers"; } +const char* BotDatabase::fail::DeleteTimers() { return "Failed to delete timers"; } +const char* BotDatabase::fail::LoadGuildMembership() { return "Failed to load guild membership"; } +const char* BotDatabase::fail::SaveGuildMembership() { return "Failed to save guild membership"; } +const char* BotDatabase::fail::DeleteGuildMembership() { return "Failed to delete guild membership"; } + +/* fail::Bot inventory functions */ +const char* BotDatabase::fail::QueryInventoryCount() { return "Failed to query inventory count"; } +const char* BotDatabase::fail::LoadItems() { return "Failed to load items"; } +const char* BotDatabase::fail::SaveItems() { return "Failed to save items"; } +const char* BotDatabase::fail::DeleteItems() { return "Failed to delete items"; } +const char* BotDatabase::fail::LoadItemBySlot() { return "Failed to load item by slot"; } +const char* BotDatabase::fail::SaveItemBySlot() { return "Failed to save item by slot"; } +const char* BotDatabase::fail::DeleteItemBySlot() { return "Failed to delete item by slot"; } +const char* BotDatabase::fail::LoadEquipmentColor() { return "Failed to load equipment color"; } +const char* BotDatabase::fail::SaveEquipmentColor() { return "Failed to save equipment color"; } + +/* fail::Bot pet functions */ +const char* BotDatabase::fail::LoadPetIndex() { return "Failed to load pet index"; } +const char* BotDatabase::fail::LoadPetSpellID() { return "Failed to load pet spell id"; } +const char* BotDatabase::fail::LoadPetStats() { return "Failed to load pet stats"; } +const char* BotDatabase::fail::SavePetStats() { return "Failed to save pet stats"; } +const char* BotDatabase::fail::DeletePetStats() { return "Failed to delete pet stats"; } +const char* BotDatabase::fail::LoadPetBuffs() { return "Failed to load pet buffs"; } +const char* BotDatabase::fail::SavePetBuffs() { return "Failed to save pet buffs"; } +const char* BotDatabase::fail::DeletePetBuffs() { return "Failed to delete pet buffs"; } +const char* BotDatabase::fail::LoadPetItems() { return "Failed to load pet items"; } +const char* BotDatabase::fail::SavePetItems() { return "Failed to save pet items"; } +const char* BotDatabase::fail::DeletePetItems() { return "Failed to delete pet items"; } + +/* fail::Bot command functions */ +const char* BotDatabase::fail::LoadInspectMessage() { return "Failed to load inspect message"; } +const char* BotDatabase::fail::SaveInspectMessage() { return "Failed to save inspect message"; } +const char* BotDatabase::fail::DeleteInspectMessage() { return "Failed to delete inspect message"; } +const char* BotDatabase::fail::SaveAllInspectMessages() { return "Failed to save all inspect messages"; } +const char* BotDatabase::fail::DeleteAllInspectMessages() { return "Failed to delete all inspect messages"; } +const char* BotDatabase::fail::SaveAllArmorColorBySlot() { return "Failed to save all armor color by slot"; } +const char* BotDatabase::fail::SaveAllArmorColors() { return "Failed to save all armor colors"; } +const char* BotDatabase::fail::SaveHelmAppearance() { return "Failed to save helm appearance"; } +const char* BotDatabase::fail::SaveAllHelmAppearances() { return "Failed to save all helm appearances"; } +const char* BotDatabase::fail::ToggleHelmAppearance() { return "Failed to save toggle helm appearance"; } +const char* BotDatabase::fail::ToggleAllHelmAppearances() { return "Failed to save toggle all helm appearance"; } +const char* BotDatabase::fail::SaveFollowDistance() { return "Failed to save follow distance"; } +const char* BotDatabase::fail::SaveAllFollowDistances() { return "Failed to save all follow distances"; } +const char* BotDatabase::fail::CreateCloneBot() { return "Failed to create clone bot"; } +const char* BotDatabase::fail::CreateCloneBotInventory() { return "Failed to create clone bot inventory"; } + +/* fail::Bot bot-group functions */ +const char* BotDatabase::fail::QueryBotGroupExistence() { return "Failed to query bot-group existence"; } +const char* BotDatabase::fail::LoadBotGroupIDByBotGroupName() { return "Failed to load bot-group id by bot-group name"; } +const char* BotDatabase::fail::LoadBotGroupIDByLeaderID() { return "Failed to load bot-group id by leader id"; } +const char* BotDatabase::fail::LoadBotGroupIDByMemberID() { return "Failed to load bot-group id by member id"; } +const char* BotDatabase::fail::LoadLeaderIDByBotGroupName() { return "Failed to load leader id by bot-group name"; } +const char* BotDatabase::fail::LoadLeaderIDByBotGroupID() { return "Failed to load leader id by bot-group id"; } +const char* BotDatabase::fail::LoadBotGroupNameByBotGroupID() { return "Failed to load bot-group name by bot-group id"; } +const char* BotDatabase::fail::LoadBotGroupNameByLeaderID() { return "Failed to load bot-group name by leader id"; } +const char* BotDatabase::fail::CreateBotGroup() { return "Failed to create bot-group"; } +const char* BotDatabase::fail::DeleteBotGroup() { return "Failed to delete bot-group"; } +const char* BotDatabase::fail::AddMemberToBotGroup() { return "Failed to add member to bot-group"; } +const char* BotDatabase::fail::RemoveMemberFromBotGroup() { return "Failed to remove member from bot-group"; } +const char* BotDatabase::fail::LoadBotGroupIDForLoadBotGroup() { return "Failed to load bot-group id for load bot-group"; } +const char* BotDatabase::fail::LoadBotGroup() { return "Failed to load bot-group"; } +const char* BotDatabase::fail::LoadBotGroupsListByOwnerID() { return "Failed to load bot-groups list by owner id"; } + +/* fail::Bot group functions */ +const char* BotDatabase::fail::LoadGroupedBotsByGroupID() { return "Failed to load grouped bots by group id"; } + +/* fail::Bot miscellaneous functions */ + #endif // BOTS diff --git a/zone/bot_database.h b/zone/bot_database.h index 5e9d02880..0acfcac33 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -29,9 +29,16 @@ #include #include + +class Bot; +class ItemInst; +class Inventory; +struct BotsAvailableList; +enum BotStanceType; + + class BotDatabase : public DBcore { - public: BotDatabase(); BotDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port); @@ -39,49 +46,223 @@ public: bool Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port); - bool GetCommandSettings(std::map>> &bot_command_settings); + bool LoadBotCommandSettings(std::map>> &bot_command_settings); - // Bot command functions - bool GetInspectMessage(uint32 bot_id, InspectMessage_Struct* message); - bool SetInspectMessage(uint32 bot_id, const InspectMessage_Struct* message); - bool SetAllInspectMessages(uint32 owner_id, const InspectMessage_Struct* message); - bool SetAllArmorColorBySlot(uint32 owner_id, int16 slot_id, uint32 rgb_value); - bool SetAllArmorColors(uint32 owner_id, uint32 rgb_value); + /* Bot functions */ + 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 SetHelmAppearance(uint32 owner_id, uint32 bot_id, bool show_flag = true); - bool SetAllHelmAppearances(uint32 owner_id, bool show_flag = true); + bool LoadOwnerID(const std::string& bot_name, uint32& owner_id); + bool LoadOwnerID(const uint32 bot_id, uint32& owner_id); + bool LoadBotID(const uint32 owner_id, const std::string& bot_name, uint32& bot_id); - bool ToggleHelmAppearance(uint32 owner_id, uint32 bot_id); - bool ToggleAllHelmAppearances(uint32 owner_id); + bool LoadBot(const uint32 bot_id, Bot*& loaded_bot); + bool SaveNewBot(Bot* bot_inst, uint32& bot_id); + bool SaveBot(Bot* bot_inst); + bool DeleteBot(const uint32 bot_id); - bool SetFollowDistance(uint32 owner_id, uint32 bot_id, uint32 follow_distance); - bool SetAllFollowDistances(uint32 owner_id, uint32 follow_distance); + bool LoadBuffs(Bot* bot_inst); + bool SaveBuffs(Bot* bot_inst); + bool DeleteBuffs(const uint32 bot_id); - uint32 Clone(uint32 owner_id, uint32 bot_id, const char* clone_name); - bool CloneInventory(uint32 owner_id, uint32 bot_id, uint32 clone_id); + bool LoadStance(const uint32 bot_id, BotStanceType& bot_stance); + bool LoadStance(Bot* bot_inst, bool& stance_flag); + bool SaveStance(const uint32 bot_id, const BotStanceType bot_stance); + bool SaveStance(Bot* bot_inst); + bool DeleteStance(const uint32 bot_id); - // Bot-group functions - bool DoesBotGroupExist(std::string& group_name); + bool LoadTimers(Bot* bot_inst); + bool SaveTimers(Bot* bot_inst); + bool DeleteTimers(const uint32 bot_id); - uint32 GetGroupIDByGroupName(std::string& group_name, std::string& error_message); - uint32 GetLeaderIDByGroupName(std::string& group_name, std::string& error_message); - std::string GetGroupNameByGroupID(uint32 group_id, std::string& error_message); - std::string GetGroupNameByLeaderID(uint32 leader_id, std::string& error_message); - uint32 GetGroupIDByLeaderID(uint32 leader_id, std::string& error_message); - uint32 GetLeaderIDByGroupID(uint32 group_id, std::string& error_message); + bool LoadGuildMembership(const uint32 bot_id, uint32& guild_id, uint8& guild_rank, std::string& guild_name); + bool SaveGuildMembership(const uint32 bot_id, const uint32 guild_id, const uint8 guild_rank); + bool DeleteGuildMembership(const uint32 bot_id); - uint32 GetGroupIDByMemberID(uint32 member_id, std::string& error_message); - bool CreateBotGroup(std::string& group_name, uint32 leader_id, std::string& error_message); - bool DeleteBotGroup(uint32 leader_id, std::string& error_message); - bool AddMemberToBotGroup(uint32 leader_id, uint32 member_id, std::string& error_message); - bool RemoveMemberFromBotGroup(uint32 member_id, std::string& error_message); + /* Bot inventory functions */ + bool QueryInventoryCount(const uint32 bot_id, uint32& item_count); - uint32 GetGroupIDForLoadGroup(uint32 owner_id, std::string& group_name, std::string& error_message); - std::map> LoadGroup(std::string& group_name, std::string& error_message); + bool LoadItems(const uint32 bot_id, Inventory &inventory_inst); + bool SaveItems(Bot* bot_inst); + bool DeleteItems(const uint32 bot_id); + + bool LoadItemBySlot(Bot* bot_inst); + bool LoadItemBySlot(const uint32 bot_id, const uint32 slot_id, uint32& item_id); + bool SaveItemBySlot(Bot* bot_inst, const uint32 slot_id, const ItemInst* item_inst); + bool DeleteItemBySlot(const uint32 bot_id, const uint32 slot_id); + + bool LoadEquipmentColor(const uint32 bot_id, const uint8 material_slot_id, uint32& rgb); + bool SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, const uint32 rgb); + + + /* Bot pet functions */ + bool LoadPetIndex(const uint32 bot_id, uint32& pet_index); + bool LoadPetSpellID(const uint32 bot_id, uint32& pet_spell_id); - std::list> GetGroupsListByOwnerID(uint32 owner_id, std::string& error_message); + bool LoadPetStats(const uint32 bot_id, std::string& pet_name, uint32& pet_mana, uint32& pet_hp, uint32& pet_spell_id); + bool SavePetStats(const uint32 bot_id, const std::string& pet_name, const uint32 pet_mana, const uint32 pet_hp, const uint32 pet_spell_id); + bool DeletePetStats(const uint32 bot_id); + + bool LoadPetBuffs(const uint32 bot_id, SpellBuff_Struct* pet_buffs); + bool SavePetBuffs(const uint32 bot_id, const SpellBuff_Struct* pet_buffs, bool delete_flag = false); + bool DeletePetBuffs(const uint32 bot_id); + + bool LoadPetItems(const uint32 bot_id, uint32* pet_items); + bool SavePetItems(const uint32 bot_id, const uint32* pet_items, bool delete_flag = false); + bool DeletePetItems(const uint32 bot_id); + + + /* Bot command functions */ + bool LoadInspectMessage(const uint32 bot_id, InspectMessage_Struct& inspect_message); + bool SaveInspectMessage(const uint32 bot_id, const InspectMessage_Struct& inspect_message); + bool DeleteInspectMessage(const uint32 bot_id); + + bool SaveAllInspectMessages(const uint32 owner_id, const InspectMessage_Struct& inspect_message); + bool DeleteAllInspectMessages(const uint32 owner_id); + + bool SaveAllArmorColorBySlot(const uint32 owner_id, const int16 slot_id, const uint32 rgb_value); + bool SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_value); + + bool SaveHelmAppearance(const uint32 owner_id, const uint32 bot_id, const bool show_flag = true); + bool SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag = true); + + bool ToggleHelmAppearance(const uint32 owner_id, const uint32 bot_id); + bool ToggleAllHelmAppearances(const uint32 owner_id); + + bool SaveFollowDistance(const uint32 owner_id, const uint32 bot_id, const uint32 follow_distance); + bool SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance); + + bool CreateCloneBot(const uint32 owner_id, const uint32 bot_id, const std::string& clone_name, uint32& clone_id); + bool CreateCloneBotInventory(const uint32 owner_id, const uint32 bot_id, const uint32 clone_id); + + + /* Bot bot-group functions */ + bool QueryBotGroupExistence(const std::string& botgroup_name, bool& extant_flag); + + bool LoadBotGroupIDByBotGroupName(const std::string& botgroup_name, uint32& botgroup_id); + bool LoadBotGroupIDByLeaderID(const uint32 leader_id, uint32& botgroup_id); + bool LoadBotGroupIDByMemberID(const uint32 member_id, uint32& botgroup_id); + + bool LoadLeaderIDByBotGroupName(const std::string& botgroup_name, uint32& leader_id); + bool LoadLeaderIDByBotGroupID(const uint32 botgroup_id, uint32& leader_id); + + bool LoadBotGroupNameByBotGroupID(const uint32 botgroup_id, std::string& botgroup_name); + bool LoadBotGroupNameByLeaderID(const uint32 leader_id, std::string& botgroup_name); + + bool CreateBotGroup(const std::string& botgroup_name, const uint32 leader_id); + bool DeleteBotGroup(const uint32 leader_id); + bool AddMemberToBotGroup(const uint32 leader_id, const uint32 member_id); + bool RemoveMemberFromBotGroup(const uint32 member_id); + + bool LoadBotGroupIDForLoadBotGroup(const uint32 owner_id, const std::string& botgroup_name, uint32& botgroup_id); + bool LoadBotGroup(const std::string& botgroup_name, std::map>& member_list); + + bool LoadBotGroupsListByOwnerID(const uint32 owner_id, std::list>& botgroups_list); + + + /* Bot group functions */ + bool LoadGroupedBotsByGroupID(const uint32 group_id, std::list& group_list); + + + /* Bot miscellaneous functions */ + + + class fail { + public: + /* fail::Bot functions */ + static const char* QueryNameAvailablity(); + static const char* QueryBotCount(); + static const char* LoadQuestableSpawnCount(); + static const char* LoadBotsList(); + static const char* LoadOwnerID(); + static const char* LoadBotID(); + static const char* LoadBot(); + static const char* SaveNewBot(); + static const char* SaveBot(); + static const char* DeleteBot(); + static const char* LoadBuffs(); + static const char* SaveBuffs(); + static const char* DeleteBuffs(); + static const char* LoadStance(); + static const char* SaveStance(); + static const char* DeleteStance(); + static const char* LoadTimers(); + static const char* SaveTimers(); + static const char* DeleteTimers(); + static const char* LoadGuildMembership(); + static const char* SaveGuildMembership(); + static const char* DeleteGuildMembership(); + + /* fail::Bot inventory functions */ + static const char* QueryInventoryCount(); + static const char* LoadItems(); + static const char* SaveItems(); + static const char* DeleteItems(); + static const char* LoadItemBySlot(); + static const char* SaveItemBySlot(); + static const char* DeleteItemBySlot(); + static const char* LoadEquipmentColor(); + static const char* SaveEquipmentColor(); + + /* fail::Bot pet functions */ + static const char* LoadPetIndex(); + static const char* LoadPetSpellID(); + static const char* LoadPetStats(); + static const char* SavePetStats(); + static const char* DeletePetStats(); + static const char* LoadPetBuffs(); + static const char* SavePetBuffs(); + static const char* DeletePetBuffs(); + static const char* LoadPetItems(); + static const char* SavePetItems(); + static const char* DeletePetItems(); + + /* fail::Bot command functions */ + static const char* LoadInspectMessage(); + static const char* SaveInspectMessage(); + static const char* DeleteInspectMessage(); + static const char* SaveAllInspectMessages(); + static const char* DeleteAllInspectMessages(); + static const char* SaveAllArmorColorBySlot(); + static const char* SaveAllArmorColors(); + static const char* SaveHelmAppearance(); + static const char* SaveAllHelmAppearances(); + static const char* ToggleHelmAppearance(); + static const char* ToggleAllHelmAppearances(); + static const char* SaveFollowDistance(); + static const char* SaveAllFollowDistances(); + static const char* CreateCloneBot(); + static const char* CreateCloneBotInventory(); + + /* fail::Bot bot-group functions */ + static const char* QueryBotGroupExistence(); + static const char* LoadBotGroupIDByBotGroupName(); + static const char* LoadBotGroupIDByLeaderID(); + static const char* LoadBotGroupIDByMemberID(); + static const char* LoadLeaderIDByBotGroupName(); + static const char* LoadLeaderIDByBotGroupID(); + static const char* LoadBotGroupNameByBotGroupID(); + static const char* LoadBotGroupNameByLeaderID(); + static const char* CreateBotGroup(); + static const char* DeleteBotGroup(); + static const char* AddMemberToBotGroup(); + static const char* RemoveMemberFromBotGroup(); + static const char* LoadBotGroupIDForLoadBotGroup(); + static const char* LoadBotGroup(); + static const char* LoadBotGroupsListByOwnerID(); + + /* fail::Bot group functions */ + static const char* LoadGroupedBotsByGroupID(); + + /* fail::Bot miscellaneous functions */ + }; + + private: + std::string query; }; extern BotDatabase botdb; diff --git a/zone/bot_structs.h b/zone/bot_structs.h index c8b9623eb..996fbff41 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -26,12 +26,12 @@ #include struct BotsAvailableList { - uint32 BotID; - char BotName[64]; - uint16 BotClass; - uint8 BotLevel; - uint16 BotRace; - uint8 BotGender; + uint32 ID; + char Name[64]; + uint16 Class; + uint8 Level; + uint16 Race; + uint8 Gender; }; struct BotGroup { diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index dc5e3136f..32d215b9b 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2056,24 +2056,23 @@ bool QuestManager::botquest() bool QuestManager::createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender) { QuestManagerCurrentQuestVars(); - std::string TempErrorMessage; uint32 MaxBotCreate = RuleI(Bots, CreationLimit); if (initiator && initiator->IsClient()) { - if(Bot::SpawnedBotCount(initiator->CharacterID(), &TempErrorMessage) >= MaxBotCreate) + if(Bot::SpawnedBotCount(initiator->CharacterID()) >= MaxBotCreate) { initiator->Message(15,"You have the maximum number of bots allowed."); return false; } - if(!TempErrorMessage.empty()) - { - initiator->Message(13, "Database Error: %s", TempErrorMessage.c_str()); + std::string test_name = name; + bool available_flag = false; + if(!botdb.QueryNameAvailablity(test_name, available_flag)) { + initiator->Message(0, "%s for '%s'", BotDatabase::fail::QueryNameAvailablity(), (char*)name); return false; } - - if(Bot::IsBotNameAvailable((char*)name,&TempErrorMessage)) { + if (!available_flag) { initiator->Message(0, "The name %s is already being used or is invalid. Please choose a different name.", (char*)name); return false; } @@ -2093,11 +2092,6 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level return false; } - if(!TempErrorMessage.empty()) { - initiator->Message(13, "Database Error: %s", TempErrorMessage.c_str()); - return false; - } - // Now that all validation is complete, we can save our newly created bot if(!NewBot->Save()) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index e90e64af5..5347b2792 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -4134,3 +4134,36 @@ bool ZoneDatabase::DeleteCharacterCorpse(uint32 db_id) { return false; } + +uint32 ZoneDatabase::LoadSaylinkID(const char* saylink_text, bool auto_insert) +{ + if (!saylink_text || saylink_text[0] == '\0') + return 0; + + std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s' LIMIT 1", saylink_text); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + if (!results.RowCount()) { + if (auto_insert) + return SaveSaylinkID(saylink_text); + else + return 0; + } + + auto row = results.begin(); + return atoi(row[0]); +} + +uint32 ZoneDatabase::SaveSaylinkID(const char* saylink_text) +{ + if (!saylink_text || saylink_text[0] == '\0') + return 0; + + std::string query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", saylink_text); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + return results.LastInsertedID(); +} diff --git a/zone/zonedb.h b/zone/zonedb.h index b5e1d3509..3c5ca7b00 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -480,6 +480,10 @@ public: void LoadAltCurrencyValues(uint32 char_id, std::map ¤cy); void UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value); + /* Saylinks */ + uint32 LoadSaylinkID(const char* saylink_text, bool auto_insert = true); + uint32 SaveSaylinkID(const char* saylink_text); + /* * Misc stuff. * PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD