diff --git a/changelog.txt b/changelog.txt index 4657226a2..cdbda16e0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/01/2014 == +Kayen: Exported to PERL $client->SendColoredText(color, msg) +demonstar55: Exported SendColoredText to lua + +== 09/30/2014 == +Uleat: Implemented click-casting from bag slots for clients that natively support it (RoF) + == 09/28/2014 == demonstar55: Add support for post June 18, 2014 Hundred Hands Effect spells (they changed the formula and stuff) set Spells:Jun182014HundredHandsRevamp to true if you're using a spell file from June 18, 2014+ diff --git a/common/clientversions.h b/common/clientversions.h index 47a7d0204..ee70d481c 100644 --- a/common/clientversions.h +++ b/common/clientversions.h @@ -17,7 +17,8 @@ static const uint32 BIT_RoFAndLater = 0xFFFFFFE0; static const uint32 BIT_RoF2AndLater = 0xFFFFFFC0; static const uint32 BIT_AllClients = 0xFFFFFFFF; -typedef enum { +typedef enum +{ EQClientUnknown = 0, EQClient62, // Build: 'Aug 4 2005 15:40:59' EQClientTitanium, // Build: 'Oct 31 2005 10:33:37' @@ -26,17 +27,50 @@ typedef enum { EQClientUnderfoot, // Build: 'Jun 8 2010 16:44:32' EQClientRoF, // Build: 'Dec 10 2012 17:35:44' EQClientRoF2, // Build: 'May 10 2013 23:30:08' - + _EQClientCount, // place new clients before this point (preferably, in release/attribute order) - + // Values below are not implemented, as yet... - + EmuNPC = _EQClientCount, EmuMerc, EmuBot, EmuPet, - + _EmuClientCount // array size for EQLimits } EQClientVersion; +static const char* EQClientVersionName(EQClientVersion version) +{ + switch (version) + { + case EQClientUnknown: + return "EQClientUnknown"; + case EQClient62: + return "EQClient62"; + case EQClientTitanium: + return "EQClientTitanium"; + case EQClientSoF: + return "EQClientSoF"; + case EQClientSoD: + return "EQClientSoD"; + case EQClientUnderfoot: + return "EQClientUnderfoot"; + case EQClientRoF: + return "EQClientRoF"; + case EQClientRoF2: + return "EQClientRoF2"; + case EmuNPC: + return "EmuNPC"; + case EmuMerc: + return "EmuMerc"; + case EmuBot: + return "EmuBot"; + case EmuPet: + return "EmuPet"; + default: + return "ERROR: Invalid EQClientVersion"; + }; +} + #endif /* CLIENTVERSIONS_H */ diff --git a/common/emu_oplist.h b/common/emu_oplist.h index af871ec6a..6ddd59123 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -254,6 +254,7 @@ N(OP_ItemLinkText), N(OP_ItemName), N(OP_ItemPacket), N(OP_ItemPreview), +N(OP_ItemRecastDelay), N(OP_ItemVerifyReply), N(OP_ItemVerifyRequest), N(OP_ItemViewUnknown), diff --git a/common/eq_dictionary.cpp b/common/eq_dictionary.cpp index 595d0cc5f..f85d3110f 100644 --- a/common/eq_dictionary.cpp +++ b/common/eq_dictionary.cpp @@ -1024,6 +1024,26 @@ bool EQLimits::AllowsEmptyBagInBag(uint32 version) { //return local[ValidateMobVersion(version)]; } +bool EQLimits::AllowsClickCastFromBag(uint32 version) { + static const bool local[_EmuClientCount] = { +/*Unknown*/ false, +/*62*/ Client62::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*Titanium*/ Titanium::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*SoF*/ SoF::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*SoD*/ SoD::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*Underfoot*/ Underfoot::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*RoF*/ RoF::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*RoF2*/ false, + +/*NPC*/ false, +/*Merc*/ false, +/*Bot*/ false, +/*Pet*/ false + }; + + return local[ValidateMobVersion(version)]; +} + // items uint16 EQLimits::ItemCommonSize(uint32 version) { static const uint16 local[_EmuClientCount] = { diff --git a/common/eq_dictionary.h b/common/eq_dictionary.h index 14f9d53d3..c08809a12 100644 --- a/common/eq_dictionary.h +++ b/common/eq_dictionary.h @@ -184,6 +184,7 @@ public: static uint64 CursorBitmask(uint32 version); static bool AllowsEmptyBagInBag(uint32 version); + static bool AllowsClickCastFromBag(uint32 version); // items static uint16 ItemCommonSize(uint32 version); diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index ea0acaa90..2e4b29261 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4264,6 +4264,13 @@ struct ItemVerifyReply_Struct { /*012*/ }; +struct ItemRecastDelay_Struct { +/*000*/ uint32 recast_delay; // in seconds +/*004*/ uint32 recast_type; +/*008*/ uint32 unknown008; +/*012*/ +}; + /** * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, * bits of your charProfileStruct (no checksum, then charProfile up till diff --git a/common/item.cpp b/common/item.cpp index 513ddb23f..cfa52e0c4 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -911,6 +911,30 @@ bool Inventory::CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_S return true; } +bool Inventory::SupportsClickCasting(int16 slot_id) +{ + // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range + if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource) + { + return true; + } + else if (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END) + { + if (EQLimits::AllowsClickCastFromBag(m_version)) + return true; + } + + return false; +} + +bool Inventory::SupportsPotionBeltCasting(int16 slot_id) +{ + if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource || (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END)) + return true; + + return false; +} + // Test whether a given slot can support a container item bool Inventory::SupportsContainers(int16 slot_id) { diff --git a/common/item.h b/common/item.h index c24c708b1..9be660117 100644 --- a/common/item.h +++ b/common/item.h @@ -121,8 +121,22 @@ public: // Public Methods /////////////////////////////// + Inventory() { m_version = EQClientUnknown; m_versionset = false; } ~Inventory(); + // Inventory v2 creep + bool SetInventoryVersion(EQClientVersion version) { + if (!m_versionset) { + m_version = version; + return (m_versionset = true); + } + else { + return false; + } + } + + EQClientVersion GetInventoryVersion() { return m_version; } + static void CleanDirty(); static void MarkDirty(ItemInst *inst); @@ -132,7 +146,7 @@ public: inline iter_queue cursor_begin() { return m_cursor.begin(); } inline iter_queue cursor_end() { return m_cursor.end(); } - inline bool CursorEmpty() { return (m_cursor.size() == 0); } + inline bool CursorEmpty() { return (m_cursor.size() == 0); } // Retrieve a read-only item from inventory inline const ItemInst* operator[](int16 slot_id) const { return GetItem(slot_id); } @@ -183,6 +197,10 @@ public: static bool CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_Struct *Container); + // Test for valid inventory casting slot + bool SupportsClickCasting(int16 slot_id); + bool SupportsPotionBeltCasting(int16 slot_id); + // Test whether a given slot can support a container item static bool SupportsContainers(int16 slot_id); @@ -229,7 +247,12 @@ protected: std::map m_bank; // Items in character bank std::map m_shbank; // Items in character shared bank std::map m_trade; // Items in a trade session - ItemInstQueue m_cursor; // Items on cursor: FIFO + ItemInstQueue m_cursor; // Items on cursor: FIFO + +private: + // Active inventory version + EQClientVersion m_version; + bool m_versionset; }; class SharedDatabase; diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h index 3920dd3f7..46e8b9099 100644 --- a/common/opcode_dispatch.h +++ b/common/opcode_dispatch.h @@ -306,6 +306,7 @@ OUTz(OP_FinishWindow2); //OUTv(OP_AdventureMerchantResponse, strlen(msg)+2); OUTv(OP_ItemPacket, ItemPacket_Struct); OUTv(OP_ColoredText, ColoredText_Struct); +OUTv(OP_ItemRecastDelay, ItemRecastDelay_Struct); OUTv(OP_FormattedMessage, FormattedMessage_Struct); OUTv(OP_GuildMemberList, uint32); //variable length, but nasty OUTv(OP_InterruptCast, InterruptCast_Struct); diff --git a/common/patches/client62_constants.h b/common/patches/client62_constants.h index 7d2125028..0e672a5de 100644 --- a/common/patches/client62_constants.h +++ b/common/patches/client62_constants.h @@ -180,6 +180,7 @@ namespace Client62 { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/rof_constants.h b/common/patches/rof_constants.h index 62a161dc5..97cb25aa5 100644 --- a/common/patches/rof_constants.h +++ b/common/patches/rof_constants.h @@ -184,6 +184,7 @@ namespace RoF { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = true; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = true; static const bool COIN_HAS_WEIGHT = false; } diff --git a/common/patches/sod_constants.h b/common/patches/sod_constants.h index 9a8907f13..8bdf45532 100644 --- a/common/patches/sod_constants.h +++ b/common/patches/sod_constants.h @@ -181,6 +181,7 @@ namespace SoD { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = false; } diff --git a/common/patches/sof_constants.h b/common/patches/sof_constants.h index d80fa2dcb..0b959ef3a 100644 --- a/common/patches/sof_constants.h +++ b/common/patches/sof_constants.h @@ -181,6 +181,7 @@ namespace SoF { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/titanium_constants.h b/common/patches/titanium_constants.h index 71625362a..85e525fe8 100644 --- a/common/patches/titanium_constants.h +++ b/common/patches/titanium_constants.h @@ -180,6 +180,7 @@ namespace Titanium { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/underfoot_constants.h b/common/patches/underfoot_constants.h index fdbe54086..b89a4f255 100644 --- a/common/patches/underfoot_constants.h +++ b/common/patches/underfoot_constants.h @@ -181,6 +181,7 @@ namespace Underfoot { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = false; } diff --git a/ucs/database.cpp b/ucs/database.cpp index 43bb9e0e3..8130585d1 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -523,9 +523,9 @@ void Database::ExpireMail() { time(nullptr) - RuleI(Mail, ExpireTrash)); results = QueryDatabase(query); if(results.Success()) - _log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); - else _log(UCS__INIT, "Expired %i trash messages.", results.RowsAffected()); + else + _log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); } diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 728676b2d..16197c0da 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -653,3 +653,5 @@ OP_RAWOutOfSession=0x0000 # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 + +OP_ItemRecastDelay=0x57ed diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 10df077f7..f0d223edb 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -660,3 +660,5 @@ OP_RAWOutOfSession=0x0000 # # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 # + +OP_ItemRecastDelay=0x15c4 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index 4621a74a9..1b65a6481 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -654,3 +654,5 @@ OP_RAWOutOfSession=0x0000 # # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 # + +OP_ItemRecastDelay=0x82d7 diff --git a/zone/bot.cpp b/zone/bot.cpp index 4102a2af6..2453a308a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1466,85 +1466,75 @@ void Bot::GenerateAABonuses(StatBonuses* newbon) { } void Bot::LoadAAs() { - std::string errorMessage; - char* Query = 0; - int length = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - int maxAAExpansion = RuleI(Bots, BotAAExpansion); //get expansion to get AAs up to botAAs.clear(); //start fresh + std::string query; + if(GetClass() == BERSERKER) - length = MakeAnyLenString(&Query, "SELECT skill_id FROM altadv_vars WHERE berserker = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetLevel(), maxAAExpansion); + query = StringFormat("SELECT skill_id FROM altadv_vars WHERE berserker = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetLevel(), maxAAExpansion); else - length = MakeAnyLenString(&Query, "SELECT skill_id FROM altadv_vars WHERE ((classes & ( 1 << %i )) >> %i) = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetClass(), GetClass(), GetLevel(), maxAAExpansion); + query = StringFormat("SELECT skill_id FROM altadv_vars WHERE ((classes & ( 1 << %i )) >> %i) = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetClass(), GetClass(), GetLevel(), maxAAExpansion); - if(!database.RunQuery(Query, length, TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int totalAAs = database.CountAAs(); + auto results = database.QueryDatabase(query); - while(DataRow = mysql_fetch_row(DatasetResult)) { - uint32 skill_id = 0; - skill_id = atoi(DataRow[0]); - - if(skill_id > 0 && skill_id < totalAAs) { - SendAA_Struct *sendAA = zone->FindAA(skill_id); - - if(sendAA) { - for(int i=0; imax_level; i++) { - //Get AA info & add to list - uint32 aaid = sendAA->id + i; - uint8 total_levels = 0; - uint8 req_level; - std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(aaid); - - //Get level required for AA - if(RequiredLevel != AARequiredLevelAndCost.end()) - req_level = RequiredLevel->second.Level; - else - req_level = (sendAA->class_type + i * sendAA->level_inc); - - if(req_level > GetLevel()) - break; - - //Bot is high enough level for AA - std::map::iterator foundAA = botAAs.find(aaid); - - // AA is not already in list - if(foundAA == botAAs.end()) { - if(sendAA->id == aaid) { - BotAA newAA; - - newAA.total_levels = 0; - newAA.aa_id = aaid; - newAA.req_level = req_level; - newAA.total_levels += 1; - - botAAs[aaid] = newAA; //add to list - } - else { - //update master AA record with number of levels a bot has in AA, based on level. - botAAs[sendAA->id].total_levels+=1; - } - } - } - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadAAs()"); + return; } + + int totalAAs = database.CountAAs(); + + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 skill_id = 0; + skill_id = atoi(row[0]); + + if(skill_id <= 0 || skill_id >= totalAAs) + continue; + + SendAA_Struct *sendAA = zone->FindAA(skill_id); + + if(!sendAA) + continue; + + for(int i=0; imax_level; i++) { + //Get AA info & add to list + uint32 aaid = sendAA->id + i; + uint8 total_levels = 0; + uint8 req_level; + std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(aaid); + + //Get level required for AA + if(RequiredLevel != AARequiredLevelAndCost.end()) + req_level = RequiredLevel->second.Level; + else + req_level = (sendAA->class_type + i * sendAA->level_inc); + + if(req_level > GetLevel()) + break; + + //Bot is high enough level for AA + std::map::iterator foundAA = botAAs.find(aaid); + + // AA is already in list + if(foundAA != botAAs.end()) + continue; + + if(sendAA->id == aaid) { + BotAA newAA; + + newAA.total_levels = 0; + newAA.aa_id = aaid; + newAA.req_level = req_level; + newAA.total_levels += 1; + + botAAs[aaid] = newAA; //add to list + } + else //update master AA record with number of levels a bot has in AA, based on level. + botAAs[sendAA->id].total_levels+=1; + } + } + } uint32 Bot::GetAA(uint32 aa_id) { @@ -2349,85 +2339,105 @@ bool Bot::IsValidName() { } bool Bot::IsBotNameAvailable(std::string* errorMessage) { - bool Result = false; - if(this->GetCleanName()) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!this->GetCleanName()) + return false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - uint32 ExistingNameCount = 0; + std::string query = StringFormat("SELECT COUNT(id) FROM vwBotCharacterMobs " + "WHERE name LIKE '%s'", this->GetCleanName()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return false; + } - while(DataRow = mysql_fetch_row(DatasetResult)) { - ExistingNameCount = atoi(DataRow[0]); - break; - } + uint32 existingNameCount = 0; - if(ExistingNameCount == 0) - Result = true; + for (auto row = results.begin(); row != results.end(); ++row) { + existingNameCount = atoi(row[0]); + break; + } - mysql_free_result(DatasetResult); - } + if(existingNameCount != 0) + return false; - safe_delete(Query); - } - - return Result; + return true; } bool Bot::Save() { - bool Result = false; - std::string errorMessage; - - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - uint32 affectedRows = 0; if(this->GetBotID() == 0) { // New bot record - uint32 TempNewBotID = 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO bots (BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, LastSpawnDate, TotalPlayTime, LastZoneId) VALUES('%u', '%u', '%s', '%s', '%u', '%i', '%i', '%i', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', NOW(), 0, %i)", this->_botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), this->lastname, this->GetLevel(), GetRace(), GetClass(), GetGender(), GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), this->GetDrakkinTattoo(), this->GetDrakkinDetails(), GetHP(), GetMana(), GetMR(), GetCR(), GetDR(), GetFR(), GetPR(), GetCorrup(), GetAC(), GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA(), GetATK(), _lastZoneId), TempErrorMessageBuffer, 0, &affectedRows, &TempNewBotID)) { - errorMessage = std::string(TempErrorMessageBuffer); + std::string query = StringFormat("INSERT INTO bots (BotOwnerCharacterID, BotSpellsID, Name, LastName, " + "BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, " + "LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, " + "LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, " + "MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, " + "LastSpawnDate, TotalPlayTime, LastZoneId) " + "VALUES('%u', '%u', '%s', '%s', '%u', '%i', '%i', '%i', '%f', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', " + "'%i', NOW(), 0, %i)", + this->_botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), + this->lastname, this->GetLevel(), GetRace(), GetClass(), GetGender(), + GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), + this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), + this->GetBeard(), this->GetDrakkinHeritage(), this->GetDrakkinTattoo(), + this->GetDrakkinDetails(), GetHP(), GetMana(), GetMR(), GetCR(), GetDR(), + GetFR(), GetPR(), GetCorrup(), GetAC(), GetSTR(), GetSTA(), GetDEX(), + GetAGI(), GetINT(), GetWIS(), GetCHA(), GetATK(), _lastZoneId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + auto botOwner = GetBotOwner(); + if (botOwner) + botOwner->Message(13, results.ErrorMessage().c_str()); + return false; } - else { - SetBotID(TempNewBotID); - Result = true; - } - } - else { - // Update existing bot record - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE bots SET BotOwnerCharacterID = '%u', BotSpellsID = '%u', Name = '%s', LastName = '%s', BotLevel = '%u', Race = '%i', Class = '%i', Gender = '%i', Size = '%f', Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', DrakkinDetails = '%i', HP = '%i', Mana = '%i', MR = '%i', CR = '%i', DR = '%i', FR = '%i', PR = '%i', Corrup = '%i', AC = '%i', STR = '%i', STA = '%i', DEX = '%i', AGI = '%i', _INT = '%i', WIS = '%i', CHA = '%i', ATK = '%i', LastSpawnDate = NOW(), TotalPlayTime = '%u', LastZoneId = %i WHERE BotID = '%u'", _botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), this->lastname, this->GetLevel(), _baseRace, this->GetClass(), _baseGender, GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), GetDrakkinTattoo(), GetDrakkinDetails(), GetHP(), GetMana(), _baseMR, _baseCR, _baseDR, _baseFR, _basePR, _baseCorrup, _baseAC, _baseSTR, _baseSTA, _baseDEX, _baseAGI, _baseINT, _baseWIS, _baseCHA, _baseATK, GetTotalPlayTime(), _lastZoneId, GetBotID()), TempErrorMessageBuffer, 0, &affectedRows)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - Result = true; - time(&_startTotalPlayTime); - } - } - safe_delete(Query); - - if(!errorMessage.empty() || (Result && affectedRows != 1)) { - if(GetBotOwner() && !errorMessage.empty()) - GetBotOwner()->Message(13, errorMessage.c_str()); - else if(GetBotOwner()) - GetBotOwner()->Message(13, std::string("Unable to save bot to the database.").c_str()); - - Result = false; - } - else { - SaveBuffs(); + SetBotID(results.LastInsertedID()); + SaveBuffs(); SavePet(); SaveStance(); SaveTimers(); + return true; } - return Result; + // Update existing bot record + std::string query = StringFormat("UPDATE bots SET BotOwnerCharacterID = '%u', BotSpellsID = '%u', " + "Name = '%s', LastName = '%s', BotLevel = '%u', Race = '%i', " + "Class = '%i', Gender = '%i', Size = '%f', Face = '%i', " + "LuclinHairStyle = '%i', LuclinHairColor = '%i', " + "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', " + "LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', " + "DrakkinTattoo = '%i', DrakkinDetails = '%i', HP = '%i', Mana = '%i', " + "MR = '%i', CR = '%i', DR = '%i', FR = '%i', PR = '%i', " + "Corrup = '%i', AC = '%i', STR = '%i', STA = '%i', DEX = '%i', " + "AGI = '%i', _INT = '%i', WIS = '%i', CHA = '%i', ATK = '%i', " + "LastSpawnDate = NOW(), TotalPlayTime = '%u', LastZoneId = %i " + "WHERE BotID = '%u'", + _botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), + this->lastname, this->GetLevel(), _baseRace, this->GetClass(), + _baseGender, GetSize(), this->GetLuclinFace(), this->GetHairStyle(), + GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), + this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), + GetDrakkinTattoo(), GetDrakkinDetails(), GetHP(), GetMana(), + _baseMR, _baseCR, _baseDR, _baseFR, _basePR, _baseCorrup, _baseAC, + _baseSTR, _baseSTA, _baseDEX, _baseAGI, _baseINT, _baseWIS, _baseCHA, + _baseATK, GetTotalPlayTime(), _lastZoneId, GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + auto botOwner = GetBotOwner(); + if (botOwner) + botOwner->Message(13, results.ErrorMessage().c_str()); + return false; + } + + SaveBuffs(); + SavePet(); + SaveStance(); + SaveTimers(); + + return true; } // Returns the current total play time for the bot @@ -2448,169 +2458,107 @@ uint32 Bot::GetTotalPlayTime() { } void Bot::SaveBuffs() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; - int InsertCount = 0; - uint32 buff_count = GetMaxTotalSlots(); - while(BuffCount < BUFF_COUNT) { - if(buffs[BuffCount].spellid > 0 && buffs[BuffCount].spellid != SPELL_UNKNOWN) { - if(InsertCount == 0) { - // Remove any existing buff saves - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - } + // Remove any existing buff saves + std::string query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - int IsPersistent = 0; + for (int buffIndex = 0; buffIndex < BUFF_COUNT; buffIndex++) { + if (buffs[buffIndex].spellid <= 0 || buffs[buffIndex].spellid == SPELL_UNKNOWN) + continue; - if(buffs[BuffCount].persistant_buff) - IsPersistent = 1; - else - IsPersistent = 0; + int isPersistent = buffs[buffIndex].persistant_buff? 1: 0; + query = StringFormat("INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " + "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, " + "CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, " + "caston_x, Persistent, caston_y, caston_z, ExtraDIChance) " + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, " + "%i, %i, %i);", + 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, + buffs[buffIndex].caston_x, isPersistent, buffs[buffIndex].caston_y, + buffs[buffIndex].caston_z, buffs[buffIndex].ExtraDIChance); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " - "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", - GetBotID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, - buffs[BuffCount].ticsremaining, - CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, - buffs[BuffCount].dot_rune, - buffs[BuffCount].caston_x, - IsPersistent, - buffs[BuffCount].caston_y, - buffs[BuffCount].caston_z, - buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - InsertCount++; - } - } + } - BuffCount++; - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } } void Bot::LoadBuffs() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - bool BuffsLoaded = false; + std::string query = StringFormat("SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, " + "PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, " + "HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, " + "caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", + GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; + int buffCount = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; + for (auto row = results.begin(); row != results.end(); ++row) { + if(buffCount == BUFF_COUNT) + break; - buffs[BuffCount].spellid = atoi(DataRow[0]); - buffs[BuffCount].casterlevel = atoi(DataRow[1]); - buffs[BuffCount].ticsremaining = atoi(DataRow[3]); + buffs[buffCount].spellid = atoi(row[0]); + buffs[buffCount].casterlevel = atoi(row[1]); + buffs[buffCount].ticsremaining = atoi(row[3]); - if(CalculatePoisonCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[4]); - } else if(CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[5]); - } else if(CalculateCurseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[6]); - } else if(CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[7]); - } - buffs[BuffCount].numhits = atoi(DataRow[8]); - buffs[BuffCount].melee_rune = atoi(DataRow[9]); - buffs[BuffCount].magic_rune = atoi(DataRow[10]); - buffs[BuffCount].dot_rune = atoi(DataRow[11]); - buffs[BuffCount].caston_x = atoi(DataRow[12]); - buffs[BuffCount].casterid = 0; + 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]); - bool IsPersistent = false; + 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].caston_x = atoi(row[12]); + buffs[buffCount].casterid = 0; - if(atoi(DataRow[13])) - IsPersistent = true; + buffs[buffCount].persistant_buff = atoi(row[13])? true: false; - buffs[BuffCount].caston_y = atoi(DataRow[14]); - buffs[BuffCount].caston_z = atoi(DataRow[15]); - buffs[BuffCount].ExtraDIChance = atoi(DataRow[16]); + buffs[buffCount].caston_y = atoi(row[14]); + buffs[buffCount].caston_z = atoi(row[15]); + buffs[buffCount].ExtraDIChance = atoi(row[16]); - buffs[BuffCount].persistant_buff = IsPersistent; + buffCount++; + } - BuffCount++; - } + query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); + results = database.QueryDatabase(query); - mysql_free_result(DatasetResult); - - BuffsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } } uint32 Bot::GetPetSaveId() { - uint32 Result = 0; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotPetsId from botpets where BotId = %u;", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - break; - } + std::string query = StringFormat("SELECT BotPetsId FROM botpets WHERE BotId = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - mysql_free_result(DatasetResult); - } + if (results.RowCount() == 0) + return 0; - safe_delete(Query); + auto row = results.begin(); - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - - return Result; + return atoi(row[0]); } void Bot::LoadPet() { @@ -2646,135 +2594,80 @@ void Bot::LoadPet() { } void Bot::LoadPetStats(std::string* petName, uint16* petMana, uint16* petHitPoints, uint32* botPetId, uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botPetSaveId == 0) + return; - bool statsLoaded = false; + std::string query = StringFormat("SELECT PetId, Name, Mana, HitPoints " + "FROM botpets WHERE BotPetsId = %u;", + botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select PetId, Name, Mana, HitPoints from botpets where BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - *botPetId = atoi(DataRow[0]); - *petName = std::string(DataRow[1]); - *petMana = atoi(DataRow[2]); - *petHitPoints = atoi(DataRow[3]); - break; - } + if (results.RowCount() == 0) + return; - mysql_free_result(DatasetResult); + auto row = results.begin(); - statsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } + *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) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!petBuffs || botPetSaveId == 0) + return; - bool BuffsLoaded = false; + std::string query = StringFormat("SELECT SpellId, CasterLevel, Duration " + "FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, Duration FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; + int buffIndex = 0; - petBuffs[BuffCount].spellid = atoi(DataRow[0]); - petBuffs[BuffCount].level = atoi(DataRow[1]); - petBuffs[BuffCount].duration = atoi(DataRow[2]); + for (auto row = results.begin();row != results.end(); ++row) { + if(buffIndex == BUFF_COUNT) + break; - BuffCount++; - } + petBuffs[buffIndex].spellid = atoi(row[0]); + petBuffs[buffIndex].level = atoi(row[1]); + petBuffs[buffIndex].duration = atoi(row[2]); - mysql_free_result(DatasetResult); - - BuffsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } + buffIndex++; } + + query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + results = database.QueryDatabase(query); + } void Bot::LoadPetItems(uint32* petItems, uint32 botPetSaveId) { - if(petItems && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!petItems || botPetSaveId == 0) + return; - bool itemsLoaded = false; + std::string query = StringFormat("SELECT ItemId FROM botpetinventory " + "WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT ItemId FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int ItemCount = 0; + int itemIndex = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(ItemCount == EmuConstants::EQUIPMENT_SIZE) - break; + for(auto row = results.begin(); row != results.end(); ++row) { + if(itemIndex == EmuConstants::EQUIPMENT_SIZE) + break; - petItems[ItemCount] = atoi(DataRow[0]); + petItems[itemIndex] = atoi(row[0]); - ItemCount++; - } + itemIndex++; + } - mysql_free_result(DatasetResult); + query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + results = database.QueryDatabase(query); - itemsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && itemsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } } void Bot::SavePet() { @@ -2814,246 +2707,165 @@ void Bot::SavePet() { } uint32 Bot::SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoints, uint32 botPetId) { - uint32 Result = 0; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + std::string query = StringFormat("REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', " + "Mana = %u, HitPoints = %u;", botPetId, GetBotID(), petName.c_str(), + petMana, petHitPoints); + auto results = database.QueryDatabase(query); - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', Mana = %u, HitPoints = %u;", botPetId, GetBotID(), petName.c_str(), petMana, petHitPoints), TempErrorMessageBuffer, 0, 0, &Result)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - - safe_delete(Query); - Query = 0; - - return Result; + return 0; } void Bot::SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { - if(petBuffs && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; + if(!petBuffs || botPetSaveId == 0) + return; - while(BuffCount < BUFF_COUNT) { - if(petBuffs[BuffCount].spellid > 0 && petBuffs[BuffCount].spellid != SPELL_UNKNOWN) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botpetbuffs (BotPetsId, SpellId, CasterLevel, Duration) VALUES(%u, %u, %u, %u);", botPetSaveId, petBuffs[BuffCount].spellid, petBuffs[BuffCount].level, petBuffs[BuffCount].duration), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - } - } + int buffIndex = 0; - BuffCount++; - } + while(buffIndex < BUFF_COUNT) { + if(petBuffs[buffIndex].spellid > 0 && petBuffs[buffIndex].spellid != SPELL_UNKNOWN) { + + std::string query = StringFormat("INSERT INTO botpetbuffs " + "(BotPetsId, SpellId, CasterLevel, 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++; + } - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } } void Bot::SavePetItems(uint32* petItems, uint32 botPetSaveId) { - if(petItems && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int ItemCount = 0; + if(!petItems || botPetSaveId == 0) + return; - while (ItemCount < EmuConstants::EQUIPMENT_SIZE) { - if(petItems[ItemCount] > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botpetinventory (BotPetsId, ItemId) VALUES(%u, %u);", botPetSaveId, petItems[ItemCount]), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - ItemCount++; - } - } + for (int itemIndex = 0;itemIndex < EmuConstants::EQUIPMENT_SIZE; itemIndex++) { + if(petItems[itemIndex] == 0) + continue; - ItemCount++; - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } + std::string query = StringFormat("INSERT INTO botpetinventory " + "(BotPetsId, ItemId) VALUES(%u, %u);", + botPetSaveId, petItems[itemIndex]); + auto results = database.QueryDatabase(query); + if(!results.Success()) + break; } + } void Bot::DeletePetBuffs(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::DeletePetItems(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::DeletePetStats(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE from botpets where BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE from botpets where BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::LoadStance() { - int Result = 0; - bool loaded = false; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select StanceID from botstances where BotID = %u;", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - loaded = true; - break; - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + std::string query = StringFormat("SELECT StanceID FROM botstances WHERE BotID = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success() || results.RowCount() == 0) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadStance()"); + SetDefaultBotStance(); + return; } - if(loaded) - SetBotStance((BotStanceType)Result); - else - SetDefaultBotStance(); + auto row = results.begin(); + + SetBotStance((BotStanceType)atoi(row[0])); } void Bot::SaveStance() { - if(_baseBotStance != _botStance) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(_baseBotStance == _botStance) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO botstances (BotID, StanceId) VALUES(%u, %u);", GetBotID(), GetBotStance()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - else { - safe_delete(Query); - Query = 0; - } + std::string query = StringFormat("REPLACE INTO botstances (BotID, StanceId) " + "VALUES(%u, %u);", GetBotID(), GetBotStance()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in Bot::SaveStance()"); - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error in Bot::SaveStance()"); - } - } } void Bot::LoadTimers() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT IfNull(bt.TimerID, 0) As TimerID, IfNull(bt.Value, 0) As Value, IfNull(MAX(sn.recast_time), 0) AS MaxTimer FROM bottimers bt, spells_new sn WHERE bt.BotID = %u AND sn.EndurTimerIndex = (SELECT case WHEN TimerID > %i THEN TimerID - %i ELSE TimerID END AS TimerID FROM bottimers WHERE TimerID = bt.TimerID AND BotID = bt.BotID ) AND sn.classes%i <= %i;", GetBotID(), DisciplineReuseStart-1, DisciplineReuseStart-1, GetClass(), GetLevel()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int TimerID = 0; - uint32 Value = 0; - uint32 MaxValue = 0; - - while(DataRow = mysql_fetch_row(DatasetResult)) { - TimerID = atoi(DataRow[0]) - 1; - Value = atoi(DataRow[1]); - MaxValue = atoi(DataRow[2]); - - if(TimerID >= 0 && TimerID < MaxTimer && Value < (Timer::GetCurrentTime() + MaxValue)) { - timers[TimerID] = Value; - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + std::string query = StringFormat("SELECT IfNull(bt.TimerID, 0) As TimerID, IfNull(bt.Value, 0) As Value, " + "IfNull(MAX(sn.recast_time), 0) AS MaxTimer FROM bottimers bt, spells_new sn " + "WHERE bt.BotID = %u AND sn.EndurTimerIndex = " + "(SELECT case WHEN TimerID > %i THEN TimerID - %i ELSE TimerID END AS TimerID " + "FROM bottimers WHERE TimerID = bt.TimerID AND BotID = bt.BotID ) " + "AND sn.classes%i <= %i;", + GetBotID(), DisciplineReuseStart-1, DisciplineReuseStart-1, GetClass(), GetLevel()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadTimers()"); + return; } + + 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; + } + } void Bot::SaveTimers() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + bool hadError = false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM bottimers WHERE BotID = %u;", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; + std::string query = StringFormat("DELETE FROM bottimers WHERE BotID = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + hadError = true; + + for(int timerIndex = 0; timerIndex < MaxTimer; timerIndex++) { + if(timers[timerIndex] <= Timer::GetCurrentTime()) + continue; + + query = StringFormat("REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u);", + GetBotID(), timerIndex+1, timers[timerIndex]); + results = database.QueryDatabase(query); + + if(!results.Success()) + hadError = true; } - for(int i = 0; i < MaxTimer; i++) { - if(timers[i] > Timer::GetCurrentTime()) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u);", GetBotID(), i+1, timers[i]), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { + if(hadError) LogFile->write(EQEMuLog::Error, "Error in Bot::SaveTimers()"); - } + } bool Bot::Process() @@ -3398,7 +3210,7 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if (CanSkillProc && HasSkillProcs()) TrySkillProc(other, skillinuse, ReuseTime); - + if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) TrySkillProc(other, skillinuse, ReuseTime, true); } @@ -4255,44 +4067,41 @@ void Bot::Depop() { } bool Bot::DeleteBot(std::string* errorMessage) { - bool Result = false; - int TempCounter = 0; + bool hadError = false; - if(this->GetBotID() > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + 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. + // 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 botinventory WHERE botid = '%u'", this->GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botinventory WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM botbuffs WHERE botid = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM botstances WHERE BotID = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botstances WHERE BotID = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM bots WHERE BotID = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM bots WHERE BotID = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; - - if(TempCounter == 4) - Result = true; - } - - return Result; + return !hadError; } void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { @@ -4347,161 +4156,145 @@ 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) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (this->GetBotID() > 0 && slotID >= EmuConstants::EQUIPMENT_BEGIN && itemID > NO_ITEM) { - 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; - } - } - if(!database.RunQuery(query, MakeAnyLenString(&query, - "REPLACE INTO botinventory " - " (botid,slotid,itemid,charges,instnodrop,color," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,%lu,%lu," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)this->GetBotID(), (unsigned long)slotID, (unsigned long)itemID, (unsigned long)inst->GetCharges(), (unsigned long)(inst->IsInstNoDrop() ? 1:0),(unsigned long)inst->GetColor(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]), errbuf)) { - *errorMessage = std::string(errbuf); - } + if (this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN || itemID <= NO_ITEM) + return; - safe_delete_array(query); - } + 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 botinventory (botid, slotid, itemid, charges, instnodrop, color, " + "augslot1, augslot2, augslot3, augslot4, augslot5) " + "VALUES(%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)", + (unsigned long)this->GetBotID(), (unsigned long)slotID, (unsigned long)itemID, + (unsigned long)inst->GetCharges(), (unsigned long)(inst->IsInstNoDrop()? 1: 0), + (unsigned long)inst->GetColor(), (unsigned long)augslot[0], (unsigned long)augslot[1], + (unsigned long)augslot[2], (unsigned long)augslot[3], (unsigned long)augslot[4]); + 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) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(this->GetBotID() > 0 && slotID >= 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botinventory WHERE botid=%i AND slotid=%i", this->GetBotID(), slotID), errbuf)){ - *errorMessage = std::string(errbuf); - } - safe_delete_array(query); - m_inv.DeleteItem(slotID); - } + if(this->GetBotID() == 0) + return; + + std::string query = StringFormat("DELETE FROM botinventory " + "WHERE botid = %i AND slotid = %i", + this->GetBotID(), slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); + + m_inv.DeleteItem(slotID); } // Retrieves all the inventory records from the database for this bot. void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { - if(this->GetBotID() > 0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0) + return; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5,instnodrop FROM botinventory WHERE botid=%i order by slotid", this->GetBotID()), errbuf, &DatasetResult)) { - while(DataRow = mysql_fetch_row(DatasetResult)) { - int16 slot_id = atoi(DataRow[0]); - uint32 item_id = atoi(DataRow[1]); - uint16 charges = atoi(DataRow[2]); - uint32 color = atoul(DataRow[3]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoul(DataRow[4]); - aug[1] = (uint32)atoul(DataRow[5]); - aug[2] = (uint32)atoul(DataRow[6]); - aug[3] = (uint32)atoul(DataRow[7]); - aug[4] = (uint32)atoul(DataRow[8]); - bool instnodrop = (DataRow[9] && (uint16)atoi(DataRow[9])) ? true : false; + std::string query = StringFormat("SELECT slotid, itemid, charges, color, " + "augslot1, augslot2, augslot3, augslot4, " + "augslot5, instnodrop FROM botinventory " + "WHERE botid = %i ORDER BY slotid", this->GetBotID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } - ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); - if(inst) { - int16 put_slot_id = INVALID_INDEX; - if(instnodrop || ((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) && inst->GetItem()->Attuneable)) - inst->SetInstNoDrop(true); - if(color > 0) - inst->SetColor(color); - if(charges==255) - inst->SetCharges(-1); - else - inst->SetCharges(charges); - if((slot_id >= 8000) && (slot_id <= 8999)) { - // do nothing - } - else { - put_slot_id = inv.PutItem(slot_id, *inst); - } - safe_delete(inst); + 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 color = atoul(row[3]); + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoul(row[4]); + aug[1] = (uint32)atoul(row[5]); + aug[2] = (uint32)atoul(row[6]); + aug[3] = (uint32)atoul(row[7]); + aug[4] = (uint32)atoul(row[8]); + bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: botid=%i, item_id=%i, slot_id=%i", - this->GetBotID(), item_id, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: botid %i has an invalid item_id %i in inventory slot %i", - this->GetBotID(), item_id, slot_id); - } - } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(errbuf); + ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); + if (!inst) { + LogFile->write(EQEMuLog::Error, "Warning: botid %i has an invalid item_id %i in inventory slot %i", this->GetBotID(), item_id, slot_id); + continue; + } + + int16 put_slot_id = INVALID_INDEX; + + if (instnodrop || ((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) && inst->GetItem()->Attuneable)) + inst->SetInstNoDrop(true); + + if (color > 0) + inst->SetColor(color); + + if (charges==255) + inst->SetCharges(-1); + else + inst->SetCharges(charges); + + if (slot_id < 8000 || slot_id > 8999) + put_slot_id = inv.PutItem(slot_id, *inst); + + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id == INVALID_INDEX) + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: botid=%i, item_id=%i, slot_id=%i",this->GetBotID(), item_id, slot_id); + + } - safe_delete_array(query); - } } // Returns the inventory record for this bot from the database for the specified equipment slot. uint32 Bot::GetBotItemBySlot(uint32 slotID) { - uint32 Result = 0; - if(this->GetBotID() > 0 && slotID >= EmuConstants::EQUIPMENT_BEGIN) { - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN) + return 0; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT itemid FROM botinventory WHERE botid=%i AND slotid=%i", GetBotID(), slotID), 0, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT itemid FROM botinventory WHERE botid=%i AND slotid = %i", GetBotID(), slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - mysql_free_result(DatasetResult); - } + if(results.RowCount() != 1) + return 0; - safe_delete_array(query); - } + auto row = results.begin(); - return Result; + return atoi(row[0]); } // Returns the number of inventory records the bot has in the database. uint32 Bot::GetBotItemsCount(std::string *errorMessage) { - uint32 Result = 0; - if(this->GetBotID() > 0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0) + return 0; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT COUNT(*) FROM botinventory WHERE botid=%i", this->GetBotID()), errbuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT COUNT(*) FROM botinventory WHERE botid = %i", this->GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(errbuf); + if(results.RowCount() != 1) + return 0; - safe_delete_array(query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } bool Bot::MesmerizeTarget(Mob* target) { @@ -4694,92 +4487,82 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { } uint32 Bot::GetBotIDByBotName(std::string botName) { - uint32 Result = 0; + if(botName.empty()) + return 0; - if(!botName.empty()) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - std::string errorMessage; + std::string query = StringFormat("SELECT BotID FROM bots WHERE Name = '%s'", botName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotID FROM bots WHERE Name = '%s'", botName.c_str()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - break; - } + if (results.RowCount() == 0) + return 0; - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - - if(!errorMessage.empty()) { - // TODO: Log this error to zone error log - } - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { - Bot* Result = 0; + Bot* loadedBot = nullptr; - if(botID > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botID == 0) + return nullptr; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, BotCreateDate, LastSpawnDate, TotalPlayTime, LastZoneId FROM bots WHERE BotID = '%u'", botID), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(DataRow[2]), std::string(DataRow[3]), atoi(DataRow[4]), atoi(DataRow[5]), atoi(DataRow[6]), atoi(DataRow[7])); - NPCType TempNPCStruct = FillNPCTypeStruct(atoi(DataRow[1]), std::string(DataRow[2]), std::string(DataRow[3]), atoi(DataRow[4]), atoi(DataRow[5]), atoi(DataRow[6]), atoi(DataRow[7]), atof(DataRow[8]), atoi(DataRow[9]), atoi(DataRow[10]), atoi(DataRow[11]), atoi(DataRow[12]), atoi(DataRow[13]), atoi(DataRow[14]), atoi(DataRow[15]), atoi(DataRow[16]), atoi(DataRow[17]), atoi(DataRow[18]), atoi(DataRow[19]), atoi(DataRow[20]), 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); - Result = new Bot(botID, atoi(DataRow[0]), atoi(DataRow[1]), atof(DataRow[38]), atoi(DataRow[39]), TempNPCStruct); - break; - } + std::string query = StringFormat("SELECT BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, " + "Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, " + "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, " + "DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, " + "BotCreateDate, LastSpawnDate, TotalPlayTime, LastZoneId " + "FROM bots WHERE BotID = '%u'", botID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return nullptr; + } - mysql_free_result(DatasetResult); - } + if (results.RowCount() == 0) + return nullptr; - safe_delete_array(Query); - } + auto row = results.begin(); + NPCType defaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), + atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7])); - return Result; + NPCType tempNPCStruct = FillNPCTypeStruct(atoi(row[1]), std::string(row[2]), std::string(row[3]), atoi(row[4]), + atoi(row[5]), atoi(row[6]), atoi(row[7]), atof(row[8]), atoi(row[9]), + atoi(row[10]), atoi(row[11]), atoi(row[12]), atoi(row[13]), atoi(row[14]), + atoi(row[15]), atoi(row[16]), atoi(row[17]), atoi(row[18]), atoi(row[19]), + atoi(row[20]), 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); + + loadedBot = new Bot(botID, atoi(row[0]), atoi(row[1]), atof(row[38]), atoi(row[39]), tempNPCStruct); + + return loadedBot; } std::list Bot::GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage) { - std::list Result; + std::list groupedBots; - if(groupId > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(groupId == 0) + return groupedBots; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select g.mobid as BotID from vwGroups as g join bots as b on g.mobid = b.BotId and g.mobtype = 'B' where g.groupid = %u", groupId), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(DataRow) { - Result.push_back(atoi(DataRow[0])); - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); + std::string query = StringFormat("SELECT g.mobid AS BotID FROM vwGroups AS g " + "JOIN bots AS b ON g.mobid = b.BotId AND g.mobtype = 'B' " + "WHERE g.groupid = %u", groupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return groupedBots; } - return Result; + for (auto row = results.begin(); row != results.end(); ++row) + groupedBots.push_back(atoi(row[0])); + + return groupedBots; } // Load and spawn all zoned bots by bot owner character @@ -4845,383 +4628,278 @@ bool Bot::GroupHasBot(Group* group) { } std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage) { - std::list Result; + std::list ownersBots; - if(botOwnerCharacterID > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return ownersBots; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotID, Name, Class, BotLevel, Race FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(DataRow) { - BotsAvailableList TempAvailableBot; - TempAvailableBot.BotID = atoi(DataRow[0]); - strcpy(TempAvailableBot.BotName, DataRow[1]); - TempAvailableBot.BotClass = atoi(DataRow[2]); - TempAvailableBot.BotLevel = atoi(DataRow[3]); - TempAvailableBot.BotRace = atoi(DataRow[4]); - - Result.push_back(TempAvailableBot); - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); + std::string query = StringFormat("SELECT BotID, Name, Class, BotLevel, Race " + "FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return ownersBots; } - return Result; + 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]); + + ownersBots.push_back(availableBot); + } + + return ownersBots; } std::list Bot::ListSpawnedBots(uint32 characterID, std::string* errorMessage) { - std::list Result; - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::list spawnedBots; - if(characterID > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT bot_name, zone_name FROM botleader WHERE leaderid=%i", characterID), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + if(characterID == 0) + return spawnedBots; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); - SpawnedBotsList TempSpawnedBotsList; - TempSpawnedBotsList.BotLeaderCharID = characterID; - strcpy(TempSpawnedBotsList.BotName, DataRow[0]); - strcpy(TempSpawnedBotsList.ZoneName, DataRow[1]); + 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; + } - Result.push_back(TempSpawnedBotsList); - } - } + for(auto row = results.begin(); row != results.end(); ++row) { + SpawnedBotsList spawnedBotsList; + spawnedBotsList.BotLeaderCharID = characterID; + strcpy(spawnedBotsList.BotName, row[0]); + strcpy(spawnedBotsList.ZoneName, row[1]); - mysql_free_result(DatasetResult); - } + spawnedBots.push_back(spawnedBotsList); + } - safe_delete_array(Query); - } - - return Result; + return spawnedBots; } void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage) { - if(botGroup && !botGroupName.empty()) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + if(!botGroup || botGroupName.empty()) + return; - Mob* tempGroupLeader = botGroup->GetLeader(); + Mob* tempGroupLeader = botGroup->GetLeader(); - if(tempGroupLeader->IsBot()) { - uint32 botGroupId = 0; + if(!tempGroupLeader->IsBot()) + return; - uint32 botGroupLeaderBotId = tempGroupLeader->CastToBot()->GetBotID(); + uint32 botGroupId = 0; + uint32 botGroupLeaderBotId = tempGroupLeader->CastToBot()->GetBotID(); - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT into botgroup (BotGroupLeaderBotId, BotGroupName) values (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()), errbuf, 0, 0, &botGroupId)) { - *errorMessage = std::string(errbuf); - } - else { - if(botGroupId > 0) { - for(int counter = 0; counter < botGroup->GroupCount(); counter++) { - Mob* tempBot = botGroup->members[counter]; + std::string query = StringFormat("INSERT INTO botgroup (BotGroupLeaderBotId, BotGroupName) " + "VALUES (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } - if(tempBot && tempBot->IsBot()) { - uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); + if(botGroupId == 0) + return; - safe_delete_array(query); + for(int groupMemberIndex = 0; groupMemberIndex < botGroup->GroupCount(); groupMemberIndex++) { + Mob* tempBot = botGroup->members[groupMemberIndex]; - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT into botgroupmembers (BotGroupId, BotId) values (%u, %u)", botGroupId, botGroupMemberBotId), errbuf)) { - *errorMessage = std::string(errbuf); - } - } - } - } - } + if(!tempBot || !tempBot->IsBot()) + continue; + + uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); + + query = StringFormat("INSERT INTO botgroupmembers (BotGroupId, BotId) " + "VALUES (%u, %u)", botGroupId, botGroupMemberBotId); + results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); + } - safe_delete_array(query); - } - } } void Bot::DeleteBotGroup(std::string botGroupName, std::string* errorMessage) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(!botGroupName.empty()) { - uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); + if(botGroupName.empty()) + return; - if(errorMessage->empty() && botGroupId > 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botgroupmembers WHERE BotGroupId = %u", botGroupId), errbuf)) { - *errorMessage = std::string(errbuf); - } - else { - safe_delete_array(query); + uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botgroup WHERE BotGroupId = %u", botGroupId), errbuf)) { - *errorMessage = std::string(errbuf); - } - } + if(!errorMessage->empty() || botGroupId== 0) + return; - safe_delete_array(query); - } - } + std::string query = StringFormat("DELETE FROM botgroupmembers WHERE BotGroupId = %u", botGroupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } + + query = StringFormat("DELETE FROM botgroup WHERE BotGroupId = %u", botGroupId); + results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); } std::list Bot::LoadBotGroup(std::string botGroupName, std::string* errorMessage) { - std::list Result; - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::list botGroup; - if(!botGroupName.empty()) { - uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); + if(botGroupName.empty()) + return botGroup; - if(botGroupId > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotId from botgroupmembers where BotGroupId = %u", botGroupId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if(botGroupId == 0) + return botGroup; - if(DataRow) { - BotGroup tempBotGroup; - tempBotGroup.BotGroupID = botGroupId; - tempBotGroup.BotID = atoi(DataRow[0]); + std::string query = StringFormat("SELECT BotId FROM botgroupmembers WHERE BotGroupId = %u", botGroupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return botGroup; + } - Result.push_back(tempBotGroup); - } - } - } + for(auto row = results.begin(); row != results.end(); ++row) { + BotGroup tempBotGroup; + tempBotGroup.BotGroupID = botGroupId; + tempBotGroup.BotID = atoi(row[0]); - mysql_free_result(DatasetResult); - } + botGroup.push_back(tempBotGroup); + } - safe_delete_array(Query); - } - } - - return Result; + return botGroup; } std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage) { - std::list result; + std::list botGroups; - if(botOwnerCharacterId > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterId == 0) + return botGroups; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupName, BotGroupLeaderName from vwBotGroups where BotOwnerCharacterId = %u", botOwnerCharacterId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupName, BotGroupLeaderName FROM vwBotGroups " + "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return botGroups; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + for(auto row = results.begin(); row != results.end(); ++row) { + BotGroupList botGroupList; + botGroupList.BotGroupName = std::string(row[0]); + botGroupList.BotGroupLeaderName = std::string(row[1]); - if(DataRow) { - BotGroupList botGroupList; - botGroupList.BotGroupName = std::string(DataRow[0]); - botGroupList.BotGroupLeaderName = std::string(DataRow[1]); + botGroups.push_back(botGroupList); + } - result.push_back(botGroupList); - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + return botGroups; } bool Bot::DoesBotGroupNameExist(std::string botGroupName) { - bool result = false; - if(!botGroupName.empty()) { - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return false; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " + "WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return false; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + for(auto row = results.begin(); row != results.end(); ++row) { + uint32 tempBotGroupId = atoi(row[0]); + std::string tempBotGroupName = std::string(row[1]); - if(DataRow) { - uint32 tempBotGroupId = atoi(DataRow[0]); - std::string tempBotGroupName = std::string(DataRow[1]); + if (botGroupName == tempBotGroupName && tempBotGroupId != 0) + return true; + } - if(botGroupName == tempBotGroupName) { - result = tempBotGroupId; - break; - } - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + return false; } uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName, std::string* errorMessage) { - uint32 result = 0; - if(botOwnerCharacterId > 0 && !botGroupName.empty()) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterId == 0 || botGroupName.empty()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId, BotGroupName from vwBotGroups where BotOwnerCharacterId = %u", botOwnerCharacterId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId, BotGroupName FROM vwBotGroups " + "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if(results.RowCount() == 0) + return 0; - if(DataRow) { - uint32 tempBotGroupId = atoi(DataRow[0]); - std::string tempBotGroupName = std::string(DataRow[1]); + for(auto row = results.begin(); row != results.end(); ++row) { - if(botGroupName == tempBotGroupName) { - result = tempBotGroupId; - break; - } - } - } - } + uint32 tempBotGroupId = atoi(row[0]); + std::string tempBotGroupName = std::string(row[1]); - mysql_free_result(DatasetResult); - } + if(botGroupName == tempBotGroupName) + return tempBotGroupId; + } - safe_delete_array(Query); - } - - return result; + return 0; } uint32 Bot::GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* errorMessage) { - uint32 result = 0; - if(!botGroupName.empty()) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " + "WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if (results.RowCount() == 0) + return 0; - if(DataRow) { - result = atoi(DataRow[0]); - break; - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { - uint32 result = 0; - if(!botGroupName.empty()) { - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupLeaderBotId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupLeaderBotId FROM vwBotGroups WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return 0; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); - - if(DataRow) { - result = atoi(DataRow[0]); - break; - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage) { - uint32 Result = 0; - if(botOwnerCharacterID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT value FROM quest_globals WHERE name='bot_spawn_limit' and charid=%i", botOwnerCharacterID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[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; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { @@ -5237,56 +4915,42 @@ uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessag } uint32 Bot::CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { - uint32 Result = 0; - if(botOwnerCharacterID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(BotID) FROM bots WHERE BotOwnerCharacterID=%i", botOwnerCharacterID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT COUNT(BotID) FROM bots " + "WHERE BotOwnerCharacterID=%i", botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) { - uint32 Result = 0; - if(botID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotOwnerCharacterID FROM bots WHERE BotID = %u", botID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - if(DataRow = mysql_fetch_row(DatasetResult)) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT BotOwnerCharacterID FROM bots WHERE BotID = %u", botID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { @@ -8109,7 +7773,7 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if (HasSkillProcs()) TrySkillProc(who, skill, ReuseTime*1000); - + if (max_damage > 0 && HasSkillProcSuccess()) TrySkillProc(who, skill, ReuseTime*1000, true); @@ -8887,58 +8551,39 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { } void Bot::SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank) { - if(botId > 0) { - std::string errorMessage; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + if(botId == 0) + return; - if(guildid > 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO botguildmembers SET char_id = %u, guild_id = %u, rank = %u;", botId, guildid, rank), errbuf)) { - errorMessage = std::string(errbuf); - } - } - else { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botguildmembers WHERE char_id = %u;", botId), errbuf)) { - errorMessage = std::string(errbuf); - } - } + if(guildid > 0) { + std::string query = StringFormat("REPLACE INTO botguildmembers " + "SET char_id = %u, guild_id = %u, rank = %u;", + botId, guildid, rank); + auto results = database.QueryDatabase(query); + return; + } - safe_delete_array(query); - - if(!errorMessage.empty()) { - // TODO: Log this error message to the zone error log - } - } + std::string query = StringFormat("DELETE FROM botguildmembers WHERE char_id = %u;", botId); + auto results = database.QueryDatabase(query); } void Bot::LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName) { - if(guildId && guildRank && guildName) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT gm.guild_id, gm.rank, g.name FROM vwGuildMembers AS gm JOIN guilds AS g ON gm.guild_id = g.id WHERE gm.char_id = %u AND gm.mobtype = 'B';", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - *guildId = atoi(DataRow[0]); - *guildRank = atoi(DataRow[1]); - *guildName = std::string(DataRow[2]); - break; - } + if(guildId == nullptr || guildRank == nullptr || guildName == nullptr) + return; - mysql_free_result(DatasetResult); - } + std::string query = StringFormat("SELECT gm.guild_id, gm.rank, g.name " + "FROM vwGuildMembers AS gm JOIN guilds AS g " + "ON gm.guild_id = g.id " + "WHERE gm.char_id = %u AND gm.mobtype = 'B';", + GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success() || results.RowCount() == 0) + return; - safe_delete(Query); - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } + auto row = results.begin(); + *guildId = atoi(row[0]); + *guildRank = atoi(row[1]); + *guildName = std::string(row[2]); } int32 Bot::CalcMaxMana() { @@ -9061,7 +8706,7 @@ void Bot::SetAttackTimer() { } int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - + if (spells[spell_id].targettype == ST_Self) return value; @@ -9073,22 +8718,22 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= (GetLevel() - 40) * 20; - + //This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch. if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch - + int chance = RuleI(Spells, BaseCritChance); chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; - + if (chance > 0){ - + int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) chance = 100; - + if (MakeRandomInt(1,100) <= chance){ Critical = true; ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; @@ -9101,23 +8746,23 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } ratio += RuleI(Spells, WizCritRatio); //Default is zero - + if (Critical){ - value = value_BaseEffect*ratio/100; + value = value_BaseEffect*ratio/100; - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; value += int(value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100)*ratio/100; if (target) { - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); + value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; + value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id)*ratio/100; + value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id)*ratio/100; - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; @@ -9129,28 +8774,28 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } value = value_BaseEffect; - - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; - + + value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100; if (target) { value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); + value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); + value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); + + value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); return value; } int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - + if (target == nullptr) target = this; @@ -9158,37 +8803,37 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int16 chance = 0; int8 modifier = 1; bool Critical = false; - - value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); - + + value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); + value = value_BaseEffect; - value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id)/100); - + value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id)/100); + // Instant Heals if(spells[spell_id].buffduration < 1) { - chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalHealDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - + chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + if(chance && (MakeRandomInt(0,99) < chance)) { Critical = true; modifier = 2; //At present time no critical heal amount modifier SPA exists. } - + value *= modifier; - value += GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier; - value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); + value += GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier; + value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); + value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; - value += value*target->GetHealRate(spell_id, this)/100; + value += value*target->GetHealRate(spell_id, this)/100; if (Critical) entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value); @@ -9198,14 +8843,14 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { - - chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - + chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); + if (spellbonuses.CriticalRegenDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - + if(chance && (MakeRandomInt(0,99) < chance)) return (value * 2); } @@ -11652,18 +11297,22 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 botid = c->GetTarget()->CastToBot()->GetBotID(); std::string errorMessage; - char* Query = 0; + int setslot = atoi(sep->arg[2]); uint8 red = atoi(sep->arg[3]); uint8 green = atoi(sep->arg[4]); uint8 blue = atoi(sep->arg[5]); uint32 setcolor = (red << 16) | (green << 8) | blue; + std::string query = StringFormat("UPDATE botinventory SET color = %u " + "WHERE slotID = %i AND botID = %u", + setcolor, setslot, botid); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE botinventory SET color = %u WHERE slotID = %i AND botID = %u",setcolor, setslot, botid))){ - int slotmaterial = Inventory::CalcMaterialFromSlot(setslot); - c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); - } + int slotmaterial = Inventory::CalcMaterialFromSlot(setslot); + c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); } else { c->Message(15, "You must target a bot you own to do this."); @@ -11671,11 +11320,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } // Help for coloring bot armor - if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor") ){ + if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor") ){ //read from db - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; c->Message(0, "-----------------#bot armorcolor help-----------------------------"); c->Message(0, "Armor: 17(Chest/Robe), 7(Arms), 9(Bracer), 12(Hands), 18(Legs), 19(Boots), 2(Helm)"); @@ -16425,27 +16071,20 @@ uint32 Bot::GetEquipmentColor(uint8 material_slot) const { //Bot tints uint32 slotid = 0; - uint32 returncolor = 0; uint32 botid = this->GetBotID(); //Translate code slot # to DB slot # slotid = Inventory::CalcSlotFromMaterial(material_slot); //read from db - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::string query = StringFormat("SELECT color FROM botinventory " + "WHERE BotID = %u AND SlotID = %u", botid, slotid); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT color FROM botinventory WHERE BotID = %u AND SlotID = %u", botid, slotid), 0, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - returncolor = atoul(DataRow[0]); - } - mysql_free_result(DatasetResult); - safe_delete_array(Query); - } - return returncolor; + auto row = results.begin(); + return atoul(row[0]); } int Bot::GetRawACNoShield(int &shield_ac) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 0061efff4..2ef62c3d3 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1307,7 +1307,13 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) conn_state = ReceivedZoneEntry; ClientVersion = Connection()->ClientVersion(); - ClientVersionBit = 1 << (ClientVersion - 1); + if (ClientVersion != EQClientUnknown) + ClientVersionBit = 1 << (ClientVersion - 1); + else + ClientVersionBit = 0; + + bool siv = m_inv.SetInventoryVersion(ClientVersion); + LogFile->write(EQEMuLog::Debug, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), EQClientVersionName(ClientVersion), ClientVersion); /* Antighost code tmp var is so the search doesnt find this object @@ -3999,15 +4005,16 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast { //discipline, using the item spell slot - if (castspell->inventoryslot == 0xFFFFFFFF) { + if (castspell->inventoryslot == INVALID_INDEX) { if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); InterruptSpell(castspell->spell_id); } return; } - else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check + else if (m_inv.SupportsClickCasting(castspell->inventoryslot) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check { + // packet field types will be reviewed as packet transistions occur -U const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field //bool cancast = true; if (inst && inst->IsType(ItemClassCommon)) @@ -8555,7 +8562,7 @@ void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); - if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check + if (m_inv.SupportsClickCasting(slot_id) || ((item->ItemType == ItemTypePotion || item->PotionBelt) && m_inv.SupportsPotionBeltCasting(slot_id))) // sanity check { ItemInst* p_inst = (ItemInst*)inst; @@ -10686,7 +10693,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) Client *i = entity_list.GetClientByName(ri->player_name); if (i){ if (IsRaidGrouped()){ - i->Message_StringID(0, 5060); //group failed, must invite members not in raid... + i->Message_StringID(0, ALREADY_IN_RAID, GetName()); //group failed, must invite members not in raid... return; } Raid *r = entity_list.GetRaidByClient(i); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 32affc06e..56abe5f09 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1239,6 +1239,11 @@ void Lua_Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in self->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg); } +void Lua_Client::SendColoredText(uint32 type, std::string msg) { + Lua_Safe_Call_Void(); + self->SendColoredText(type, msg); +} + void Lua_Client::PlayMP3(std::string file) { Lua_Safe_Call_Void(); @@ -1492,6 +1497,7 @@ luabind::scope lua_register_client() { .def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst) .def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption) .def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage) + .def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText) .def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3); } diff --git a/zone/lua_client.h b/zone/lua_client.h index 8809779e9..ef2af1181 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -275,6 +275,7 @@ public: void SetThirst(int in_thirst); void SetConsumption(int in_hunger, int in_thirst); void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg); + void SendColoredText(uint32 type, std::string msg); void PlayMP3(std::string file); }; diff --git a/zone/npc.cpp b/zone/npc.cpp index a4ecaec3a..b4c726acb 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -965,240 +965,352 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, } } -uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - uint32 tmp2 = 0; +uint32 ZoneDatabase::CreateNewNPCCommand(const char* zone, uint32 zone_version,Client *client, NPC* spawn, uint32 extra) { + + uint32 npc_type_id = 0; + + if (extra && client && client->GetZoneID()) + { + // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) + int starting_npc_id = client->GetZoneID() * 1000; + + std::string query = StringFormat("SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", + starting_npc_id, starting_npc_id + 1000); + auto results = QueryDatabase(query); + if (results.Success()) { + if (results.RowCount() != 0) + { + auto row = results.begin(); + npc_type_id = atoi(row[0]) + 1; + // Prevent the npc_type id from exceeding the range for this zone + if (npc_type_id >= (starting_npc_id + 1000)) + npc_type_id = 0; + } + else // No npc_type IDs set in this range yet + npc_type_id = starting_npc_id; + } + } + + char tmpstr[64]; + EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); + std::string query; + if (npc_type_id) + { + query = StringFormat("INSERT INTO npc_types (id, name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(%i, \"%s\" , %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + npc_type_id = results.LastInsertedID(); + } + else + { + query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + npc_type_id = results.LastInsertedID(); + } + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawngroup (id, name) VALUES(%i, '%s-%s')", 0, zone, spawn->GetName()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + uint32 spawngroupid = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, + spawn->GetHeading(), spawngroupid); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", + spawngroupid, npc_type_id, 100); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + if(client) + client->LogSQL(query.c_str()); + + return true; +} + +uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) { + uint32 last_insert_id = 0; + + std::string query = StringFormat("INSERT INTO spawngroup (name) VALUES('%s%s%i')", + zone, spawn->GetName(), Timer::GetCurrentTime()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + last_insert_id = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + uint32 respawntime = 0; + uint32 spawnid = 0; + if (respawnTime) + respawntime = respawnTime; + else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) + respawntime = spawn->respawn2->RespawnTimer(); + else + respawntime = 1200; + + query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, + spawn->GetHeading(), last_insert_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + spawnid = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", + last_insert_id, spawn->GetNPCTypeID(), 100); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if(client) + client->LogSQL(query.c_str()); + + return spawnid; +} + +uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) { + + std::string query = StringFormat("UPDATE npc_types SET name = \"%s\", level = %i, race = %i, class = %i, " + "hp = %i, gender = %i, texture = %i, helmtexture = %i, size = %i, " + "loottable_id = %i, merchant_id = %i, face = %i, WHERE id = %i", + spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, spawn->GetNPCTypeID()); + auto results = QueryDatabase(query); + if (!results.Success() && client) + client->LogSQL(query.c_str()); + + return results.Success() == true? 1: 0; +} + +uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn) { + uint32 id = 0; + uint32 spawngroupID = 0; + + std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE " + "zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + if (row[0]) + id = atoi(row[0]); + + if (row[1]) + spawngroupID = atoi(row[1]); + + query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + return 1; +} + +uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn) { + + uint32 id = 0; + uint32 spawngroupID = 0; + + std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE zone = '%s' " + "AND version = %u AND spawngroupID = %i", + zone, zone_version, spawn->GetSp2()); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + if (row[0]) + id = atoi(row[0]); + + if (row[1]) + spawngroupID = atoi(row[1]); + + query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM npc_types WHERE id = '%i'", spawn->GetNPCTypeID()); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + return 1; +} + +uint32 ZoneDatabase::AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { + uint32 last_insert_id = 0; + std::string query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, client->GetX(), client->GetY(), client->GetZ(), + 120, client->GetHeading(), spawnGroupID); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + return 1; +} + +uint32 ZoneDatabase::AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { + + uint32 npc_type_id; + char numberlessName[64]; + + EntityList::RemoveNumbers(strn0cpy(numberlessName, spawn->GetName(), sizeof(numberlessName))); + std::string query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + numberlessName, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + npc_type_id = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + if(client) + client->Message(0, "%s npc_type ID %i created successfully!", numberlessName, npc_type_id); + + return 1; +} + +uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { + switch (command) { case 0: { // Create a new NPC and add all spawn related data - uint32 npc_type_id = 0; - uint32 spawngroupid; - if (extra && c && c->GetZoneID()) - { - // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) - int starting_npc_id = c->GetZoneID() * 1000; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) - { - if (row[0]) - { - npc_type_id = atoi(row[0]) + 1; - // Prevent the npc_type id from exceeding the range for this zone - if (npc_type_id >= (starting_npc_id + 1000)) - { - npc_type_id = 0; - } - } - else - { - // row[0] is nullptr - No npc_type IDs set in this range yet - npc_type_id = starting_npc_id; - } - } - - safe_delete_array(query); - mysql_free_result(result); - } - } - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (npc_type_id) - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - if(c) c->LogSQL(query); - safe_delete_array(query); - snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; + return CreateNewNPCCommand(zone, zone_version, c, spawn, extra); } case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID - tmp2 = spawn->GetNPCTypeID(); - char tmpstr[64]; - snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - uint32 respawntime = 0; - uint32 spawnid = 0; - if (extra) - respawntime = extra; - else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) - respawntime = spawn->respawn2->RespawnTimer(); - else - respawntime = 1200; - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return spawnid; - break; + return AddNewNPCSpawnGroupCommand(zone, zone_version, c, spawn, extra); } case 2: { // Update npc_type appearance and other data on targeted spawn - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - } - else { - safe_delete_array(query); - return false; - } - break; + return UpdateNPCTypeAppearance(c, spawn); } case 3: { // delete spawn from spawning, but leave in npc_types table - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return 0; - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - - - break; + return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn); } case 4: { //delete spawn from DB (including npc_type) - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return(0); - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - mysql_free_result(result); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; + return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn); } case 5: { // add a spawn from spawngroup - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, c->GetX(), c->GetY(), c->GetZ(), 120, c->GetHeading(), extra), errbuf, 0, 0, &tmp)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - return true; - break; - } + return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra); + } case 6: { // add npc_type - uint32 npc_type_id; - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if(c) c->Message(0, "%s npc_type ID %i created successfully!", tmpstr, npc_type_id); - return true; - break; + return AddNPCTypes(zone, zone_version, c, spawn, extra); } } return false; @@ -1743,7 +1855,7 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) else ns->spawn.IsMercenary = 0; } - + //Not recommended if using above (However, this will work better on older clients). if (RuleB(Pets, UnTargetableSwarmPet)) { if(GetOwnerID() || GetSwarmOwner()) { diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 03da3bc35..04becfdc8 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -6020,6 +6020,32 @@ XS(XS_Client_SendMarqueeMessage) XSRETURN_EMPTY; } +XS(XS_Client_SendColoredText); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendColoredText) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SendColoredText(color, message)"); + { + Client * THIS; + uint32 color = (uint32)SvUV(ST(1)); + std::string msg = (std::string)SvPV_nolen(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendColoredText(color, msg); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6259,6 +6285,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$"); + newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$"); XSRETURN_YES; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 901456cd2..f3527b997 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -26,6 +26,7 @@ #include "string_ids.h" #include "../common/misc_functions.h" #include "../common/rulesys.h" +#include "../common/string_util.h" int Mob::GetKickDamage() { @@ -346,15 +347,23 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction; //Live AA - Technique of Master Wu - uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if (bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0, 99))) { - - int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; - MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); - - // always 1/4 of the double attack chance, 25% at rank 5 (100/4) - if ((bDoubleSpecialAttack / 4) > MakeRandomInt(0, 99)) - MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { + if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) { + int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; + int extra = 1; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + if (wuchance / 4 > MakeRandomInt(0, 99)) + extra++; + // They didn't add a string ID for this. + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + // live uses 400 here -- not sure if it's the best for all clients though + SendColoredText(400, msg); + while (extra) { + MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); + extra--; + } + } } if(ReuseTime < 100) { @@ -1743,18 +1752,21 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) return; //Live AA - Technique of Master Wu - uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if( bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0,100))) { - - int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; - MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]); - - int TripleChance = 25; - if (bDoubleSpecialAttack > 100) - TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100; - - if(TripleChance > MakeRandomInt(0,100)) { - MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]); + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { + if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) { + int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; + int extra = 1; + if (wuchance / 4 > MakeRandomInt(0, 99)) + extra++; + // They didn't add a string ID for this. + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + // live uses 400 here -- not sure if it's the best for all clients though + SendColoredText(400, msg); + while (extra) { + MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0, 4)]); + extra--; + } } } } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f5509ece2..add06714b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -3205,55 +3205,55 @@ snare has both of them negative, yet their range should work the same: case 124: // check sign result = ubase; if (caster_level > 50) - result += caster_level - 50; + result += updownsign * (caster_level - 50); break; case 125: // check sign result = ubase; if (caster_level > 50) - result += 2 * (caster_level - 50); + result += updownsign * 2 * (caster_level - 50); break; case 126: // check sign result = ubase; if (caster_level > 50) - result += 3 * (caster_level - 50); + result += updownsign * 3 * (caster_level - 50); break; case 127: // check sign result = ubase; if (caster_level > 50) - result += 4 * (caster_level - 50); + result += updownsign * 4 * (caster_level - 50); break; case 128: // check sign result = ubase; if (caster_level > 50) - result += 5 * (caster_level - 50); + result += updownsign * 5 * (caster_level - 50); break; case 129: // check sign result = ubase; if (caster_level > 50) - result += 10 * (caster_level - 50); + result += updownsign * 10 * (caster_level - 50); break; case 130: // check sign result = ubase; if (caster_level > 50) - result += 15 * (caster_level - 50); + result += updownsign * 15 * (caster_level - 50); break; case 131: // check sign result = ubase; if (caster_level > 50) - result += 20 * (caster_level - 50); + result += updownsign * 20 * (caster_level - 50); break; case 132: // check sign result = ubase; if (caster_level > 50) - result += 25 * (caster_level - 50); + result += updownsign * 25 * (caster_level - 50); break; case 137: // used in berserker AA desperation diff --git a/zone/spells.cpp b/zone/spells.cpp index a18eabe20..fb3cba454 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -484,6 +484,19 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, return false; } + // This needs a bit more work for saving timer to PP for zoning etc + if (IsClient() && item_slot != INVALID_INDEX && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) { + ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot); + if (itm && itm->GetItem()->RecastDelay) { + outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); + ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; + ird->recast_delay = itm->GetItem()->RecastDelay; + ird->recast_type = itm->GetItem()->RecastType; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + } + } + return(true); } diff --git a/zone/string_ids.h b/zone/string_ids.h index a443fabd9..56e1c4043 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -275,6 +275,7 @@ #define TOLD_NOT_ONLINE 5046 //%1 is not online at this time. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. +#define ALREADY_IN_RAID 5060 //%1 is already in a raid. #define GAIN_RAIDEXP 5085 //You gained raid experience! #define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there. #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. diff --git a/zone/trading.cpp b/zone/trading.cpp index 376d2cb14..738e763ed 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -1615,36 +1615,36 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat } -void Client::SendBazaarWelcome(){ - +void Client::SendBazaarWelcome() +{ const std::string query = "SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader"; - auto results = database.QueryDatabase(query); + auto results = database.QueryDatabase(query); if (results.Success() && results.RowCount() == 1){ - auto row = results.begin(); + auto row = results.begin(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); - memset(outapp->pBuffer,0,outapp->size); + memset(outapp->pBuffer,0,outapp->size); - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; + BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; - bws->Beginning.Action = BazaarWelcome; + bws->Beginning.Action = BazaarWelcome; - bws->Traders = atoi(row[0]); - bws->Items = atoi(row[1]); + bws->Traders = atoi(row[0]); + bws->Items = atoi(row[1]); - QueuePacket(outapp); + QueuePacket(outapp); - safe_delete(outapp); - } + safe_delete(outapp); + } - const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer"; - results = database.QueryDatabase(query); + const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer"; + results = database.QueryDatabase(buyerCountQuery); if (!results.Success() || results.RowCount() != 1) - return; + return; - auto row = results.begin(); - Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, " + auto row = results.begin(); + Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, " "or use /buyer to set up your own Buy Lines.", atoi(row[0])); } diff --git a/zone/zonedb.h b/zone/zonedb.h index b6577cf42..30721de9a 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -376,6 +376,13 @@ public: /* NPCs */ const NPCType* GetNPCType(uint32 id); uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete + uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); + uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); + uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn); + uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn); + uint32 AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); + uint32 AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); + uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into);