diff --git a/changelog.txt b/changelog.txt index 23640f7e5..a006c9a50 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,33 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 09/16/2014 == +demonstar55: Implement spell formula 137 (BER AA Desperation) +Uleat (NateDog): Fix for LoadBuffs() crash when a spell with a non-persistent Illusion effect was loaded. +demonstar55: Fix some effect calcs + implement more (derived from the client) + +== 09/15/2014 == +Kayen: Nimbus effects will now be reapplied after zoning and will be removed when associated buff fades. + +== 09/13/2014 == +demonstar55: Fix rogues not having Thieves' Cant + +== 09/09/2014 == +demonstar55: Incrase Mob kick/bash timer by 3 + see: http://www.eqemulator.org/forums/showthread.php?t=38734 +demonstar55: Fix slow effect on NPC special attack reuse timers + see: http://www.eqemulator.org/forums/showthread.php?t=38734 +demonstar55: Slow fixes to bots! +demonstar55: Revamped how NPC attack rate is set + SQL: 2014_09_09_attack_delay.sql +demonstar55: Added attackdelay to #npcedit + +== 09/08/2014 == +demonstar55: Fix slow calc + see: http://www.eqemulator.org/forums/showthread.php?t=38734 + +== 09/07/2014 == +Akkadius: Fixed ROF Augment item dupe with not checking for available slots properly and adding items to the virtual instance + == 09/06/2014 == Uleat: Tweaked 'Smart' trading code to return main slots before sub slots in stackable and free space search processes. (If enough people ask for it, I'll add an optional rule to allow 'bag packing' - the original implementation behavior) diff --git a/common/database.cpp b/common/database.cpp index a3a279af5..c90439ff2 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -272,7 +272,7 @@ bool Database::DeleteAccount(const char* name) { } bool Database::SetLocalPassword(uint32 accid, const char* password) { - std::string query = StringFormat("UPDATE `account` SET `password` = MD5('%s') where id=%i;", password, accid); + std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", EscapeString(password).c_str(), accid); auto results = QueryDatabase(query); diff --git a/common/item.cpp b/common/item.cpp index 65f57dbf9..513ddb23f 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -1582,13 +1582,12 @@ int8 ItemInst::AvailableAugmentSlot(int32 augtype) const bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const { if (m_item->ItemClass != ItemClassCommon || !m_item) - return -1; + return false; if ((!GetItem(slot) && m_item->AugSlotVisible[slot]) && augtype == -1 || (m_item->AugSlotType[slot] && ((1 << (m_item->AugSlotType[slot] - 1)) & augtype))) { return true; } - - return false; + return false; } // Retrieve item inside container diff --git a/common/ruletypes.h b/common/ruletypes.h index 73d538f8a..c61590ac4 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -212,10 +212,6 @@ RULE_REAL ( Map, FixPathingZMaxDeltaMoving, 20 ) //at runtime while pathing: max RULE_REAL ( Map, FixPathingZMaxDeltaWaypoint, 20 ) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply. RULE_REAL ( Map, FixPathingZMaxDeltaSendTo, 20 ) //at runtime in SendTo: max change in Z to allow the BestZ code to apply. RULE_REAL ( Map, FixPathingZMaxDeltaLoading, 45 ) //while loading each waypoint: max change in Z to allow the BestZ code to apply. -RULE_BOOL ( Map, UseClosestZ, false) // Move mobs to the nearest Z above or below, rather than just the nearest below. - // Only set UseClosestZ true if all your .map files generated from EQGs were created - // with azone2. - // RULE_INT ( Map, FindBestZHeightAdjust, 1) // Adds this to the current Z before seeking the best Z position RULE_CATEGORY_END() diff --git a/queryserv/database.cpp b/queryserv/database.cpp index cfc4a8892..f2dfebb56 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include #include @@ -96,191 +96,253 @@ Close the connection to the database Database::~Database() { } - + void Database::AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - char *S1 = new char[strlen(from) * 2 + 1]; - char *S2 = new char[strlen(to) * 2 + 1]; - char *S3 = new char[strlen(message) * 2 + 1]; - DoEscapeString(S1, from, strlen(from)); - DoEscapeString(S2, to, strlen(to)); - DoEscapeString(S3, message, strlen(message)); + char *escapedFrom = new char[strlen(from) * 2 + 1]; + char *escapedTo = new char[strlen(to) * 2 + 1]; + char *escapedMessage = new char[strlen(message) * 2 + 1]; + DoEscapeString(escapedFrom, from, strlen(from)); + DoEscapeString(escapedTo, to, strlen(to)); + DoEscapeString(escapedMessage, message, strlen(message)); - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_speech` SET `from`='%s', `to`='%s', `message`='%s', `minstatus`='%i', `guilddbid`='%i', `type`='%i'", S1, S2, S3, minstatus, guilddbid, type), errbuf, 0, 0)) { - _log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); + std::string query = StringFormat("INSERT INTO `qs_player_speech` " + "SET `from` = '%s', `to` = '%s', `message`='%s', " + "`minstatus`='%i', `guilddbid`='%i', `type`='%i'", + escapedFrom, escapedTo, escapedMessage, minstatus, guilddbid, type); + safe_delete_array(escapedFrom); + safe_delete_array(escapedTo); + safe_delete_array(escapedMessage); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - safe_delete_array(query); - safe_delete_array(S1); - safe_delete_array(S2); - safe_delete_array(S3); + } -void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount) { +void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record` SET `time`=NOW(), " - "`char1_id`='%i', `char1_pp`='%i', `char1_gp`='%i', `char1_sp`='%i', `char1_cp`='%i', `char1_items`='%i', " - "`char2_id`='%i', `char2_pp`='%i', `char2_gp`='%i', `char2_sp`='%i', `char2_cp`='%i', `char2_items`='%i'", - QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, QS->char1_money.silver, QS->char1_money.copper, QS->char1_count, - QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, QS->char2_money.silver, QS->char2_money.copper, QS->char2_count), - errbuf, 0, 0, &lastid)) { - _log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); + std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), " + "`char1_id` = '%i', `char1_pp` = '%i', `char1_gp` = '%i', " + "`char1_sp` = '%i', `char1_cp` = '%i', `char1_items` = '%i', " + "`char2_id` = '%i', `char2_pp` = '%i', `char2_gp` = '%i', " + "`char2_sp` = '%i', `char2_cp` = '%i', `char2_items` = '%i'", + QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, + QS->char1_money.silver, QS->char1_money.copper, QS->char1_count, + QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, + QS->char2_money.silver, QS->char2_money.copper, QS->char2_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - if(DetailCount > 0) { - for(int i = 0; i < DetailCount; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record_entries` SET `event_id`='%i', " - "`from_id`='%i', `from_slot`='%i', `to_id`='%i', `to_slot`='%i', `item_id`='%i', " - "`charges`='%i', `aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].from_id, QS->items[i].from_slot, QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id, - QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } + if(detailCount == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < detailCount; i++) { + query = StringFormat("INSERT INTO `qs_player_trade_record_entries` SET `event_id` = '%i', " + "`from_id` = '%i', `from_slot` = '%i', `to_id` = '%i', `to_slot` = '%i', " + "`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', " + "`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].from_id, QS->items[i].from_slot, + QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + +} + +void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 detailCount) { + + std::string query = StringFormat("INSERT INTO `qs_player_handin_record` SET `time` = NOW(), " + "`quest_id` = '%i', `char_id` = '%i', `char_pp` = '%i', " + "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " + "`char_items` = '%i', `npc_id` = '%i', `npc_pp` = '%i', " + "`npc_gp` = '%i', `npc_sp` = '%i', `npc_cp` = '%i', " + "`npc_items`='%i'", + QS->quest_id, QS->char_id, QS->char_money.platinum, + QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, + QS->char_count, QS->npc_id, QS->npc_money.platinum, + QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, + QS->npc_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + if(detailCount == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < detailCount; i++) { + query = StringFormat("INSERT INTO `qs_player_handin_record_entries` SET `event_id` = '%i', " + "`action_type` = '%s', `char_slot` = '%i', `item_id` = '%i', " + "`charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', " + "`aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].action_type, QS->items[i].char_slot, + QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, + QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + } + +} + +void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 members){ + + std::string query = StringFormat("INSERT INTO `qs_player_npc_kill_record` " + "SET `npc_id` = '%i', `type` = '%i', " + "`zone_id` = '%i', `time` = NOW()", + QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + if(members == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for (int i = 0; i < members; i++) { + query = StringFormat("INSERT INTO `qs_player_npc_kill_record_entries` " + "SET `event_id` = '%i', `char_id` = '%i'", + lastIndex, QS->Chars[i].char_id); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } + } + } -void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record` SET `time`=NOW(), `quest_id`='%i', " - "`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i', " - "`npc_id`='%i', `npc_pp`='%i', `npc_gp`='%i', `npc_sp`='%i', `npc_cp`='%i', `npc_items`='%i'", - QS->quest_id, QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count, - QS->npc_id, QS->npc_money.platinum, QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, QS->npc_count), - errbuf, 0, 0, &lastid)) { - _log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); +void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 items) { + + std::string query = StringFormat("INSERT INTO `qs_player_delete_record` SET `time` = NOW(), " + "`char_id` = '%i', `stack_size` = '%i', `char_items` = '%i'", + QS->char_id, QS->stack_size, QS->char_count, QS->char_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - if(DetailCount > 0) { - for(int i = 0; i < DetailCount; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record_entries` SET `event_id`='%i', " - "`action_type`='%s', `char_slot`='%i', `item_id`='%i', `charges`='%i', " - "`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].action_type, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, - QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - } - } + if(items == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < items; i++) { + query = StringFormat("INSERT INTO `qs_player_delete_record_entries` SET `event_id` = '%i', " + "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " + "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, + QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + } -void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record` SET `npc_id`='%i', `type`='%i', `zone_id`='%i', `time`=NOW()", QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID), errbuf, 0, 0, &lastid)) { - _log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - - if(Members > 0){ - for (int i = 0; i < Members; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record_entries` SET `event_id`='%i', `char_id`='%i'", lastid, QS->Chars[i].char_id, errbuf, 0, 0))) { - _log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - } - } -} - -void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record` SET `time`=NOW(), " - "`char_id`='%i', `stack_size`='%i', `char_items`='%i'", - QS->char_id, QS->stack_size, QS->char_count, QS->char_count), - errbuf, 0, 0, &lastid)) { - _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record_entries` SET `event_id`='%i', " - "`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', " - "`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, - QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - } - } -} - -void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) { +void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 items) { /* These are item moves */ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record` SET `time`=NOW(), " - "`char_id`='%i', `from_slot`='%i', `to_slot`='%i', `stack_size`='%i', `char_items`='%i', `postaction`='%i'", - QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, QS->char_count, QS->postaction), - errbuf, 0, 0, &lastid)) { - _log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record_entries` SET `event_id`='%i', " - "`from_slot`='%i', `to_slot`='%i', `item_id`='%i', `charges`='%i', " - "`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", lastid, - QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, QS->items[i].charges, - QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - } + + std::string query = StringFormat("INSERT INTO `qs_player_move_record` SET `time` = NOW(), " + "`char_id` = '%i', `from_slot` = '%i', `to_slot` = '%i', " + "`stack_size` = '%i', `char_items` = '%i', `postaction` = '%i'", + QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, + QS->char_count, QS->postaction); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } + + if(items == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < items; i++) { + query = StringFormat("INSERT INTO `qs_player_move_record_entries` SET `event_id` = '%i', " + "`from_slot` = '%i', `to_slot` = '%i', `item_id` = '%i', `charges` = '%i', " + "`aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + } -void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items) { +void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 items) { /* Merchant transactions are from the perspective of the merchant, not the player -U */ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record` SET `time`=NOW(), " - "`zone_id`='%i', `merchant_id`='%i', `merchant_pp`='%i', `merchant_gp`='%i', `merchant_sp`='%i', `merchant_cp`='%i', `merchant_items`='%i', " - "`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i'", - QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, QS->merchant_money.gold, QS->merchant_money.silver, QS->merchant_money.copper, QS->merchant_count, - QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count), - errbuf, 0, 0, &lastid)) { - _log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); + std::string query = StringFormat("INSERT INTO `qs_merchant_transaction_record` SET `time` = NOW(), " + "`zone_id` = '%i', `merchant_id` = '%i', `merchant_pp` = '%i', " + "`merchant_gp` = '%i', `merchant_sp` = '%i', `merchant_cp` = '%i', " + "`merchant_items` = '%i', `char_id` = '%i', `char_pp` = '%i', " + "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " + "`char_items` = '%i'", + QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, + QS->merchant_money.gold, QS->merchant_money.silver, + QS->merchant_money.copper, QS->merchant_count, QS->char_id, + QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, + QS->char_money.copper, QS->char_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id`='%i', " - "`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', " - "`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, - QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); - } - } - } - safe_delete_array(query); + if(items == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < items; i++) { + query = StringFormat("INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id` = '%i', " + "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " + "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, + QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + } void Database::GeneralQueryReceive(ServerPacket *pack) { @@ -293,11 +355,11 @@ void Database::GeneralQueryReceive(ServerPacket *pack) { char errbuf[MYSQL_ERRMSG_SIZE]; char* query = 0; uint32 lastid = 0; - if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) { + if (!RunQuery(query, MakeAnyLenString(&query, Query), errbuf, 0, 0, &lastid)) { _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", errbuf); - _log(QUERYSERV__ERROR, "%s", query); + _log(QUERYSERV__ERROR, "%s", query); } safe_delete_array(query); - safe_delete(pack); - safe_delete(Query); + safe_delete(pack); + safe_delete(Query); } diff --git a/utils/sql/git/required/2014_09_09_attack_delay.sql b/utils/sql/git/required/2014_09_09_attack_delay.sql new file mode 100644 index 000000000..87efa043a --- /dev/null +++ b/utils/sql/git/required/2014_09_09_attack_delay.sql @@ -0,0 +1,3 @@ +ALTER TABLE `npc_types` ADD `attack_delay` TINYINT(3) UNSIGNED DEFAULT '30' NOT NULL AFTER `attack_speed`; +UPDATE `npc_types` SET `attack_delay` = 36 + 36 * (`attack_speed` / 100); +UPDATE `npc_types` SET `attack_delay` = 30 WHERE `attack_speed` = 0; diff --git a/world/client.cpp b/world/client.cpp index 93e0f782b..73c4a9493 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1402,6 +1402,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) { SetRacialLanguages( &pp ); SetRaceStartingSkills( &pp ); SetClassStartingSkills( &pp ); + SetClassLanguages(&pp); pp.skills[SkillSenseHeading] = 200; // Some one fucking fix this to use a field name. -Doodman //pp.unknown3596[28] = 15; // @bp: This is to enable disc usage @@ -1981,3 +1982,15 @@ void Client::SetRacialLanguages( PlayerProfile_Struct *pp ) } } +void Client::SetClassLanguages(PlayerProfile_Struct *pp) +{ + // we only need to handle one class, but custom server might want to do more + switch(pp->class_) { + case ROGUE: + pp->languages[LANG_THIEVES_CANT] = 100; + break; + default: + break; + } +} + diff --git a/world/client.h b/world/client.h index bdf1afbec..9014051ed 100644 --- a/world/client.h +++ b/world/client.h @@ -90,6 +90,7 @@ private: void SetClassStartingSkills( PlayerProfile_Struct *pp ); void SetRaceStartingSkills( PlayerProfile_Struct *pp ); void SetRacialLanguages( PlayerProfile_Struct *pp ); + void SetClassLanguages(PlayerProfile_Struct *pp); ClientListEntry* cle; Timer CLE_keepalive_timer; diff --git a/zone/attack.cpp b/zone/attack.cpp index 5abba347b..a515a08c4 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3516,7 +3516,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons if(spell_id == SPELL_UNKNOWN) { damage = ReduceDamage(damage); mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage); - ReduceAllDamage(damage); + damage = ReduceAllDamage(damage); TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker); } else { int32 origdmg = damage; @@ -3529,7 +3529,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons //Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes. Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName()); } - ReduceAllDamage(damage); + damage = ReduceAllDamage(damage); TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker); } @@ -4803,3 +4803,145 @@ void Mob::CommonBreakInvisible() hidden = false; improved_hidden = false; } + +void Mob::SetAttackTimer() +{ + attack_timer.SetAtTrigger(4000, true); +} + +void Client::SetAttackTimer() +{ + float PermaHaste = GetPermaHaste(); + + //default value for attack timer in case they have + //an invalid weapon equipped: + attack_timer.SetAtTrigger(4000, true); + + Timer *TimerToUse = nullptr; + const Item_Struct *PrimaryWeapon = nullptr; + + for (int i = MainRange; i <= MainSecondary; i++) { + //pick a timer + if (i == MainPrimary) + TimerToUse = &attack_timer; + else if (i == MainRange) + TimerToUse = &ranged_timer; + else if (i == MainSecondary) + TimerToUse = &attack_dw_timer; + else //invalid slot (hands will always hit this) + continue; + + const Item_Struct *ItemToUse = nullptr; + + //find our item + ItemInst *ci = GetInv().GetItem(i); + if (ci) + ItemToUse = ci->GetItem(); + + //special offhand stuff + if (i == MainSecondary) { + //if we have a 2H weapon in our main hand, no dual + if (PrimaryWeapon != nullptr) { + if (PrimaryWeapon->ItemClass == ItemClassCommon + && (PrimaryWeapon->ItemType == ItemType2HSlash + || PrimaryWeapon->ItemType == ItemType2HBlunt + || PrimaryWeapon->ItemType == ItemType2HPiercing)) { + attack_dw_timer.Disable(); + continue; + } + } + + //if we cant dual wield, skip it + if (!CanThisClassDualWield()) { + attack_dw_timer.Disable(); + continue; + } + } + + //see if we have a valid weapon + if (ItemToUse != nullptr) { + //check type and damage/delay + if (ItemToUse->ItemClass != ItemClassCommon + || ItemToUse->Damage == 0 + || ItemToUse->Delay == 0) { + //no weapon + ItemToUse = nullptr; + } + // Check to see if skill is valid + else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) && + (ItemToUse->ItemType != ItemTypeMartial) && + (ItemToUse->ItemType != ItemType2HPiercing)) { + //no weapon + ItemToUse = nullptr; + } + } + + int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + int speed = 0; + + //if we have no weapon.. + if (ItemToUse == nullptr) { + //above checks ensure ranged weapons do not fall into here + // Work out if we're a monk + if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) + speed = static_cast((GetMonkHandToHandDelay() * (100 + DelayMod) / 100) * PermaHaste); + else + speed = static_cast((36 * (100 + DelayMod) / 100) * PermaHaste); + } else { + //we have a weapon, use its delay + // Convert weapon delay to timer resolution (milliseconds) + //delay * 100 + speed = static_cast((ItemToUse->Delay * (100 + DelayMod) / 100) * PermaHaste); + if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) { + float quiver_haste = GetQuiverHaste(); + if (quiver_haste > 0) + speed *= quiver_haste; + } + } + TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); + + if (i == MainPrimary) + PrimaryWeapon = ItemToUse; + } +} + +void NPC::SetAttackTimer() +{ + float PermaHaste = GetPermaHaste(); + + //default value for attack timer in case they have + //an invalid weapon equipped: + attack_timer.SetAtTrigger(4000, true); + + Timer *TimerToUse = nullptr; + + for (int i = MainRange; i <= MainSecondary; i++) { + //pick a timer + if (i == MainPrimary) + TimerToUse = &attack_timer; + else if (i == MainRange) + TimerToUse = &ranged_timer; + else if (i == MainSecondary) + TimerToUse = &attack_dw_timer; + else //invalid slot (hands will always hit this) + continue; + + //special offhand stuff + if (i == MainSecondary) { + //NPCs get it for free at 13 + if(GetLevel() < 13) { + attack_dw_timer.Disable(); + continue; + } + } + + int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + + // Technically NPCs should do some logic for weapons, but the effect is minimal + // What they do is take the lower of their set delay and the weapon's + // ex. Mob's delay set to 20, weapon set to 19, delay 19 + // Mob's delay set to 20, weapon set to 21, delay 20 + int speed = static_cast((attack_delay * (100 + DelayMod) / 100) * PermaHaste); + TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); + } +} diff --git a/zone/bot.cpp b/zone/bot.cpp index 9d7dc508f..d001a0b88 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8350,12 +8350,10 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { return; float HasteModifier = 0; - if(GetHaste() >= 0){ + if (GetHaste()) HasteModifier = 10000 / (100 + GetHaste()); - } - else { - HasteModifier = (100 - GetHaste()); - } + else + HasteModifier = 100; int32 dmg = 0; uint16 skill_to_use = -1; @@ -8981,10 +8979,8 @@ int32 Bot::CalcMaxMana() { void Bot::SetAttackTimer() { float PermaHaste; - if(GetHaste() > 0) + if (GetHaste()) PermaHaste = 1 / (1 + (float)GetHaste()/100); - else if(GetHaste() < 0) - PermaHaste = 1 * (1 - (float)GetHaste()/100); else PermaHaste = 1.0f; diff --git a/zone/client.cpp b/zone/client.cpp index e4a91cb4d..11735ca6f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -3949,54 +3949,38 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const void Client::KeyRingLoad() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - - sprintf(query, "SELECT item_id FROM keyring WHERE char_id='%i' ORDER BY item_id",character_id); - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - while(0 != (row = mysql_fetch_row(result))){ - keyring.push_back(atoi(row[0])); - } - mysql_free_result(result); - }else { - std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT item_id FROM keyring " + "WHERE char_id = '%i' ORDER BY item_id", character_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << results.ErrorMessage() << std::endl; return; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) + keyring.push_back(atoi(row[0])); + } void Client::KeyRingAdd(uint32 item_id) { - if(0==item_id)return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - query = new char[256]; - bool bFound = KeyRingCheck(item_id); - if(!bFound){ - sprintf(query, "INSERT INTO keyring(char_id,item_id) VALUES(%i,%i)",character_id,item_id); - if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) { - Message(4,"Added to keyring."); + if(0==item_id) + return; - /* QS: PlayerLogKeyringAddition */ - if (RuleB(QueryServ, PlayerLogKeyringAddition)){ - std::string event_desc = StringFormat("itemid:%i in zoneid:%i instid:%i", item_id, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Keyring_Addition, this->CharacterID(), event_desc); - } - safe_delete_array(query); - } - else { - std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return; - } - keyring.push_back(item_id); - } + bool found = KeyRingCheck(item_id); + if (found) + return; + + std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in Doors::HandleClick query '" << query << "' " << results.ErrorMessage() << std::endl; + return; + } + + Message(4,"Added to keyring."); + + keyring.push_back(item_id); } bool Client::KeyRingCheck(uint32 item_id) @@ -8282,3 +8266,19 @@ void Client::ShowNumHits() return; } +float Client::GetQuiverHaste() +{ + float quiver_haste = 0; + for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) { + const ItemInst *pi = GetInv().GetItem(r); + if (!pi) + continue; + if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) { + float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv)); + quiver_haste = std::max(temp_wr, quiver_haste); + } + } + if (quiver_haste > 0) + quiver_haste = 1.0f / (1.0f + static_cast(quiver_haste) / 100.0f); + return quiver_haste; +} diff --git a/zone/client.h b/zone/client.h index 983c98915..e78c37c42 100644 --- a/zone/client.h +++ b/zone/client.h @@ -218,6 +218,8 @@ public: virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); } virtual inline bool IsBerserk() { return berserk; } virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); + virtual void SetAttackTimer(); + float GetQuiverHaste(); void AI_Init(); void AI_Start(uint32 iMoveDelay = 0); @@ -1193,9 +1195,9 @@ public: int32 mod_client_xp(int32 in_exp, NPC *npc); uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level); int mod_client_haste_cap(int cap); - int mod_consume(Item_Struct *item, ItemUseTypes type, int change); - int mod_food_value(const Item_Struct *item, int change); - int mod_drink_value(const Item_Struct *item, int change); + int mod_consume(Item_Struct *item, ItemUseTypes type, int change); + int mod_food_value(const Item_Struct *item, int change); + int mod_drink_value(const Item_Struct *item, int change); void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; } bool GetEngagedRaidTarget() const { return EngagedRaidTarget; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 59d43591e..a27b7dd05 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -6781,7 +6781,7 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) tobe_auged = user_inv.GetItem(slot_id); auged_with = user_inv.GetItem(MainCursor); - if(tobe_auged && auged_with) + if (tobe_auged && auged_with) { if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) @@ -9432,11 +9432,17 @@ void Client::CompleteConnect() { //reapply some buffs uint32 buff_count = GetMaxTotalSlots(); for (uint32 j1 = 0; j1 < buff_count; j1++) { - if (buffs[j1].spellid >(uint32)SPDAT_RECORDS) + if (!IsValidSpell(buffs[j1].spellid)) continue; const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; + int NimbusEffect = GetNimbusEffect(buffs[j1].spellid); + if(NimbusEffect) { + if(!IsNimbusEffectActive(NimbusEffect)) + SendSpellEffect(NimbusEffect, 500, 0, 1, 3000, true); + } + for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effectid[x1]) { case SE_IllusionCopy: diff --git a/zone/command.cpp b/zone/command.cpp index c1e304dd1..6f1cdeca3 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -6555,6 +6555,7 @@ void command_npcedit(Client *c, const Seperator *sep) c->Message(0, "#npcedit qglobal - Sets an NPC's quest global flag"); c->Message(0, "#npcedit limit - Sets an NPC's spawn limit counter"); c->Message(0, "#npcedit Attackspeed - Sets an NPC's attack speed modifier"); + c->Message(0, "#npcedit Attackdelay - Sets an NPC's attack delay"); c->Message(0, "#npcedit findable - Sets an NPC's findable flag"); c->Message(0, "#npcedit wep1 - Sets an NPC's primary weapon model"); c->Message(0, "#npcedit wep2 - Sets an NPC's secondary weapon model"); @@ -7056,6 +7057,15 @@ void command_npcedit(Client *c, const Seperator *sep) c->LogSQL(query); safe_delete_array(query); } + else if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 ) + { + char errbuf[MYSQL_ERRMSG_SIZE]; + char *query = 0; + c->Message(15,"NPCID %u now has attack_delay set to %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); + database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_delay=%i where id=%i",atoi(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); + c->LogSQL(query); + safe_delete_array(query); + } else if ( strcasecmp( sep->arg[1], "findable" ) == 0 ) { char errbuf[MYSQL_ERRMSG_SIZE]; diff --git a/zone/forage.cpp b/zone/forage.cpp index 4a39d508d..d1008031a 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -356,9 +356,11 @@ void Client::GoFish() inst = m_inv.GetItem(MainCursor); } - std::vector args; - args.push_back(inst); - parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args); + if(inst) { + std::vector args; + args.push_back(inst); + parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args); + } } } else @@ -470,9 +472,11 @@ void Client::ForageItem(bool guarantee) { inst = m_inv.GetItem(MainCursor); } - std::vector args; - args.push_back(inst); - parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args); + if(inst) { + std::vector args; + args.push_back(inst); + parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args); + } } int ChanceSecondForage = aabonuses.ForageAdditionalItems + itembonuses.ForageAdditionalItems + spellbonuses.ForageAdditionalItems; diff --git a/zone/groups.cpp b/zone/groups.cpp index 5c34cf359..c73b8f093 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -561,6 +561,18 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) } } + /* This may seem pointless but the case above does not cover the following situation: + * Group has Leader a, member b, member c + * b and c are out of zone + * a disconnects/quits + * b or c zone back in and disconnects/quits + * a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b) + * Ultimately we should think up a better solution to this. + */ + if(oldmember == GetLeader()) { + SetLeader(nullptr); + } + ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; gl->gid = GetID(); diff --git a/zone/guild.cpp b/zone/guild.cpp index 6512bedec..5f8378d2b 100644 --- a/zone/guild.cpp +++ b/zone/guild.cpp @@ -165,7 +165,7 @@ void Client::SendGuildSpawnAppearance() { switch (rank) { case 0: { rank = 5; break; } // GUILD_MEMBER 0 case 1: { rank = 3; break; } // GUILD_OFFICER 1 - case 2: { rank = 1; break; } // GUILD_LEADER 2 + case 2: { rank = 1; break; } // GUILD_LEADER 2 default: { break; } // GUILD_NONE } } @@ -417,57 +417,36 @@ void Client::GuildChangeRank(const char* name, uint32 guild_id, uint32 oldrank, }*/ -bool ZoneDatabase::CheckGuildDoor(uint8 doorid,uint16 guild_id,const char* zone) { - MYSQL_ROW row; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - if (!RunQuery(query, MakeAnyLenString(&query, - "SELECT guild FROM doors where doorid=%i AND zone='%s'", - doorid-128, zone), errbuf, &result)) - { - LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } else { - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (atoi(row[0]) == guild_id) - { - mysql_free_result(result); - return true; - } - else - { - mysql_free_result(result); - return false; - } +bool ZoneDatabase::CheckGuildDoor(uint8 doorid, uint16 guild_id, const char* zone) { - // code below will never be reached - mysql_free_result(result); - return false; - } + std::string query = StringFormat("SELECT guild FROM doors WHERE doorid = %i AND zone = '%s'", + doorid-128, zone); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - return false; + + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + return atoi(row[0]) == guild_id; } bool ZoneDatabase::SetGuildDoor(uint8 doorid,uint16 guild_id, const char* zone) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; + if (doorid > 127) doorid = doorid - 128; - if (!RunQuery(query, MakeAnyLenString(&query, - "UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')", - guild_id, doorid, zone), errbuf, 0,&affected_rows)) - { - LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query, errbuf); - safe_delete_array(query); + + std::string query = StringFormat("UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')", + guild_id, doorid, zone); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - return(affected_rows > 0); + return (results.RowsAffected() > 0); } diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index 712d210d5..bad060aef 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -850,24 +850,16 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32 return false; } - const char *Query="INSERT INTO `guild_bank` (`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) " - "VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* query = 0; - - if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor), errbuf)) - { - _log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query, errbuf); - - safe_delete_array(query); - + std::string query = StringFormat("INSERT INTO `guild_bank` " + "(`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) " + "VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')", + GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + _log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - const Item_Struct *Item = database.GetItem(ItemID); GuildBankItemUpdate_Struct gbius; diff --git a/zone/mob.cpp b/zone/mob.cpp index 135318cbd..77ff4938a 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -174,8 +174,9 @@ Mob::Mob(const char* in_name, drakkin_heritage = in_drakkin_heritage; drakkin_tattoo = in_drakkin_tattoo; drakkin_details = in_drakkin_details; - attack_speed= 0; - slow_mitigation= 0; + attack_speed = 0; + attack_delay = 0; + slow_mitigation = 0; findable = false; trackable = true; has_shieldequiped = false; @@ -1940,159 +1941,6 @@ void Mob::Kill() { Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand); } -void Mob::SetAttackTimer() { - float PermaHaste; - if(GetHaste() > 0) - PermaHaste = 1 / (1 + (float)GetHaste()/100); - else if(GetHaste() < 0) - PermaHaste = 1 * (1 - (float)GetHaste()/100); - else - PermaHaste = 1.0f; - - //default value for attack timer in case they have - //an invalid weapon equipped: - attack_timer.SetAtTrigger(4000, true); - - Timer* TimerToUse = nullptr; - const Item_Struct* PrimaryWeapon = nullptr; - - for (int i=MainRange; i<=MainSecondary; i++) { - - //pick a timer - if (i == MainPrimary) - TimerToUse = &attack_timer; - else if (i == MainRange) - TimerToUse = &ranged_timer; - else if(i == MainSecondary) - TimerToUse = &attack_dw_timer; - else //invalid slot (hands will always hit this) - continue; - - const Item_Struct* ItemToUse = nullptr; - - //find our item - if (IsClient()) { - ItemInst* ci = CastToClient()->GetInv().GetItem(i); - if (ci) - ItemToUse = ci->GetItem(); - } else if(IsNPC()) - { - //The code before here was fundementally flawed because equipment[] - //isn't the same as PC inventory and also: - //NPCs don't use weapon speed to dictate how fast they hit anyway. - ItemToUse = nullptr; - } - - //special offhand stuff - if(i == MainSecondary) { - //if we have a 2H weapon in our main hand, no dual - if(PrimaryWeapon != nullptr) { - if( PrimaryWeapon->ItemClass == ItemClassCommon - && (PrimaryWeapon->ItemType == ItemType2HSlash - || PrimaryWeapon->ItemType == ItemType2HBlunt - || PrimaryWeapon->ItemType == ItemType2HPiercing)) { - attack_dw_timer.Disable(); - continue; - } - } - - //clients must have the skill to use it... - if(IsClient()) { - //if we cant dual wield, skip it - if (!CanThisClassDualWield()) { - attack_dw_timer.Disable(); - continue; - } - } else { - //NPCs get it for free at 13 - if(GetLevel() < 13) { - attack_dw_timer.Disable(); - continue; - } - } - } - - //see if we have a valid weapon - if(ItemToUse != nullptr) { - //check type and damage/delay - if(ItemToUse->ItemClass != ItemClassCommon - || ItemToUse->Damage == 0 - || ItemToUse->Delay == 0) { - //no weapon - ItemToUse = nullptr; - } - // Check to see if skill is valid - else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) { - //no weapon - ItemToUse = nullptr; - } - } - - int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands; - if (DelayMod < -99) - DelayMod = -99; - - //if we have no weapon.. - if (ItemToUse == nullptr) { - //above checks ensure ranged weapons do not fall into here - // Work out if we're a monk - if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) { - //we are a monk, use special delay - int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - // 1200 seemed too much, with delay 10 weapons available - if(speed < RuleI(Combat, MinHastedDelay)) //lower bound - speed = RuleI(Combat, MinHastedDelay); - TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic - } else { - //not a monk... using fist, regular delay - int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound - speed = RuleI(Combat, MinHastedDelay); - TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36 - } - } else { - //we have a weapon, use its delay - // Convert weapon delay to timer resolution (milliseconds) - //delay * 100 - int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - if(speed < RuleI(Combat, MinHastedDelay)) - speed = RuleI(Combat, MinHastedDelay); - - if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing)) - { - if(IsClient()) - { - float max_quiver = 0; - for(int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) - { - const ItemInst *pi = CastToClient()->GetInv().GetItem(r); - if(!pi) - continue; - if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) - { - float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) ); - if(temp_wr > max_quiver) - { - max_quiver = temp_wr; - } - } - } - if(max_quiver > 0) - { - float quiver_haste = 1 / (1 + max_quiver / 100); - speed *= quiver_haste; - } - } - } - TimerToUse->SetAtTrigger(speed, true); - } - - if(i == MainPrimary) - PrimaryWeapon = ItemToUse; - } - -} - bool Mob::CanThisClassDualWield(void) const { if(!IsClient()) { return(GetSkill(SkillDualWield) > 0); @@ -4528,6 +4376,15 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID) void Mob::RemoveNimbusEffect(int effectid) { + if (effectid == nimbus_effect1) + nimbus_effect1 = 0; + + else if (effectid == nimbus_effect2) + nimbus_effect2 = 0; + + else if (effectid == nimbus_effect3) + nimbus_effect3 = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct)); RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer; rne->spawnid = GetID(); @@ -5068,7 +4925,7 @@ void Mob::ClearSpecialAbilities() { } } -void Mob::ProcessSpecialAbilities(const std::string str) { +void Mob::ProcessSpecialAbilities(const std::string &str) { ClearSpecialAbilities(); std::vector sp = SplitString(str, '^'); diff --git a/zone/mob.h b/zone/mob.h index 844a1f093..4e72f0264 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -684,6 +684,7 @@ public: inline bool GetInvul(void) { return invulnerable; } inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; } virtual int GetHaste(); + inline float GetPermaHaste() { return GetHaste() ? 100.0f / (1.0f + static_cast(GetHaste()) / 100.0f) : 100.0f; } uint8 GetWeaponDamageBonus(const Item_Struct* Weapon); uint16 GetDamageTable(SkillUseTypes skillinuse); @@ -842,7 +843,7 @@ public: void StopSpecialAbilityTimer(int ability); Timer *GetSpecialAbilityTimer(int ability); void ClearSpecialAbilities(); - void ProcessSpecialAbilities(const std::string str); + void ProcessSpecialAbilities(const std::string &str); Shielders_Struct shielder[MAX_SHIELDERS]; Trade* trade; @@ -1052,6 +1053,7 @@ protected: Timer attack_dw_timer; Timer ranged_timer; float attack_speed; //% increase/decrease in attack speed (not haste) + int8 attack_delay; //delay between attacks in 10ths of seconds float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; diff --git a/zone/npc.cpp b/zone/npc.cpp index 151852a2b..a4ecaec3a 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -248,6 +248,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float delaytimer = false; combat_event = false; attack_speed = d->attack_speed; + attack_delay = d->attack_delay; slow_mitigation = d->slow_mitigation; EntityList::RemoveNumbers(name); @@ -935,6 +936,8 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, npc_type->WIS = 150; npc_type->CHA = 150; + npc_type->attack_delay = 30; + npc_type->prim_melee_type = 28; npc_type->sec_melee_type = 28; diff --git a/zone/npc.h b/zone/npc.h index 5b28260d4..b23522bee 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -134,7 +134,6 @@ public: void CalcNPCRegen(); void CalcNPCDamage(); - int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr); int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr); inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;} @@ -158,6 +157,7 @@ public: virtual void InitializeBuffSlots(); virtual void UninitializeBuffSlots(); + virtual void SetAttackTimer(); virtual void RangedAttack(Mob* other); virtual void ThrowingAttack(Mob* other) { } int32 GetNumberOfAttacks() const { return attack_count; } @@ -388,16 +388,16 @@ public: inline void SetHealScale(float amt) { healscale = amt; } inline float GetHealScale() { return healscale; } - uint32 GetSpawnKillCount(); - int GetScore(); - void SetMerchantProbability(uint8 amt) { probability = amt; } + uint32 GetSpawnKillCount(); + int GetScore(); + void SetMerchantProbability(uint8 amt) { probability = amt; } uint8 GetMerchantProbability() { return probability; } - void mod_prespawn(Spawn2 *sp); - int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other); + void mod_prespawn(Spawn2 *sp); + int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other); void mod_npc_killed_merit(Mob* c); void mod_npc_killed(Mob* oos); - void AISpellsList(Client *c); - + void AISpellsList(Client *c); + bool IsRaidTarget() const { return raid_target; }; protected: diff --git a/zone/qglobals.cpp b/zone/qglobals.cpp index 53f64670b..f63e13dd4 100644 --- a/zone/qglobals.cpp +++ b/zone/qglobals.cpp @@ -165,13 +165,12 @@ void QGlobalCache::LoadByGlobalContext() LoadBy(query); } -void QGlobalCache::LoadBy(const std::string query) +void QGlobalCache::LoadBy(const std::string &query) { - auto results = database.QueryDatabase(query); - if (!results.Success()) - return; - - for (auto row = results.begin(); row != results.end(); ++row) - AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]? atoi(row[5]): 0xFFFFFFFF)); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + for (auto row = results.begin(); row != results.end(); ++row) + AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]? atoi(row[5]): 0xFFFFFFFF)); } diff --git a/zone/qglobals.h b/zone/qglobals.h index b879cba97..5f0938a20 100644 --- a/zone/qglobals.h +++ b/zone/qglobals.h @@ -42,7 +42,7 @@ public: void LoadByZoneID(uint32 zoneID); //zone void LoadByGlobalContext(); //zone protected: - void LoadBy(const std::string query); + void LoadBy(const std::string &query); std::list qGlobalBucket; }; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 0431eb9b1..904dc41b3 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -1413,10 +1413,8 @@ void NPC::DoClassAttacks(Mob *target) { return; float HasteModifier = 0; - if(GetHaste() > 0) + if (GetHaste()) HasteModifier = 10000 / (100 + GetHaste()); - else if(GetHaste() < 0) - HasteModifier = (100 - GetHaste()); else HasteModifier = 100; @@ -1464,7 +1462,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = KickReuseTime * 1000; + reuse = (KickReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); did_attack = true; } @@ -1484,7 +1482,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = BashReuseTime * 1000; + reuse = (BashReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); did_attack = true; } @@ -1537,7 +1535,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = KickReuseTime * 1000; + reuse = (KickReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); did_attack = true; } @@ -1562,7 +1560,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = BashReuseTime * 1000; + reuse = (BashReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); did_attack = true; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b10c436f2..f0bc8498b 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -202,7 +202,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) effect_value = GetMaxHP(); if (GetSpellPowerDistanceMod()) - effect_value = effect_value*(GetSpellPowerDistanceMod()/100); + effect_value = effect_value*GetSpellPowerDistanceMod()/100; #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; @@ -3127,26 +3127,42 @@ snare has both of them negative, yet their range should work the same: case 110: // confirmed 2/6/04 //is there a reason we dont use updownsign here??? - result = ubase + (caster_level / 5); break; + result = ubase + (caster_level / 6); + break; case 111: - result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 6 * (caster_level - 16)); + break; case 112: - result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 8 * (caster_level - 24)); + break; case 113: - result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 10 * (caster_level - 34)); + break; case 114: - result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 15 * (caster_level - 44)); + break; - //these formula were updated according to lucy 10/16/04 case 115: // this is only in symbol of transal - result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 15) + result += 7 * (caster_level - 15); + break; case 116: // this is only in symbol of ryltan - result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 24) + result += 10 * (caster_level - 24); + break; case 117: // this is only in symbol of pinzarn - result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 34) + result += 13 * (caster_level - 34); + break; case 118: // used in naltron and a few others - result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 44) + result += 20 * (caster_level - 44); + break; case 119: // confirmed 2/6/04 result = ubase + (caster_level / 8); break; @@ -3166,6 +3182,93 @@ snare has both of them negative, yet their range should work the same: result = MakeRandomInt(ubase, abs(max)); break; + case 124: // check sign + result = ubase; + if (caster_level > 50) + result += caster_level - 50; + break; + + case 125: // check sign + result = ubase; + if (caster_level > 50) + result += 2 * (caster_level - 50); + break; + + case 126: // check sign + result = ubase; + if (caster_level > 50) + result += 3 * (caster_level - 50); + break; + + case 127: // check sign + result = ubase; + if (caster_level > 50) + result += 4 * (caster_level - 50); + break; + + case 128: // check sign + result = ubase; + if (caster_level > 50) + result += 5 * (caster_level - 50); + break; + + case 129: // check sign + result = ubase; + if (caster_level > 50) + result += 10 * (caster_level - 50); + break; + + case 130: // check sign + result = ubase; + if (caster_level > 50) + result += 15 * (caster_level - 50); + break; + + case 131: // check sign + result = ubase; + if (caster_level > 50) + result += 20 * (caster_level - 50); + break; + + case 132: // check sign + result = ubase; + if (caster_level > 50) + result += 25 * (caster_level - 50); + break; + + case 137: // used in berserker AA desperation + result = ubase - (ubase * (GetHPRatio() / 100.0f)); + break; + + case 138: { // unused on live? + int maxhps = GetMaxHP() / 2; + if (GetHP() <= maxhps) + result = -(ubase * GetHP() / maxhps); + else + result = -ubase; + break; + } + + case 139: // check sign + result = ubase + (caster_level > 30 ? (caster_level - 30) / 2 : 0); + break; + + case 140: // check sign + result = ubase + (caster_level > 30 ? caster_level - 30 : 0); + break; + + case 141: // check sign + result = ubase + (caster_level > 30 ? (3 * caster_level - 90) / 2 : 0); + break; + + case 142: // check sign + result = ubase + (caster_level > 30 ? 2 * caster_level - 60 : 0); + break; + + case 143: // check sign + result = ubase + (3 * caster_level / 4); + break; + //these are used in stacking effects... formula unknown case 201: case 203: @@ -4016,6 +4119,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if (!found_numhits) Numhits(false); } + + if (spells[buffs[slot].spellid].NimbusEffect > 0) + RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect); buffs[slot].spellid = SPELL_UNKNOWN; if(IsPet() && GetOwner() && GetOwner()->IsClient()) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 1f37a21e4..d400e46ce 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4501,13 +4501,12 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } else { - resist_chance -= roll; if(resist_chance < 1) { resist_chance = 1; } - int partial_modifier = ((150 * (roll - resist_chance)) / resist_chance); + int partial_modifier = ((150 * (resist_chance - roll)) / resist_chance); if(IsNPC()) { @@ -4535,17 +4534,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } } - if(partial_modifier < 0) + if(partial_modifier <= 0) + { + return 100; + } + else if(partial_modifier >= 100) { return 0; } - if(partial_modifier > 100) - { - return 100; - } - - return partial_modifier; + return (100.0f - partial_modifier); } } } diff --git a/zone/titles.cpp b/zone/titles.cpp index 802d33935..eeae38b8a 100644 --- a/zone/titles.cpp +++ b/zone/titles.cpp @@ -31,25 +31,17 @@ bool TitleManager::LoadTitles() { Titles.clear(); - TitleEntry Title; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, `min_aa_points`, `max_aa_points`, `class`, `gender`, " - "`char_id`, `status`, `item_id`, `prefix`, `suffix`, `title_set` from titles"), errbuf, &result)) - { - LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query, errbuf); - safe_delete_array(query); - return(false); + std::string query = "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, " + "`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, " + "`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - - while ((row = mysql_fetch_row(result))) { + for (auto row = results.begin(); row != results.end(); ++row) { + TitleEntry Title; Title.TitleID = atoi(row[0]); Title.SkillID = (SkillUseTypes) atoi(row[1]); Title.MinSkillValue = atoi(row[2]); @@ -66,9 +58,8 @@ bool TitleManager::LoadTitles() Title.TitleSet = atoi(row[13]); Titles.push_back(Title); } - mysql_free_result(result); - return(true); + return true; } EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c) @@ -244,92 +235,70 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue) return false; } -void TitleManager::CreateNewPlayerTitle(Client *c, const char *Title) +void TitleManager::CreateNewPlayerTitle(Client *client, const char *title) { - if(!c || !Title) + if(!client || !title) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; + char *escTitle = new char[strlen(title) * 2 + 1]; - char *EscTitle = new char[strlen(Title) * 2 + 1]; + client->SetAATitle(title); - c->SetAATitle(Title); - - database.DoEscapeString(EscTitle, Title, strlen(Title)); - - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `id` from titles where `prefix` = '%s' and char_id = %i", EscTitle, c->CharacterID()), errbuf, &result)) - { - if(mysql_num_rows(result) > 0) - { - mysql_free_result(result); - safe_delete_array(query); - safe_delete_array(EscTitle); - return; - } - mysql_free_result(result); + database.DoEscapeString(escTitle, title, strlen(title)); + auto query = StringFormat("SELECT `id` FROM titles " + "WHERE `prefix` = '%s' AND char_id = %i", + escTitle, client->CharacterID()); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() > 0){ + safe_delete_array(escTitle); + return; } - safe_delete_array(query); - - if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `prefix`) VALUES(%i, '%s')", - c->CharacterID(), EscTitle), errbuf)) - LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query, errbuf); - else - { - ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - } - safe_delete_array(query); - safe_delete_array(EscTitle); + query = StringFormat("INSERT INTO titles (`char_id`, `prefix`) VALUES(%i, '%s')", + client->CharacterID(), escTitle); + safe_delete_array(escTitle); + results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); } -void TitleManager::CreateNewPlayerSuffix(Client *c, const char *Suffix) +void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix) { - if(!c || !Suffix) + if(!client || !suffix) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; + client->SetTitleSuffix(suffix); - char *EscSuffix = new char[strlen(Suffix) * 2 + 1]; + char *escSuffix = new char[strlen(suffix) * 2 + 1]; + database.DoEscapeString(escSuffix, suffix, strlen(suffix)); - c->SetTitleSuffix(Suffix); - - database.DoEscapeString(EscSuffix, Suffix, strlen(Suffix)); - - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `id` from titles where `suffix` = '%s' and char_id = %i", EscSuffix, c->CharacterID()), errbuf, &result)) - { - if(mysql_num_rows(result) > 0) - { - mysql_free_result(result); - safe_delete_array(query); - safe_delete_array(EscSuffix); + std::string query = StringFormat("SELECT `id` FROM titles " + "WHERE `suffix` = '%s' AND char_id = %i", + escSuffix, client->CharacterID()); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() > 0) { + safe_delete_array(escSuffix); return; - } - mysql_free_result(result); - } + } - safe_delete_array(query); - - if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `suffix`) VALUES(%i, '%s')", - c->CharacterID(), EscSuffix), errbuf)) - LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query, errbuf); - else - { - ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - } - safe_delete_array(query); - safe_delete_array(EscSuffix); + query = StringFormat("INSERT INTO titles (`char_id`, `suffix`) VALUES(%i, '%s')", + client->CharacterID(), escSuffix); + safe_delete_array(escSuffix); + results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); } void Client::SetAATitle(const char *Title) @@ -368,67 +337,48 @@ void Client::SetTitleSuffix(const char *Suffix) safe_delete(outapp); } -void Client::EnableTitle(int titleset) { +void Client::EnableTitle(int titleSet) { - if (CheckTitle(titleset)) { + if (CheckTitle(titleSet)) return; - } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("INSERT INTO player_titlesets " + "(char_id, title_set) VALUES (%i, %i)", + CharacterID(), titleSet); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleSet, CharacterID()); - if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO player_titlesets (char_id, title_set) VALUES (%i, %i)", CharacterID(), titleset), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleset, CharacterID()); - safe_delete_array(query); - return; - } - else { - safe_delete_array(query); - return; - } } -bool Client::CheckTitle(int titleset) { +bool Client::CheckTitle(int titleSet) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT `id` FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", titleset, CharacterID()), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) >= 1) { - mysql_free_result(result); - return(true); - } - mysql_free_result(result); + std::string query = StringFormat("SELECT `id` FROM player_titlesets " + "WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", + titleSet, CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - else { - LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query, errbuf); - safe_delete_array(query); - } + if (results.RowCount() == 0) + return false; - return(false); + return true; } -void Client::RemoveTitle(int titleset) { +void Client::RemoveTitle(int titleSet) { - if (!CheckTitle(titleset)) { + if (!CheckTitle(titleSet)) return; - } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("DELETE FROM player_titlesets " + "WHERE `title_set` = %i AND `char_id` = %i", + titleSet, CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - if (database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i", titleset, CharacterID()), errbuf)) { - safe_delete_array(query); - } - - else { - LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query, errbuf); - safe_delete_array(query); - } - - return; } diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 580b9c51c..a36bcec8b 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -567,7 +567,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b { Map::Vertex dest(x_pos, y_pos, z_pos); - float newz = zone->zonemap->FindBestZ(dest, nullptr); + float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); @@ -696,7 +696,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b { Map::Vertex dest(x_pos, y_pos, z_pos); - float newz = zone->zonemap->FindBestZ(dest, nullptr); + float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); @@ -821,7 +821,7 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec { Map::Vertex dest(x_pos, y_pos, z_pos); - float newz = zone->zonemap->FindBestZ(dest, nullptr); + float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); @@ -1007,26 +1007,19 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) { } int ZoneDatabase::GetHighestGrid(uint32 zoneid) { - char *query = 0; - char errbuff[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - int res = 0; - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", - zoneid),errbuff,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - res = atoi( row[0] ); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query, errbuff); - safe_delete_array(query); - } - return(res); + std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } uint8 ZoneDatabase::GetGridType2(uint32 grid, uint16 zoneid) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index d6b59a8c4..7ebf6f084 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -71,124 +71,121 @@ ZoneDatabase::~ZoneDatabase() { } } -bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "update zone set underworld=%f,minclip=%f," - "maxclip=%f,fog_minclip=%f,fog_maxclip=%f,fog_blue=%i,fog_red=%i,fog_green=%i,sky=%i," - "ztype=%i,zone_exp_multiplier=%f,safe_x=%f,safe_y=%f,safe_z=%f " - "where zoneidnumber=%i and version=%i", - zd->underworld,zd->minclip, - zd->maxclip,zd->fog_minclip[0],zd->fog_maxclip[0],zd->fog_blue[0],zd->fog_red[0],zd->fog_green[0],zd->sky, - zd->ztype,zd->zone_exp_multiplier, - zd->safe_x,zd->safe_y,zd->safe_z, - zoneid, instance_id),errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in SaveZoneCFG query %s: %s", query, errbuf); - safe_delete_array(query); - return false; +bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd) { + + std::string query = StringFormat("UPDATE zone SET underworld = %f, minclip = %f, " + "maxclip = %f, fog_minclip = %f, fog_maxclip = %f, " + "fog_blue = %i, fog_red = %i, fog_green = %i, " + "sky = %i, ztype = %i, zone_exp_multiplier = %f, " + "safe_x = %f, safe_y = %f, safe_z = %f " + "WHERE zoneidnumber = %i AND version = %i", + zd->underworld, zd->minclip, + zd->maxclip, zd->fog_minclip[0], zd->fog_maxclip[0], + zd->fog_blue[0], zd->fog_red[0], zd->fog_green[0], + zd->sky, zd->ztype, zd->zone_exp_multiplier, + zd->safe_x, zd->safe_y, zd->safe_z, + zoneid, instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in SaveZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); + return true; } bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int i=0; - int b=0; - bool good = false; + *map_filename = new char[100]; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT ztype," - "fog_red,fog_green,fog_blue,fog_minclip,fog_maxclip," - "fog_red2,fog_green2,fog_blue2,fog_minclip2,fog_maxclip2," - "fog_red3,fog_green3,fog_blue3,fog_minclip3,fog_maxclip3," - "fog_red4,fog_green4,fog_blue4,fog_minclip4,fog_maxclip4,fog_density," - "sky,zone_exp_multiplier,safe_x,safe_y,safe_z,underworld," - "minclip,maxclip,time_type,canbind,cancombat,canlevitate," - "castoutdoor,hotzone,ruleset,suspendbuffs,map_file_name,short_name," - "rain_chance1,rain_chance2,rain_chance3,rain_chance4," - "rain_duration1,rain_duration2,rain_duration3,rain_duration4," - "snow_chance1,snow_chance2,snow_chance3,snow_chance4," - "snow_duration1,snow_duration2,snow_duration3,snow_duration4" - " from zone where zoneidnumber=%i and version=%i",zoneid, instance_id), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if(row) - { - int r = 0; - memset(zone_data,0,sizeof(NewZone_Struct)); - zone_data->ztype=atoi(row[r++]); - - for(i=0;i<4;i++){ - zone_data->fog_red[i]=atoi(row[r++]); - zone_data->fog_green[i]=atoi(row[r++]); - zone_data->fog_blue[i]=atoi(row[r++]); - zone_data->fog_minclip[i]=atof(row[r++]); - zone_data->fog_maxclip[i]=atof(row[r++]); - } - - zone_data->fog_density = atof(row[r++]);; - zone_data->sky=atoi(row[r++]); - zone_data->zone_exp_multiplier=atof(row[r++]); - zone_data->safe_x=atof(row[r++]); - zone_data->safe_y=atof(row[r++]); - zone_data->safe_z=atof(row[r++]); - zone_data->underworld=atof(row[r++]); - zone_data->minclip=atof(row[r++]); - zone_data->maxclip=atof(row[r++]); - - zone_data->time_type=atoi(row[r++]); -//not in the DB yet: - zone_data->gravity = 0.4; - - b = atoi(row[r++]); - can_bind = b==0?false:true; - is_city = b==2?true:false; - can_combat = atoi(row[r++])==0?false:true; - can_levitate = atoi(row[r++])==0?false:true; - can_castoutdoor = atoi(row[r++])==0?false:true; - is_hotzone = atoi(row[r++])==0?false:true; - allow_mercs = true; - zone_type = zone_data->ztype; - ruleset = atoi(row[r++]); - zone_data->SuspendBuffs = atoi(row[r++]); - char *file = row[r++]; - if(file) - { - strcpy(*map_filename, file); - } - else - { - strcpy(*map_filename, row[r++]); - } - for(i=0;i<4;i++){ - zone_data->rain_chance[i]=atoi(row[r++]); - } - for(i=0;i<4;i++){ - zone_data->rain_duration[i]=atoi(row[r++]); - } - for(i=0;i<4;i++){ - zone_data->snow_chance[i]=atoi(row[r++]); - } - for(i=0;i<4;i++){ - zone_data->snow_duration[i]=atof(row[r++]); - } - good = true; - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in GetZoneCFG query %s: %s", query, errbuf); - strcpy(*map_filename, "default"); - } - safe_delete_array(query); - zone_data->zone_id = zoneid; - return(good); + std::string query = StringFormat("SELECT ztype, fog_red, fog_green, fog_blue, fog_minclip, fog_maxclip, " // 5 + "fog_red2, fog_green2, fog_blue2, fog_minclip2, fog_maxclip2, " // 5 + "fog_red3, fog_green3, fog_blue3, fog_minclip3, fog_maxclip3, " // 5 + "fog_red4, fog_green4, fog_blue4, fog_minclip4, fog_maxclip4, " // 5 + "fog_density, sky, zone_exp_multiplier, safe_x, safe_y, safe_z, underworld, " // 7 + "minclip, maxclip, time_type, canbind, cancombat, canlevitate, " // 6 + "castoutdoor, hotzone, ruleset, suspendbuffs, map_file_name, short_name, " // 6 + "rain_chance1, rain_chance2, rain_chance3, rain_chance4, " // 4 + "rain_duration1, rain_duration2, rain_duration3, rain_duration4, " // 4 + "snow_chance1, snow_chance2, snow_chance3, snow_chance4, " // 4 + "snow_duration1, snow_duration2, snow_duration3, snow_duration4 " // 4 + "FROM zone WHERE zoneidnumber = %i AND version = %i", zoneid, instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + strcpy(*map_filename, "default"); + return false; + } + + if (results.RowCount() == 0) { + strcpy(*map_filename, "default"); + return false; + } + + auto row = results.begin(); + + memset(zone_data, 0, sizeof(NewZone_Struct)); + zone_data->ztype = atoi(row[0]); + zone_type = zone_data->ztype; + + int index; + for(index = 0; index < 4; index++) { + zone_data->fog_red[index]=atoi(row[1 + index * 5]); + zone_data->fog_green[index]=atoi(row[2 + index * 5]); + zone_data->fog_blue[index]=atoi(row[3 + index * 5]); + zone_data->fog_minclip[index]=atof(row[4 + index * 5]); + zone_data->fog_maxclip[index]=atof(row[5 + index * 5]); + } + + zone_data->fog_density = atof(row[21]); + zone_data->sky=atoi(row[22]); + zone_data->zone_exp_multiplier=atof(row[23]); + zone_data->safe_x=atof(row[24]); + zone_data->safe_y=atof(row[25]); + zone_data->safe_z=atof(row[26]); + zone_data->underworld=atof(row[27]); + zone_data->minclip=atof(row[28]); + zone_data->maxclip=atof(row[29]); + zone_data->time_type=atoi(row[30]); + + //not in the DB yet: + zone_data->gravity = 0.4; + allow_mercs = true; + + int bindable = 0; + bindable = atoi(row[31]); + + can_bind = bindable == 0? false: true; + is_city = bindable == 2? true: false; + can_combat = atoi(row[32]) == 0? false: true; + can_levitate = atoi(row[33]) == 0? false: true; + can_castoutdoor = atoi(row[34]) == 0? false: true; + is_hotzone = atoi(row[35]) == 0? false: true; + + + ruleset = atoi(row[36]); + zone_data->SuspendBuffs = atoi(row[37]); + + char *file = row[38]; + if(file) + strcpy(*map_filename, file); + else + strcpy(*map_filename, row[39]); + + for(index = 0; index < 4; index++) + zone_data->rain_chance[index]=atoi(row[40 + index]); + + for(index = 0; index < 4; index++) + zone_data->rain_duration[index]=atoi(row[44 + index]); + + for(index = 0; index < 4; index++) + zone_data->snow_chance[index]=atoi(row[48 + index]); + + for(index = 0; index < 4; index++) + zone_data->snow_duration[index]=atof(row[52 + index]); + + return true; } //updates or clears the respawn time in the database for the current spawn id @@ -198,99 +195,73 @@ void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 ti gettimeofday(&tv, nullptr); uint32 cur = tv.tv_sec; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //if we pass timeleft as 0 that means we clear from respawn time //otherwise we update with a REPLACE INTO - if(timeleft == 0) - { - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE id=%lu " - "AND instance_id=%lu",(unsigned long)id, (unsigned long)instance_id),errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query, errbuf); - } - safe_delete_array(query); - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO respawn_times (id,start,duration,instance_id) " - "VALUES(%lu,%lu,%lu,%lu)",(unsigned long)id, (unsigned long)cur, (unsigned long)timeleft, (unsigned long)instance_id),errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query, errbuf); - } - safe_delete_array(query); + if(timeleft == 0) { + std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu " + "AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + + return; } + + std::string query = StringFormat("REPLACE INTO respawn_times (id, start, duration, instance_id) " + "VALUES (%lu, %lu, %lu, %lu)", + (unsigned long)id, (unsigned long)cur, + (unsigned long)timeleft, (unsigned long)instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return; } //Gets the respawn time left in the database for the current spawn id uint32 ZoneDatabase::GetSpawnTimeLeft(uint32 id, uint16 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - MakeAnyLenString(&query, "SELECT start, duration FROM respawn_times WHERE id=%lu AND instance_id=%lu", - (unsigned long)id, (unsigned long)zone->GetInstanceID()); - - if (RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - row = mysql_fetch_row(result); - if(row) - { - timeval tv; - gettimeofday(&tv, nullptr); - uint32 resStart = atoi(row[0]); - uint32 resDuration = atoi(row[1]); - - //compare our values to current time - if((resStart + resDuration) <= tv.tv_sec) - { - //our current time was expired - mysql_free_result(result); - return 0; - } - else - { - //we still have time left on this timer - mysql_free_result(result); - return ((resStart + resDuration) - tv.tv_sec); - } - } - else - { - mysql_free_result(result); - return 0; - } - } - else - { - LogFile->write(EQEMuLog::Error, "Error in GetSpawnTimeLeft query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT start, duration FROM respawn_times " + "WHERE id = %lu AND instance_id = %lu", + (unsigned long)id, (unsigned long)zone->GetInstanceID()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetSpawnTimeLeft query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return 0; - } - return 0; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + timeval tv; + gettimeofday(&tv, nullptr); + uint32 resStart = atoi(row[0]); + uint32 resDuration = atoi(row[1]); + + //compare our values to current time + if((resStart + resDuration) <= tv.tv_sec) { + //our current time was expired + return 0; + } + + //we still have time left on this timer + return ((resStart + resDuration) - tv.tv_sec); + } void ZoneDatabase::UpdateSpawn2Status(uint32 id, uint8 new_status) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("UPDATE spawn2 SET enabled = %i WHERE id = %lu", new_status, (unsigned long)id); + auto results = QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateSpawn2Status query %s: %s", query.c_str(), results.ErrorMessage().c_str()); - if(!RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET enabled=%i WHERE id=%lu", new_status, (unsigned long)id),errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in UpdateSpawn2Status query %s: %s", query, errbuf); - } - safe_delete_array(query); - return; } bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname, const char* target,const char* descriptiontype, const char* description,int event_nid){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 len = strlen(description); uint32 len2 = strlen(target); char* descriptiontext = new char[2*len+1]; @@ -299,22 +270,25 @@ bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 stat memset(targetarr, 0, 2*len2+1); DoEscapeString(descriptiontext, description, len); DoEscapeString(targetarr, target, len2); - if (!RunQuery(query, MakeAnyLenString(&query, "Insert into eventlog (accountname,accountid,status,charname,target,descriptiontype,description,event_nid) values('%s',%i,%i,'%s','%s','%s','%s','%i')", accountname,accountid,status,charname,targetarr,descriptiontype,descriptiontext,event_nid), errbuf)) { - std::cerr << "Error in logevents" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + std::string query = StringFormat("INSERT INTO eventlog (accountname, accountid, status, " + "charname, target, descriptiontype, description, event_nid) " + "VALUES('%s', %i, %i, '%s', '%s', '%s', '%s', '%i')", + accountname, accountid, status, charname, targetarr, + descriptiontype, descriptiontext, event_nid); + safe_delete_array(descriptiontext); + safe_delete_array(targetarr); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in logevents" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); - safe_delete_array(descriptiontext); - safe_delete_array(targetarr); + return true; } -void ZoneDatabase::UpdateBug(BugStruct* bug){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - +void ZoneDatabase::UpdateBug(BugStruct* bug) { uint32 len = strlen(bug->bug); char* bugtext = nullptr; @@ -344,48 +318,45 @@ void ZoneDatabase::UpdateBug(BugStruct* bug){ } //x and y are intentionally swapped because eq is inversexy coords - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) " - "values('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())", zone->GetShortName(), bug->name, - uitext==nullptr?"":uitext, bug->y, bug->x, bug->z, bug->chartype, bug->type, targettext==nullptr?"Unknown Target":targettext, - bugtext==nullptr?"":bugtext), errbuf)) { - std::cerr << "Error in UpdateBug" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); - safe_delete_array(bugtext); + std::string query = StringFormat("INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) " + "VALUES('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())", + zone->GetShortName(), bug->name, uitext == nullptr ? "": uitext, + bug->x, bug->y, bug->z, bug->chartype, bug->type, targettext == nullptr? "Unknown Target": targettext, + bugtext==nullptr?"":bugtext); + safe_delete_array(bugtext); safe_delete_array(uitext); safe_delete_array(targettext); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl; + } void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 len = strlen(bug->text); char* bugtext = new char[2*len+1]; memset(bugtext, 0, 2*len+1); DoEscapeString(bugtext, bug->text, len); - if (!RunQuery(query, MakeAnyLenString(&query, "Insert into bugs (type,name,bugtext,flag) values('%s','%s','%s',%i)","Petition",bug->name,bugtext,25), errbuf)) { - std::cerr << "Error in UpdateBug" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); - safe_delete_array(bugtext); + + std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) " + "VALUES('%s', '%s', '%s', %i)", + "Petition", bug->name, bugtext, 25); + safe_delete_array(bugtext); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl; + } bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET npcspecialattks='%s' WHERE id=%i;",flag,id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); + std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id); + auto results = QueryDatabase(query); + if (!results.Success()) return false; - } - safe_delete_array(query); - if (affected_rows == 0) { - return false; - } - - return true; + return results.RowsAffected() != 0; } bool ZoneDatabase::DoorIsOpen(uint8 door_id,const char* zone_name) @@ -407,12 +378,6 @@ void ZoneDatabase::SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name) void ZoneDatabase::GetEventLogs(const char* name,char* target,uint32 account_id,uint8 eventid,char* detail,char* timestamp, CharacterEventLog_Struct* cel) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - uint32 count = 0; char modifications[200]; if(strlen(name) != 0) sprintf(modifications,"charname=\'%s\'",name); @@ -420,46 +385,42 @@ void ZoneDatabase::GetEventLogs(const char* name,char* target,uint32 account_id, sprintf(modifications,"accountid=%i",account_id); if(strlen(target) != 0) - sprintf(modifications,"%s AND target like \'%%%s%%\'",modifications,target); + sprintf(modifications,"%s AND target LIKE \'%%%s%%\'",modifications,target); if(strlen(detail) != 0) - sprintf(modifications,"%s AND description like \'%%%s%%\'",modifications,detail); + sprintf(modifications,"%s AND description LIKE \'%%%s%%\'",modifications,detail); if(strlen(timestamp) != 0) - sprintf(modifications,"%s AND time like \'%%%s%%\'",modifications,timestamp); + sprintf(modifications,"%s AND time LIKE \'%%%s%%\'",modifications,timestamp); if(eventid == 0) eventid =1; sprintf(modifications,"%s AND event_nid=%i",modifications,eventid); - MakeAnyLenString(&query, "SELECT id,accountname,accountid,status,charname,target,time,descriptiontype,description FROM eventlog where %s",modifications); - if (RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - if(count > 255) - break; - cel->eld[count].id = atoi(row[0]); - strn0cpy(cel->eld[count].accountname,row[1],64); - cel->eld[count].account_id = atoi(row[2]); - cel->eld[count].status = atoi(row[3]); - strn0cpy(cel->eld[count].charactername,row[4],64); - strn0cpy(cel->eld[count].targetname,row[5],64); - sprintf(cel->eld[count].timestamp,"%s",row[6]); - strn0cpy(cel->eld[count].descriptiontype,row[7],64); - strn0cpy(cel->eld[count].details,row[8],128); - cel->eventid = eventid; - count++; - cel->count = count; - } - mysql_free_result(result); - } - else - { - // TODO: Invalid item length in database - safe_delete_array(query); - } + std::string query = StringFormat("SELECT id, accountname, accountid, status, charname, target, " + "time, descriptiontype, description FROM eventlog WHERE %s", modifications); + auto results = QueryDatabase(query); + if (!results.Success()) + return; + + int index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + if(index == 255) + break; + + cel->eld[index].id = atoi(row[0]); + strn0cpy(cel->eld[index].accountname,row[1],64); + cel->eld[index].account_id = atoi(row[2]); + cel->eld[index].status = atoi(row[3]); + strn0cpy(cel->eld[index].charactername,row[4],64); + strn0cpy(cel->eld[index].targetname,row[5],64); + sprintf(cel->eld[index].timestamp,"%s",row[6]); + strn0cpy(cel->eld[index].descriptiontype,row[7],64); + strn0cpy(cel->eld[index].details,row[8],128); + cel->eventid = eventid; + cel->count = index + 1; + } + } // Load child objects for a world container (i.e., forge, bag dropped to ground, etc) @@ -470,232 +431,207 @@ void ZoneDatabase::LoadWorldContainer(uint32 parentid, ItemInst* container) return; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - //const Item_Struct* item = nullptr; - //ItemInst* inst = nullptr; + std::string query = StringFormat("SELECT bagidx, itemid, charges, augslot1, augslot2, augslot3, augslot4, augslot5 " + "FROM object_contents WHERE parentid = %i", parentid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in DB::LoadWorldContainer: %s", results.ErrorMessage().c_str()); + return; + } - uint32 len_query = MakeAnyLenString(&query, "select " - "bagidx,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5 from object_contents where parentid=%i", parentid); + for (auto row = results.begin(); row != results.end(); ++row) { + uint8 index = (uint8)atoi(row[0]); + uint32 item_id = (uint32)atoi(row[1]); + int8 charges = (int8)atoi(row[2]); + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoi(row[3]); + aug[1] = (uint32)atoi(row[4]); + aug[2] = (uint32)atoi(row[5]); + aug[3] = (uint32)atoi(row[6]); + aug[4] = (uint32)atoi(row[7]); - if (RunQuery(query, len_query, errbuf, &result)) { - while ((row = mysql_fetch_row(result))) { - uint8 index = (uint8)atoi(row[0]); - uint32 item_id = (uint32)atoi(row[1]); - int8 charges = (int8)atoi(row[2]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoi(row[3]); - aug[1] = (uint32)atoi(row[4]); - aug[2] = (uint32)atoi(row[5]); - aug[3] = (uint32)atoi(row[6]); - aug[4] = (uint32)atoi(row[7]); + ItemInst* inst = database.CreateItem(item_id, charges); + if (inst && inst->GetItem()->ItemClass == ItemClassCommon) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) + if (aug[i]) + inst->PutAugment(&database, i, aug[i]); + // Put item inside world container + container->PutItem(index, *inst); + safe_delete(inst); + } + } - ItemInst* inst = database.CreateItem(item_id, charges); - if (inst) { - if (inst->GetItem()->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(&database, i, aug[i]); - } - } - } - // Put item inside world container - container->PutItem(index, *inst); - safe_delete(inst); - } - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in DB::LoadWorldContainer: %s", errbuf); - } - - safe_delete_array(query); } // Save child objects for a world container (i.e., forge, bag dropped to ground, etc) void ZoneDatabase::SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - // Since state is not saved for each world container action, we'll just delete // all and save from scratch .. we may come back later to optimize - //DeleteWorldContainer(parent_id); - - if (!container) { + if (!container) return; - } + //Delete all items from container DeleteWorldContainer(parent_id,zone_id); + // Save all 10 items, if they exist for (uint8 index = SUB_BEGIN; index < EmuConstants::ITEM_CONTAINER_SIZE; index++) { + ItemInst* inst = container->GetItem(index); - if (inst) { - uint32 item_id = inst->GetItem()->ID; - uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (inst->IsType(ItemClassCommon)) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - ItemInst *auginst=inst->GetAugment(i); - augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } - } - uint32 len_query = MakeAnyLenString(&query, + if (!inst) + continue; - "replace into object_contents (zoneid,parentid,bagidx,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5,droptime) values (%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,now())", - zone_id, parent_id, index, item_id, inst->GetCharges(),augslot[0],augslot[1],augslot[2],augslot[3],augslot[4]); + uint32 item_id = inst->GetItem()->ID; + uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (!RunQuery(query, len_query, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", errbuf); - } - safe_delete_array(query); - } + if (inst->IsType(ItemClassCommon)) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + ItemInst *auginst=inst->GetAugment(i); + augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; + } + } + + std::string query = StringFormat("REPLACE INTO object_contents " + "(zoneid, parentid, bagidx, itemid, charges, " + "augslot1, augslot2, augslot3, augslot4, augslot5, droptime) " + "VALUES (%i, %i, %i, %i, %i, %i, %i, %i, %i, %i, now())", + zone_id, parent_id, index, item_id, inst->GetCharges(), + augslot[0], augslot[1], augslot[2], augslot[3], augslot[4]); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", results.ErrorMessage().c_str()); + + } - } } // Remove all child objects inside a world container (i.e., forge, bag dropped to ground, etc) -void ZoneDatabase::DeleteWorldContainer(uint32 parent_id,uint32 zone_id) +void ZoneDatabase::DeleteWorldContainer(uint32 parent_id, uint32 zone_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + std::string query = StringFormat("DELETE FROM object_contents WHERE parentid = %i AND zoneid = %i", parent_id, zone_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::DeleteWorldContainer: %s", results.ErrorMessage().c_str()); - uint32 len_query = MakeAnyLenString(&query, - "delete from object_contents where parentid=%i and zoneid=%i", parent_id,zone_id); - if (!RunQuery(query, len_query, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::DeleteWorldContainer: %s", errbuf); - } - - safe_delete_array(query); } -Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; +Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id) +{ Trader_Struct* loadti = new Trader_Struct; memset(loadti,0,sizeof(Trader_Struct)); - if (RunQuery(query,MakeAnyLenString(&query, "select * from trader where char_id=%i order by slot_id limit 80",char_id),errbuf,&result)){ - safe_delete_array(query); - loadti->Code = BazaarTrader_ShowItems; - while ((row = mysql_fetch_row(result))) { - if(atoi(row[5])>=80 || atoi(row[4])<0) - _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); - else{ - loadti->Items[atoi(row[5])] = atoi(row[1]); - loadti->ItemCost[atoi(row[5])] = atoi(row[4]); - } - } - mysql_free_result(result); - } - else{ - safe_delete_array(query); + + std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i ORDER BY slot_id LIMIT 80", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { _log(TRADING__CLIENT, "Failed to load trader information!\n"); + return loadti; + } + + loadti->Code = BazaarTrader_ShowItems; + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[5]) >= 80 || atoi(row[4]) < 0) { + _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); + continue; + } + + loadti->Items[atoi(row[5])] = atoi(row[1]); + loadti->ItemCost[atoi(row[5])] = atoi(row[4]); } return loadti; } -TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; +TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id) +{ TraderCharges_Struct* loadti = new TraderCharges_Struct; memset(loadti,0,sizeof(TraderCharges_Struct)); - if (RunQuery(query,MakeAnyLenString(&query, "select * from trader where char_id=%i order by slot_id limit 80",char_id),errbuf,&result)){ - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - if(atoi(row[5])>=80 || atoi(row[5])<0) - _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); - else{ - loadti->ItemID[atoi(row[5])] = atoi(row[1]); - loadti->SerialNumber[atoi(row[5])] = atoi(row[2]); - loadti->Charges[atoi(row[5])] = atoi(row[3]); - loadti->ItemCost[atoi(row[5])] = atoi(row[4]); - } - } - mysql_free_result(result); - } - else{ - safe_delete_array(query); + + std::string query = StringFormat("SELECT * FROM trader WHERE char_id=%i ORDER BY slot_id LIMIT 80", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { _log(TRADING__CLIENT, "Failed to load trader information!\n"); + return loadti; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[5]) >= 80 || atoi(row[5]) < 0) { + _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); + continue; + } + + loadti->ItemID[atoi(row[5])] = atoi(row[1]); + loadti->SerialNumber[atoi(row[5])] = atoi(row[2]); + loadti->Charges[atoi(row[5])] = atoi(row[3]); + loadti->ItemCost[atoi(row[5])] = atoi(row[4]); } return loadti; } ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i AND serialnumber = %i " + "ORDER BY slot_id LIMIT 80", CharID, SerialNumber); + auto results = QueryDatabase(query); + if (!results.Success()) + return nullptr; - if (RunQuery(query,MakeAnyLenString(&query, "select * from trader where char_id=%i and serialnumber=%i order by slot_id limit 80", - CharID, SerialNumber),errbuf,&result)){ - safe_delete_array(query); + if (results.RowCount() == 0) { + _log(TRADING__CLIENT, "Bad result from query\n"); fflush(stdout); + return nullptr; + } - if (mysql_num_rows(result) != 1) { - _log(TRADING__CLIENT, "Bad result from query\n"); fflush(stdout); - return nullptr; - } - row = mysql_fetch_row(result); - int ItemID = atoi(row[1]); - int Charges = atoi(row[3]); - int Cost = atoi(row[4]); + auto row = results.begin(); - const Item_Struct *item=database.GetItem(ItemID); + int ItemID = atoi(row[1]); + int Charges = atoi(row[3]); + int Cost = atoi(row[4]); - if(!item) { - _log(TRADING__CLIENT, "Unable to create item\n"); fflush(stdout); - return nullptr; - } + const Item_Struct *item = database.GetItem(ItemID); - if (item && (item->NoDrop!=0)) { - ItemInst* inst = database.CreateItem(item); - if(!inst) { - _log(TRADING__CLIENT, "Unable to create item instance\n"); fflush(stdout); - return nullptr; - } - - inst->SetCharges(Charges); - inst->SetSerialNumber(SerialNumber); - inst->SetMerchantSlot(SerialNumber); - inst->SetPrice(Cost); - if(inst->IsStackable()) - inst->SetMerchantCount(Charges); - - return inst; - } + if(!item) { + _log(TRADING__CLIENT, "Unable to create item\n"); + fflush(stdout); + return nullptr; } - return nullptr; + if (item->NoDrop == 0) + return nullptr; + ItemInst* inst = database.CreateItem(item); + if(!inst) { + _log(TRADING__CLIENT, "Unable to create item instance\n"); + fflush(stdout); + return nullptr; + } + inst->SetCharges(Charges); + inst->SetSerialNumber(SerialNumber); + inst->SetMerchantSlot(SerialNumber); + inst->SetPrice(Cost); + + if(inst->IsStackable()) + inst->SetMerchantCount(Charges); + + return inst; } void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNumber, int32 Charges, uint32 ItemCost, uint8 Slot){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "replace INTO trader VALUES(%i,%i,%i,%i,%i,%i)", - CharID, ItemID, SerialNumber, Charges, ItemCost, Slot),errbuf))) - _log(TRADING__CLIENT, "Failed to save trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, errbuf); + std::string query = StringFormat("REPLACE INTO trader VALUES(%i, %i, %i, %i, %i, %i)", + CharID, ItemID, SerialNumber, Charges, ItemCost, Slot); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to save trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - safe_delete_array(query); } void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", CharID, SerialNumber, Charges); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "update trader set charges=%i where char_id=%i and serialnumber=%i", - Charges, CharID, SerialNumber),errbuf))) - _log(TRADING__CLIENT, "Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n", - SerialNumber, CharID, errbuf); - safe_delete_array(query); + std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i", + Charges, CharID, SerialNumber); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n", + SerialNumber, CharID, results.ErrorMessage().c_str()); } @@ -708,98 +644,94 @@ void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charg if(!item) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* Query = 0; - if(NewPrice == 0) { _log(TRADING__CLIENT, "Removing Trader items from the DB for CharID %i, ItemID %i", CharID, ItemID); - if (!(RunQuery(Query,MakeAnyLenString(&Query, "delete from trader where char_id=%i and item_id=%i", - CharID, ItemID),errbuf))) - - _log(TRADING__CLIENT, "Failed to remove trader item(s): %i for char_id: %i, the error was: %s\n", - ItemID, CharID, errbuf); - - safe_delete_array(Query); + std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i AND item_id = %i",CharID, ItemID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to remove trader item(s): %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); return; } - else { - if(!item->Stackable) { - if (!(RunQuery(Query,MakeAnyLenString(&Query, "update trader set item_cost=%i where char_id=%i and item_id=%i" - " and charges=%i", NewPrice, CharID, ItemID, Charges),errbuf))) - _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", - ItemID, CharID, errbuf); - } - else { - if (!(RunQuery(Query,MakeAnyLenString(&Query, "update trader set item_cost=%i where char_id=%i and item_id=%i", - NewPrice, CharID, ItemID),errbuf))) + if(!item->Stackable) { + std::string query = StringFormat("UPDATE trader SET item_cost = %i " + "WHERE char_id = %i AND item_id = %i AND charges=%i", + NewPrice, CharID, ItemID, Charges); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", - ItemID, CharID, errbuf); - } - - safe_delete_array(Query); - } + return; + } + std::string query = StringFormat("UPDATE trader SET item_cost = %i " + "WHERE char_id = %i AND item_id = %i", + NewPrice, CharID, ItemID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); } void ZoneDatabase::DeleteTraderItem(uint32 char_id){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if(char_id==0){ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader"),errbuf))) - _log(TRADING__CLIENT, "Failed to delete all trader items data, the error was: %s\n",errbuf); + + if(char_id==0) { + const std::string query = "DELETE FROM trader"; + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete all trader items data, the error was: %s\n", results.ErrorMessage().c_str()); + + return; } - else{ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader where char_id=%i",char_id),errbuf))) - _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",char_id,errbuf); - } - safe_delete_array(query); + + std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n", char_id, results.ErrorMessage().c_str()); + } -void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader where char_id=%i and slot_id=%i",CharID, SlotID),errbuf))) - _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",CharID, errbuf); - safe_delete_array(query); +void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID) { + + std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i And slot_id = %i", CharID, SlotID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",CharID, results.ErrorMessage().c_str()); } -void ZoneDatabase::DeleteBuyLines(uint32 CharID){ +void ZoneDatabase::DeleteBuyLines(uint32 CharID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if(CharID==0){ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from buyer"),errbuf))) - _log(TRADING__CLIENT, "Failed to delete all buyer items data, the error was: %s\n",errbuf); + if(CharID==0) { + const std::string query = "DELETE FROM buyer"; + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete all buyer items data, the error was: %s\n",results.ErrorMessage().c_str()); + + return; } - else{ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from buyer where charid=%i",CharID),errbuf))) - _log(TRADING__CLIENT, "Failed to delete buyer item data for charid: %i, the error was: %s\n",CharID,errbuf); - } - safe_delete_array(query); + + std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i", CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete buyer item data for charid: %i, the error was: %s\n",CharID,results.ErrorMessage().c_str()); + } void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "replace INTO buyer VALUES(%i,%i, %i,\"%s\",%i,%i)", - CharID, BuySlot, ItemID, ItemName, Quantity, Price),errbuf))) - _log(TRADING__CLIENT, "Failed to save buline item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, errbuf); + std::string query = StringFormat("REPLACE INTO buyer VALUES(%i, %i, %i, \"%s\", %i, %i)", + CharID, BuySlot, ItemID, ItemName, Quantity, Price); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to save buline item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - safe_delete_array(query); } void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i AND buyslot = %i", CharID, BuySlot); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from buyer where charid=%i and buyslot=%i", CharID, BuySlot), errbuf))) - _log(TRADING__CLIENT, "Failed to delete buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, errbuf); - - safe_delete_array(query); } void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { @@ -808,14 +740,10 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) return; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - if (!(RunQuery(query,MakeAnyLenString(&query, "update buyer set quantity=%i where charid=%i and buyslot=%i", - Quantity, CharID, BuySlot), errbuf))) - _log(TRADING__CLIENT, "Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, errbuf); - - safe_delete_array(query); + std::string query = StringFormat("UPDATE buyer SET quantity = %i WHERE charid = %i AND buyslot = %i", Quantity, CharID, BuySlot); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); } @@ -1730,20 +1658,19 @@ bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spe } bool ZoneDatabase::NoRentExpired(const char* name){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "Select (UNIX_TIMESTAMP(NOW()) - last_login) from `character_data` where name='%s'", name), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 seconds = atoi(row[0]); - mysql_free_result(result); - return (seconds>1800); - } - } - return false; + std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW())-timelaston) " + "FROM character_ WHERE name = '%s'", name); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + uint32 seconds = atoi(row[0]); + + return (seconds>1800); } /* Searches npctable for matching id, and returns the item if found, @@ -1752,1914 +1679,1378 @@ bool ZoneDatabase::NoRentExpired(const char* name){ */ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { const NPCType *npc=nullptr; - std::map::iterator itr; // If NPC is already in tree, return it. - if((itr = zone->npctable.find(id)) != zone->npctable.end()) + auto itr = zone->npctable.find(id); + if(itr != zone->npctable.end()) return itr->second; - // Otherwise, get NPCs from database. - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + // Otherwise, get NPCs from database. - // If id is 0, load all npc_types for the current zone, - // according to spawn2. - const char *basic_query = "SELECT " - "npc_types.id," - "npc_types.name," - "npc_types.level," - "npc_types.race," - "npc_types.class," - "npc_types.hp," - "npc_types.mana," - "npc_types.gender," - "npc_types.texture," - "npc_types.helmtexture," - "npc_types.size," - "npc_types.loottable_id," - "npc_types.merchant_id," - "npc_types.alt_currency_id," - "npc_types.adventure_template_id," - "npc_types.trap_template," - "npc_types.attack_speed," - "npc_types.STR," - "npc_types.STA," - "npc_types.DEX," - "npc_types.AGI," - "npc_types._INT," - "npc_types.WIS," - "npc_types.CHA," - "npc_types.MR," - "npc_types.CR," - "npc_types.DR," - "npc_types.FR," - "npc_types.PR," - "npc_types.Corrup," - "npc_types.PhR," - "npc_types.mindmg," - "npc_types.maxdmg," - "npc_types.attack_count," - "npc_types.special_abilities," - "npc_types.npc_spells_id," - "npc_types.npc_spells_effects_id," - "npc_types.d_meele_texture1," - "npc_types.d_meele_texture2," - "npc_types.ammo_idfile," - "npc_types.prim_melee_type," - "npc_types.sec_melee_type," - "npc_types.ranged_type," - "npc_types.runspeed," - "npc_types.findable," - "npc_types.trackable," - "npc_types.hp_regen_rate," - "npc_types.mana_regen_rate," - "npc_types.aggroradius," - "npc_types.assistradius," - "npc_types.bodytype," - "npc_types.npc_faction_id," - "npc_types.face," - "npc_types.luclin_hairstyle," - "npc_types.luclin_haircolor," - "npc_types.luclin_eyecolor," - "npc_types.luclin_eyecolor2," - "npc_types.luclin_beardcolor," - "npc_types.luclin_beard," - "npc_types.drakkin_heritage," - "npc_types.drakkin_tattoo," - "npc_types.drakkin_details," - "npc_types.armortint_id," - "npc_types.armortint_red," - "npc_types.armortint_green," - "npc_types.armortint_blue," - "npc_types.see_invis," - "npc_types.see_invis_undead," - "npc_types.lastname," - "npc_types.qglobal," - "npc_types.AC," - "npc_types.npc_aggro," - "npc_types.spawn_limit," - "npc_types.see_hide," - "npc_types.see_improved_hide," - "npc_types.ATK," - "npc_types.Accuracy," - "npc_types.Avoidance," - "npc_types.slow_mitigation," - "npc_types.maxlevel," - "npc_types.scalerate," - "npc_types.private_corpse," - "npc_types.unique_spawn_by_name," - "npc_types.underwater," - "npc_types.emoteid," - "npc_types.spellscale," - "npc_types.healscale," - "npc_types.no_target_hotkey," - "npc_types.raid_target"; + // If id is 0, load all npc_types for the current zone, + // according to spawn2. + std::string query = StringFormat("SELECT npc_types.id, npc_types.name, npc_types.level, npc_types.race, " + "npc_types.class, npc_types.hp, npc_types.mana, npc_types.gender, " + "npc_types.texture, npc_types.helmtexture, npc_types.size, " + "npc_types.loottable_id, npc_types.merchant_id, npc_types.alt_currency_id, " + "npc_types.adventure_template_id, npc_types.trap_template, npc_types.attack_speed, " + "npc_types.STR, npc_types.STA, npc_types.DEX, npc_types.AGI, npc_types._INT, " + "npc_types.WIS, npc_types.CHA, npc_types.MR, npc_types.CR, npc_types.DR, " + "npc_types.FR, npc_types.PR, npc_types.Corrup, npc_types.PhR," + "npc_types.mindmg, npc_types.maxdmg, npc_types.attack_count, npc_types.special_abilities," + "npc_types.npc_spells_id, npc_types.npc_spells_effects_id, npc_types.d_meele_texture1," + "npc_types.d_meele_texture2, npc_types.ammo_idfile, npc_types.prim_melee_type," + "npc_types.sec_melee_type, npc_types.ranged_type, npc_types.runspeed, npc_types.findable," + "npc_types.trackable, npc_types.hp_regen_rate, npc_types.mana_regen_rate, " + "npc_types.aggroradius, npc_types.assistradius, npc_types.bodytype, npc_types.npc_faction_id, " + "npc_types.face, npc_types.luclin_hairstyle, npc_types.luclin_haircolor, " + "npc_types.luclin_eyecolor, npc_types.luclin_eyecolor2, npc_types.luclin_beardcolor," + "npc_types.luclin_beard, npc_types.drakkin_heritage, npc_types.drakkin_tattoo, " + "npc_types.drakkin_details, npc_types.armortint_id, " + "npc_types.armortint_red, npc_types.armortint_green, npc_types.armortint_blue, " + "npc_types.see_invis, npc_types.see_invis_undead, npc_types.lastname, " + "npc_types.qglobal, npc_types.AC, npc_types.npc_aggro, npc_types.spawn_limit, " + "npc_types.see_hide, npc_types.see_improved_hide, npc_types.ATK, npc_types.Accuracy, " + "npc_types.Avoidance, npc_types.slow_mitigation, npc_types.maxlevel, npc_types.scalerate, " + "npc_types.private_corpse, npc_types.unique_spawn_by_name, npc_types.underwater, " + "npc_types.emoteid, npc_types.spellscale, npc_types.healscale, npc_types.no_target_hotkey," + "npc_types.raid_target, npc_types.attack_delay FROM npc_types WHERE id = %d", id); - MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl; + return nullptr; + } - if (RunQuery(query, strlen(query), errbuf, &result)) { - // Process each row returned. - while((row = mysql_fetch_row(result))) { - NPCType *tmpNPCType; - tmpNPCType = new NPCType; - memset (tmpNPCType, 0, sizeof *tmpNPCType); - int r = 0; - tmpNPCType->npc_id = atoi(row[r++]); + for (auto row = results.begin(); row != results.end(); ++row) { + NPCType *tmpNPCType; + tmpNPCType = new NPCType; + memset (tmpNPCType, 0, sizeof *tmpNPCType); - strn0cpy(tmpNPCType->name, row[r++], 50); + tmpNPCType->npc_id = atoi(row[0]); - tmpNPCType->level = atoi(row[r++]); - tmpNPCType->race = atoi(row[r++]); - tmpNPCType->class_ = atoi(row[r++]); - tmpNPCType->max_hp = atoi(row[r++]); - tmpNPCType->cur_hp = tmpNPCType->max_hp; - tmpNPCType->Mana = atoi(row[r++]); - tmpNPCType->gender = atoi(row[r++]); - tmpNPCType->texture = atoi(row[r++]); - tmpNPCType->helmtexture = atoi(row[r++]); - tmpNPCType->size = atof(row[r++]); - tmpNPCType->loottable_id = atoi(row[r++]); - tmpNPCType->merchanttype = atoi(row[r++]); - tmpNPCType->alt_currency_type = atoi(row[r++]); - tmpNPCType->adventure_template = atoi(row[r++]); - tmpNPCType->trap_template = atoi(row[r++]); - tmpNPCType->attack_speed = atof(row[r++]); - tmpNPCType->STR = atoi(row[r++]); - tmpNPCType->STA = atoi(row[r++]); - tmpNPCType->DEX = atoi(row[r++]); - tmpNPCType->AGI = atoi(row[r++]); - tmpNPCType->INT = atoi(row[r++]); - tmpNPCType->WIS = atoi(row[r++]); - tmpNPCType->CHA = atoi(row[r++]); - tmpNPCType->MR = atoi(row[r++]); - tmpNPCType->CR = atoi(row[r++]); - tmpNPCType->DR = atoi(row[r++]); - tmpNPCType->FR = atoi(row[r++]); - tmpNPCType->PR = atoi(row[r++]); - tmpNPCType->Corrup = atoi(row[r++]); - tmpNPCType->PhR = atoi(row[r++]); - tmpNPCType->min_dmg = atoi(row[r++]); - tmpNPCType->max_dmg = atoi(row[r++]); - tmpNPCType->attack_count = atoi(row[r++]); - strn0cpy(tmpNPCType->special_abilities, row[r++], 512); - tmpNPCType->npc_spells_id = atoi(row[r++]); - tmpNPCType->npc_spells_effects_id = atoi(row[r++]); - tmpNPCType->d_meele_texture1 = atoi(row[r++]); - tmpNPCType->d_meele_texture2 = atoi(row[r++]); - strn0cpy(tmpNPCType->ammo_idfile, row[r++], 30); - tmpNPCType->prim_melee_type = atoi(row[r++]); - tmpNPCType->sec_melee_type = atoi(row[r++]); - tmpNPCType->ranged_type = atoi(row[r++]); - tmpNPCType->runspeed= atof(row[r++]); - tmpNPCType->findable = atoi(row[r++]) == 0? false : true; - tmpNPCType->trackable = atoi(row[r++]) == 0? false : true; - tmpNPCType->hp_regen = atoi(row[r++]); - tmpNPCType->mana_regen = atoi(row[r++]); + strn0cpy(tmpNPCType->name, row[1], 50); - tmpNPCType->aggroradius = (int32)atoi(row[r++]); - // set defaultvalue for aggroradius - if (tmpNPCType->aggroradius <= 0) - tmpNPCType->aggroradius = 70; - tmpNPCType->assistradius = (int32)atoi(row[r++]); - if (tmpNPCType->assistradius <= 0) - tmpNPCType->assistradius = tmpNPCType->aggroradius; + tmpNPCType->level = atoi(row[2]); + tmpNPCType->race = atoi(row[3]); + tmpNPCType->class_ = atoi(row[4]); + tmpNPCType->max_hp = atoi(row[5]); + tmpNPCType->cur_hp = tmpNPCType->max_hp; + tmpNPCType->Mana = atoi(row[6]); + tmpNPCType->gender = atoi(row[7]); + tmpNPCType->texture = atoi(row[8]); + tmpNPCType->helmtexture = atoi(row[9]); + tmpNPCType->size = atof(row[10]); + tmpNPCType->loottable_id = atoi(row[11]); + tmpNPCType->merchanttype = atoi(row[12]); + tmpNPCType->alt_currency_type = atoi(row[13]); + tmpNPCType->adventure_template = atoi(row[14]); + tmpNPCType->trap_template = atoi(row[15]); + tmpNPCType->attack_speed = atof(row[16]); + tmpNPCType->STR = atoi(row[17]); + tmpNPCType->STA = atoi(row[18]); + tmpNPCType->DEX = atoi(row[19]); + tmpNPCType->AGI = atoi(row[20]); + tmpNPCType->INT = atoi(row[21]); + tmpNPCType->WIS = atoi(row[22]); + tmpNPCType->CHA = atoi(row[23]); + tmpNPCType->MR = atoi(row[24]); + tmpNPCType->CR = atoi(row[25]); + tmpNPCType->DR = atoi(row[26]); + tmpNPCType->FR = atoi(row[27]); + tmpNPCType->PR = atoi(row[28]); + tmpNPCType->Corrup = atoi(row[29]); + tmpNPCType->PhR = atoi(row[30]); + tmpNPCType->min_dmg = atoi(row[31]); + tmpNPCType->max_dmg = atoi(row[32]); + tmpNPCType->attack_count = atoi(row[33]); + strn0cpy(tmpNPCType->special_abilities, row[34], 512); + tmpNPCType->npc_spells_id = atoi(row[35]); + tmpNPCType->npc_spells_effects_id = atoi(row[36]); + tmpNPCType->d_meele_texture1 = atoi(row[37]); + tmpNPCType->d_meele_texture2 = atoi(row[38]); + strn0cpy(tmpNPCType->ammo_idfile, row[39], 30); + tmpNPCType->prim_melee_type = atoi(row[40]); + tmpNPCType->sec_melee_type = atoi(row[41]); + tmpNPCType->ranged_type = atoi(row[42]); + tmpNPCType->runspeed= atof(row[43]); + tmpNPCType->findable = atoi(row[44]) == 0? false : true; + tmpNPCType->trackable = atoi(row[45]) == 0? false : true; + tmpNPCType->hp_regen = atoi(row[46]); + tmpNPCType->mana_regen = atoi(row[47]); - if (row[r] && strlen(row[r])) - tmpNPCType->bodytype = (uint8)atoi(row[r]); - else - tmpNPCType->bodytype = 0; - r++; + // set defaultvalue for aggroradius + tmpNPCType->aggroradius = (int32)atoi(row[48]); + if (tmpNPCType->aggroradius <= 0) + tmpNPCType->aggroradius = 70; - tmpNPCType->npc_faction_id = atoi(row[r++]); + tmpNPCType->assistradius = (int32)atoi(row[49]); + if (tmpNPCType->assistradius <= 0) + tmpNPCType->assistradius = tmpNPCType->aggroradius; - tmpNPCType->luclinface = atoi(row[r++]); - tmpNPCType->hairstyle = atoi(row[r++]); - tmpNPCType->haircolor = atoi(row[r++]); - tmpNPCType->eyecolor1 = atoi(row[r++]); - tmpNPCType->eyecolor2 = atoi(row[r++]); - tmpNPCType->beardcolor = atoi(row[r++]); - tmpNPCType->beard = atoi(row[r++]); - tmpNPCType->drakkin_heritage = atoi(row[r++]); - tmpNPCType->drakkin_tattoo = atoi(row[r++]); - tmpNPCType->drakkin_details = atoi(row[r++]); - uint32 armor_tint_id = atoi(row[r++]); - tmpNPCType->armor_tint[0] = (atoi(row[r++]) & 0xFF) << 16; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF); - tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - - int i; - if (armor_tint_id > 0) - { - if (tmpNPCType->armor_tint[0] == 0) - { - char at_errbuf[MYSQL_ERRMSG_SIZE]; - char *at_query = nullptr; - MYSQL_RES *at_result = nullptr; - MYSQL_ROW at_row; + if (row[50] && strlen(row[50])) + tmpNPCType->bodytype = (uint8)atoi(row[50]); + else + tmpNPCType->bodytype = 0; - MakeAnyLenString(&at_query, - "SELECT " - "red1h,grn1h,blu1h," - "red2c,grn2c,blu2c," - "red3a,grn3a,blu3a," - "red4b,grn4b,blu4b," - "red5g,grn5g,blu5g," - "red6l,grn6l,blu6l," - "red7f,grn7f,blu7f," - "red8x,grn8x,blu8x," - "red9x,grn9x,blu9x " - "FROM npc_types_tint WHERE id=%d", armor_tint_id); + tmpNPCType->npc_faction_id = atoi(row[51]); - if (RunQuery(at_query, strlen(at_query), at_errbuf, &at_result)) - { - if ((at_row = mysql_fetch_row(at_result))) - { - for (i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = atoi(at_row[i * 3]) << 16; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 1]) << 8; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 2]); - tmpNPCType->armor_tint[i] |= (tmpNPCType->armor_tint[i]) ? (0xFF << 24) : 0; - } - } - else - { - armor_tint_id = 0; - } - } - else - { - armor_tint_id = 0; - } + tmpNPCType->luclinface = atoi(row[52]); + tmpNPCType->hairstyle = atoi(row[53]); + tmpNPCType->haircolor = atoi(row[54]); + tmpNPCType->eyecolor1 = atoi(row[55]); + tmpNPCType->eyecolor2 = atoi(row[56]); + tmpNPCType->beardcolor = atoi(row[57]); + tmpNPCType->beard = atoi(row[58]); + tmpNPCType->drakkin_heritage = atoi(row[59]); + tmpNPCType->drakkin_tattoo = atoi(row[60]); + tmpNPCType->drakkin_details = atoi(row[61]); - if (at_result) - { - mysql_free_result(at_result); - } + uint32 armor_tint_id = atoi(row[62]); - safe_delete_array(at_query); - } - else - { - armor_tint_id = 0; - } - } + tmpNPCType->armor_tint[0] = (atoi(row[63]) & 0xFF) << 16; + tmpNPCType->armor_tint[0] |= (atoi(row[64]) & 0xFF) << 8; + tmpNPCType->armor_tint[0] |= (atoi(row[65]) & 0xFF); + tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - if (armor_tint_id == 0) - { - for (i = MaterialChest; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = tmpNPCType->armor_tint[0]; - } - } + if (armor_tint_id == 0) + for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++) + tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0]; + else if (tmpNPCType->armor_tint[0] == 0) + { + std::string armortint_query = StringFormat("SELECT red1h, grn1h, blu1h, " + "red2c, grn2c, blu2c, " + "red3a, grn3a, blu3a, " + "red4b, grn4b, blu4b, " + "red5g, grn5g, blu5g, " + "red6l, grn6l, blu6l, " + "red7f, grn7f, blu7f, " + "red8x, grn8x, blu8x, " + "red9x, grn9x, blu9x " + "FROM npc_types_tint WHERE id = %d", + armor_tint_id); + auto armortint_results = QueryDatabase(armortint_query); + if (!armortint_results.Success() || armortint_results.RowCount() == 0) + armor_tint_id = 0; + else { + auto armorTint_row = results.begin(); - tmpNPCType->see_invis = atoi(row[r++]); - tmpNPCType->see_invis_undead = atoi(row[r++])==0?false:true; // Set see_invis_undead flag - if (row[r] != nullptr) - strn0cpy(tmpNPCType->lastname, row[r], 32); - r++; + for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) { + tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]); + tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0; + } + } + } else + armor_tint_id = 0; - tmpNPCType->qglobal = atoi(row[r++])==0?false:true; // qglobal - tmpNPCType->AC = atoi(row[r++]); - tmpNPCType->npc_aggro = atoi(row[r++])==0?false:true; - tmpNPCType->spawn_limit = atoi(row[r++]); - tmpNPCType->see_hide = atoi(row[r++])==0?false:true; - tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true; - tmpNPCType->ATK = atoi(row[r++]); - tmpNPCType->accuracy_rating = atoi(row[r++]); - tmpNPCType->avoidance_rating = atoi(row[r++]); - tmpNPCType->slow_mitigation = atoi(row[r++]); - tmpNPCType->maxlevel = atoi(row[r++]); - tmpNPCType->scalerate = atoi(row[r++]); - tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->unique_spawn_by_name = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->underwater = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->emoteid = atoi(row[r++]); - tmpNPCType->spellscale = atoi(row[r++]); - tmpNPCType->healscale = atoi(row[r++]); - tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->raid_target = atoi(row[r++]) == 0 ? false : true; - - // If NPC with duplicate NPC id already in table, - // free item we attempted to add. - if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) - { - std::cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << std::endl; - delete tmpNPCType; - npc = nullptr; - } else { - zone->npctable[tmpNPCType->npc_id]=tmpNPCType; - npc = tmpNPCType; - } + tmpNPCType->see_invis = atoi(row[66]); + tmpNPCType->see_invis_undead = atoi(row[67]) == 0? false: true; // Set see_invis_undead flag + if (row[68] != nullptr) + strn0cpy(tmpNPCType->lastname, row[68], 32); -// Sleep(0); - } + tmpNPCType->qglobal = atoi(row[69]) == 0? false: true; // qglobal + tmpNPCType->AC = atoi(row[70]); + tmpNPCType->npc_aggro = atoi(row[71]) == 0? false: true; + tmpNPCType->spawn_limit = atoi(row[72]); + tmpNPCType->see_hide = atoi(row[73]) == 0? false: true; + tmpNPCType->see_improved_hide = atoi(row[74]) == 0? false: true; + tmpNPCType->ATK = atoi(row[75]); + tmpNPCType->accuracy_rating = atoi(row[76]); + tmpNPCType->avoidance_rating = atoi(row[77]); + tmpNPCType->slow_mitigation = atoi(row[78]); + tmpNPCType->maxlevel = atoi(row[79]); + tmpNPCType->scalerate = atoi(row[80]); + tmpNPCType->private_corpse = atoi(row[81]) == 1 ? true: false; + tmpNPCType->unique_spawn_by_name = atoi(row[82]) == 1 ? true: false; + tmpNPCType->underwater = atoi(row[83]) == 1 ? true: false; + tmpNPCType->emoteid = atoi(row[84]); + tmpNPCType->spellscale = atoi(row[85]); + tmpNPCType->healscale = atoi(row[86]); + tmpNPCType->no_target_hotkey = atoi(row[87]) == 1 ? true: false; + tmpNPCType->raid_target = atoi(row[88]) == 0 ? false: true; + tmpNPCType->attack_delay = atoi(row[89]); - if (result) { - mysql_free_result(result); - } - } else - std::cerr << "Error loading NPCs from database. Bad query: " << errbuf << std::endl; - safe_delete_array(query); + // If NPC with duplicate NPC id already in table, + // free item we attempted to add. + if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) { + std::cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << std::endl; + delete tmpNPCType; + return nullptr; + } + + zone->npctable[tmpNPCType->npc_id]=tmpNPCType; + npc = tmpNPCType; + } return npc; } - const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 clientlevel) { - const NPCType *npc=nullptr; - std::map::iterator itr; //need to save based on merc_npc_type & client level uint32 merc_type_id = id * 100 + clientlevel; // If NPC is already in tree, return it. - if((itr = zone->merctable.find(merc_type_id)) != zone->merctable.end()) + auto itr = zone->merctable.find(merc_type_id); + if(itr != zone->merctable.end()) return itr->second; + //If the NPC type is 0, return nullptr. (sanity check) if(id == 0) - return nullptr; + return nullptr; // Otherwise, get NPCs from database. - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + // If id is 0, load all npc_types for the current zone, + // according to spawn2. + std::string query = StringFormat("SELECT vwMercNpcTypes.merc_npc_type_id, vwMercNpcTypes.name, " + "vwMercNpcTypes.level, vwMercNpcTypes.race_id, vwMercNpcTypes.class_id, " + "vwMercNpcTypes.hp, vwMercNpcTypes.mana, vwMercNpcTypes.gender, " + "vwMercNpcTypes.texture, vwMercNpcTypes.helmtexture, vwMercNpcTypes.attack_speed, " + "vwMercNpcTypes.STR, vwMercNpcTypes.STA, vwMercNpcTypes.DEX, vwMercNpcTypes.AGI, " + "vwMercNpcTypes._INT, vwMercNpcTypes.WIS, vwMercNpcTypes.CHA, vwMercNpcTypes.MR, " + "vwMercNpcTypes.CR, vwMercNpcTypes.DR, vwMercNpcTypes.FR, vwMercNpcTypes.PR, " + "vwMercNpcTypes.Corrup, vwMercNpcTypes.mindmg, vwMercNpcTypes.maxdmg, " + "vwMercNpcTypes.attack_count, vwMercNpcTypes.special_abilities, " + "vwMercNpcTypes.d_meele_texture1, vwMercNpcTypes.d_meele_texture2, " + "vwMercNpcTypes.prim_melee_type, vwMercNpcTypes.sec_melee_type, " + "vwMercNpcTypes.runspeed, vwMercNpcTypes.hp_regen_rate, vwMercNpcTypes.mana_regen_rate, " + "vwMercNpcTypes.bodytype, vwMercNpcTypes.armortint_id, " + "vwMercNpcTypes.armortint_red, vwMercNpcTypes.armortint_green, vwMercNpcTypes.armortint_blue, " + "vwMercNpcTypes.AC, vwMercNpcTypes.ATK, vwMercNpcTypes.Accuracy, vwMercNpcTypes.spellscale, " + "vwMercNpcTypes.healscale FROM vwMercNpcTypes " + "WHERE merc_npc_type_id = %d AND clientlevel = %d AND race_id = %d", + id, clientlevel, raceid); //dual primary keys. one is ID, one is level. + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl; + return nullptr; + } + const NPCType *npc; - // If id is 0, load all npc_types for the current zone, - // according to spawn2. - const char *basic_query = "SELECT " - "vwMercNpcTypes.merc_npc_type_id," - "vwMercNpcTypes.name," - //"vwMercNpcTypes.clientlevel," - "vwMercNpcTypes.level," - "vwMercNpcTypes.race_id," - "vwMercNpcTypes.class_id," - "vwMercNpcTypes.hp," - "vwMercNpcTypes.mana," - "vwMercNpcTypes.gender," - "vwMercNpcTypes.texture," - "vwMercNpcTypes.helmtexture," - //"vwMercNpcTypes.size," - // "vwMercNpcTypes.loottable_id," - // "vwMercNpcTypes.merchant_id," - // "vwMercNpcTypes.alt_currency_id," - // "vwMercNpcTypes.adventure_template_id," - // "vwMercNpcTypes.trap_template," - "vwMercNpcTypes.attack_speed," - "vwMercNpcTypes.STR," - "vwMercNpcTypes.STA," - "vwMercNpcTypes.DEX," - "vwMercNpcTypes.AGI," - "vwMercNpcTypes._INT," - "vwMercNpcTypes.WIS," - "vwMercNpcTypes.CHA," - "vwMercNpcTypes.MR," - "vwMercNpcTypes.CR," - "vwMercNpcTypes.DR," - "vwMercNpcTypes.FR," - "vwMercNpcTypes.PR," - "vwMercNpcTypes.Corrup," - "vwMercNpcTypes.mindmg," - "vwMercNpcTypes.maxdmg," - "vwMercNpcTypes.attack_count," - "vwMercNpcTypes.special_abilities," - // "vwMercNpcTypes.npc_spells_id," - "vwMercNpcTypes.d_meele_texture1," - "vwMercNpcTypes.d_meele_texture2," - "vwMercNpcTypes.prim_melee_type," - "vwMercNpcTypes.sec_melee_type," - "vwMercNpcTypes.runspeed," - // "vwMercNpcTypes.findable," - // "vwMercNpcTypes.trackable," - "vwMercNpcTypes.hp_regen_rate," - "vwMercNpcTypes.mana_regen_rate," - // "vwMercNpcTypes.aggroradius," - "vwMercNpcTypes.bodytype," - // "vwMercNpcTypes.npc_faction_id," - //"vwMercNpcTypes.face," - //"vwMercNpcTypes.luclin_hairstyle," - //"vwMercNpcTypes.luclin_haircolor," - //"vwMercNpcTypes.luclin_eyecolor," - //"vwMercNpcTypes.luclin_eyecolor2," - //"vwMercNpcTypes.luclin_beardcolor," - //"vwMercNpcTypes.luclin_beard," - //"vwMercNpcTypes.drakkin_heritage," - //"vwMercNpcTypes.drakkin_tattoo," - //"vwMercNpcTypes.drakkin_details," - "vwMercNpcTypes.armortint_id," - "vwMercNpcTypes.armortint_red," - "vwMercNpcTypes.armortint_green," - "vwMercNpcTypes.armortint_blue," - // "vwMercNpcTypes.see_invis," - // "vwMercNpcTypes.see_invis_undead," - // "vwMercNpcTypes.lastname," - // "vwMercNpcTypes.qglobal," - "vwMercNpcTypes.AC," - // "vwMercNpcTypes.npc_aggro," - // "vwMercNpcTypes.spawn_limit," - // "vwMercNpcTypes.see_hide," - // "vwMercNpcTypes.see_improved_hide," - "vwMercNpcTypes.ATK," - "vwMercNpcTypes.Accuracy," - "vwMercNpcTypes.spellscale," - "vwMercNpcTypes.healscale"; - // "vwMercNpcTypes.slow_mitigation," - // "vwMercNpcTypes.maxlevel," - // "vwMercNpcTypes.scalerate," - // "vwMercNpcTypes.private_corpse," - // "vwMercNpcTypes.unique_spawn_by_name," - // "vwMercNpcTypes.underwater," - // "vwMercNpcTypes.emoteid"; + // Process each row returned. + for (auto row = results.begin(); row != results.end(); ++row) { + NPCType *tmpNPCType; + tmpNPCType = new NPCType; + memset (tmpNPCType, 0, sizeof *tmpNPCType); - MakeAnyLenString(&query, "%s FROM vwMercNpcTypes WHERE merc_npc_type_id=%d AND clientlevel=%d AND race_id = %d", basic_query, id, clientlevel, raceid); //dual primary keys. one is ID, one is level. + tmpNPCType->npc_id = atoi(row[0]); - if (RunQuery(query, strlen(query), errbuf, &result)) { - // Process each row returned. - while((row = mysql_fetch_row(result))) { - NPCType *tmpNPCType; - tmpNPCType = new NPCType; - memset (tmpNPCType, 0, sizeof *tmpNPCType); + strn0cpy(tmpNPCType->name, row[1], 50); - int r = 0; - tmpNPCType->npc_id = atoi(row[r++]); + tmpNPCType->level = atoi(row[2]); + tmpNPCType->race = atoi(row[3]); + tmpNPCType->class_ = atoi(row[4]); + tmpNPCType->max_hp = atoi(row[5]); + tmpNPCType->cur_hp = tmpNPCType->max_hp; + tmpNPCType->Mana = atoi(row[6]); + tmpNPCType->gender = atoi(row[7]); + tmpNPCType->texture = atoi(row[8]); + tmpNPCType->helmtexture = atoi(row[9]); + tmpNPCType->attack_speed = atof(row[10]); + tmpNPCType->STR = atoi(row[11]); + tmpNPCType->STA = atoi(row[12]); + tmpNPCType->DEX = atoi(row[13]); + tmpNPCType->AGI = atoi(row[14]); + tmpNPCType->INT = atoi(row[15]); + tmpNPCType->WIS = atoi(row[16]); + tmpNPCType->CHA = atoi(row[17]); + tmpNPCType->MR = atoi(row[18]); + tmpNPCType->CR = atoi(row[19]); + tmpNPCType->DR = atoi(row[20]); + tmpNPCType->FR = atoi(row[21]); + tmpNPCType->PR = atoi(row[22]); + tmpNPCType->Corrup = atoi(row[23]); + tmpNPCType->min_dmg = atoi(row[24]); + tmpNPCType->max_dmg = atoi(row[25]); + tmpNPCType->attack_count = atoi(row[26]); + strn0cpy(tmpNPCType->special_abilities, row[27], 512); - strn0cpy(tmpNPCType->name, row[r++], 50); + tmpNPCType->d_meele_texture1 = atoi(row[28]); + tmpNPCType->d_meele_texture2 = atoi(row[29]); + tmpNPCType->prim_melee_type = atoi(row[30]); + tmpNPCType->sec_melee_type = atoi(row[31]); + tmpNPCType->runspeed= atof(row[32]); - tmpNPCType->level = atoi(row[r++]); - tmpNPCType->race = atoi(row[r++]); - tmpNPCType->class_ = atoi(row[r++]); - tmpNPCType->max_hp = atoi(row[r++]); - tmpNPCType->cur_hp = tmpNPCType->max_hp; - tmpNPCType->Mana = atoi(row[r++]); - tmpNPCType->gender = atoi(row[r++]); - tmpNPCType->texture = atoi(row[r++]); - tmpNPCType->helmtexture = atoi(row[r++]); - //tmpNPCType->size = atof(row[r++]); - //tmpNPCType->loottable_id = atoi(row[r++]); - //tmpNPCType->merchanttype = atoi(row[r++]); - //tmpNPCType->alt_currency_type = atoi(row[r++]); - //tmpNPCType->adventure_template = atoi(row[r++]); - //tmpNPCType->trap_template = atoi(row[r++]); - tmpNPCType->attack_speed = atof(row[r++]); - tmpNPCType->STR = atoi(row[r++]); - tmpNPCType->STA = atoi(row[r++]); - tmpNPCType->DEX = atoi(row[r++]); - tmpNPCType->AGI = atoi(row[r++]); - tmpNPCType->INT = atoi(row[r++]); - tmpNPCType->WIS = atoi(row[r++]); - tmpNPCType->CHA = atoi(row[r++]); - tmpNPCType->MR = atoi(row[r++]); - tmpNPCType->CR = atoi(row[r++]); - tmpNPCType->DR = atoi(row[r++]); - tmpNPCType->FR = atoi(row[r++]); - tmpNPCType->PR = atoi(row[r++]); - tmpNPCType->Corrup = atoi(row[r++]); - tmpNPCType->min_dmg = atoi(row[r++]); - tmpNPCType->max_dmg = atoi(row[r++]); - tmpNPCType->attack_count = atoi(row[r++]); - strn0cpy(tmpNPCType->special_abilities, row[r++], 512); - //tmpNPCType->npc_spells_id = atoi(row[r++]); - tmpNPCType->d_meele_texture1 = atoi(row[r++]); - tmpNPCType->d_meele_texture2 = atoi(row[r++]); - tmpNPCType->prim_melee_type = atoi(row[r++]); - tmpNPCType->sec_melee_type = atoi(row[r++]); - tmpNPCType->runspeed= atof(row[r++]); - //tmpNPCType->findable = atoi(row[r++]) == 0? false : true; - //tmpNPCType->trackable = atoi(row[r++]) == 0? false : true; - tmpNPCType->hp_regen = atoi(row[r++]); - tmpNPCType->mana_regen = atoi(row[r++]); + tmpNPCType->hp_regen = atoi(row[33]); + tmpNPCType->mana_regen = atoi(row[34]); - //tmpNPCType->aggroradius = (int32)atoi(row[r++]); - tmpNPCType->aggroradius = RuleI(Mercs, AggroRadius); - // set defaultvalue for aggroradius - //if (tmpNPCType->aggroradius <= 0) - // tmpNPCType->aggroradius = 70; + tmpNPCType->aggroradius = RuleI(Mercs, AggroRadius); - if (row[r] && strlen(row[r])) - tmpNPCType->bodytype = (uint8)atoi(row[r]); - else - tmpNPCType->bodytype = 1; - r++; + if (row[35] && strlen(row[35])) + tmpNPCType->bodytype = (uint8)atoi(row[35]); + else + tmpNPCType->bodytype = 1; - //tmpNPCType->npc_faction_id = atoi(row[r++]); + uint32 armor_tint_id = atoi(row[36]); + tmpNPCType->armor_tint[0] = (atoi(row[37]) & 0xFF) << 16; + tmpNPCType->armor_tint[0] |= (atoi(row[38]) & 0xFF) << 8; + tmpNPCType->armor_tint[0] |= (atoi(row[39]) & 0xFF); + tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - //tmpNPCType->luclinface = atoi(row[r++]); - //tmpNPCType->hairstyle = atoi(row[r++]); - //tmpNPCType->haircolor = atoi(row[r++]); - //tmpNPCType->eyecolor1 = atoi(row[r++]); - //tmpNPCType->eyecolor2 = atoi(row[r++]); - //tmpNPCType->beardcolor = atoi(row[r++]); - //tmpNPCType->beard = atoi(row[r++]); - //tmpNPCType->drakkin_heritage = atoi(row[r++]); - //tmpNPCType->drakkin_tattoo = atoi(row[r++]); - //tmpNPCType->drakkin_details = atoi(row[r++]); - uint32 armor_tint_id = atoi(row[r++]); - tmpNPCType->armor_tint[0] = (atoi(row[r++]) & 0xFF) << 16; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF); - tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; + if (armor_tint_id == 0) + for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++) + tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0]; + else if (tmpNPCType->armor_tint[0] == 0) { + std::string armorTint_query = StringFormat("SELECT red1h, grn1h, blu1h, " + "red2c, grn2c, blu2c, " + "red3a, grn3a, blu3a, " + "red4b, grn4b, blu4b, " + "red5g, grn5g, blu5g, " + "red6l, grn6l, blu6l, " + "red7f, grn7f, blu7f, " + "red8x, grn8x, blu8x, " + "red9x, grn9x, blu9x " + "FROM npc_types_tint WHERE id = %d", + armor_tint_id); + auto armorTint_results = QueryDatabase(armorTint_query); + if (!results.Success() || results.RowCount() == 0) + armor_tint_id = 0; + else { + auto armorTint_row = results.begin(); - int i; - if (armor_tint_id > 0) - { - if (tmpNPCType->armor_tint[0] == 0) - { - char at_errbuf[MYSQL_ERRMSG_SIZE]; - char *at_query = nullptr; - MYSQL_RES *at_result = nullptr; - MYSQL_ROW at_row; + for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) { + tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]); + tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0; + } + } + } else + armor_tint_id = 0; - MakeAnyLenString(&at_query, - "SELECT " - "red1h,grn1h,blu1h," - "red2c,grn2c,blu2c," - "red3a,grn3a,blu3a," - "red4b,grn4b,blu4b," - "red5g,grn5g,blu5g," - "red6l,grn6l,blu6l," - "red7f,grn7f,blu7f," - "red8x,grn8x,blu8x," - "red9x,grn9x,blu9x " - "FROM npc_types_tint WHERE id=%d", armor_tint_id); + tmpNPCType->AC = atoi(row[40]); + tmpNPCType->ATK = atoi(row[41]); + tmpNPCType->accuracy_rating = atoi(row[42]); + tmpNPCType->scalerate = RuleI(Mercs, ScaleRate); + tmpNPCType->spellscale = atoi(row[43]); + tmpNPCType->healscale = atoi(row[4]); - if (RunQuery(at_query, strlen(at_query), at_errbuf, &at_result)) - { - if ((at_row = mysql_fetch_row(at_result))) - { - for (i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = atoi(at_row[i * 3]) << 16; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 1]) << 8; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 2]); - tmpNPCType->armor_tint[i] |= (tmpNPCType->armor_tint[i]) ? (0xFF << 24) : 0; - } - } - else - { - armor_tint_id = 0; - } - } - else - { - armor_tint_id = 0; - } + // If NPC with duplicate NPC id already in table, + // free item we attempted to add. + if (zone->merctable.find(tmpNPCType->npc_id * 100 + clientlevel) != zone->merctable.end()) { + delete tmpNPCType; + return nullptr; + } - if (at_result) - { - mysql_free_result(at_result); - } - - safe_delete_array(at_query); - } - else - { - armor_tint_id = 0; - } - } - - if (armor_tint_id == 0) - { - for (i = MaterialChest; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = tmpNPCType->armor_tint[0]; - } - } - - //tmpNPCType->see_invis = atoi(row[r++]); - //tmpNPCType->see_invis_undead = atoi(row[r++])==0?false:true; // Set see_invis_undead flag - //if (row[r] != nullptr) - // strn0cpy(tmpNPCType->lastname, row[r], 32); - //r++; - - //tmpNPCType->qglobal = atoi(row[r++])==0?false:true; // qglobal - tmpNPCType->AC = atoi(row[r++]); - //tmpNPCType->npc_aggro = atoi(row[r++])==0?false:true; - //tmpNPCType->spawn_limit = atoi(row[r++]); - //tmpNPCType->see_hide = atoi(row[r++])==0?false:true; - //tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true; - tmpNPCType->ATK = atoi(row[r++]); - tmpNPCType->accuracy_rating = atoi(row[r++]); - //tmpNPCType->slow_mitigation = atof(row[r++]); - //tmpNPCType->maxlevel = atoi(row[r++]); - tmpNPCType->scalerate = RuleI(Mercs, ScaleRate); - //tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false; - //tmpNPCType->unique_spawn_by_name = atoi(row[r++]) == 1 ? true : false; - //tmpNPCType->underwater = atoi(row[r++]) == 1 ? true : false; - //tmpNPCType->emoteid = atoi(row[r++]); - tmpNPCType->spellscale = atoi(row[r++]); - tmpNPCType->healscale = atoi(row[r++]); - - // If NPC with duplicate NPC id already in table, - // free item we attempted to add. - if (zone->merctable.find(tmpNPCType->npc_id * 100 + clientlevel) != zone->merctable.end()) - { - delete tmpNPCType; - npc = nullptr; - } else { - zone->merctable[tmpNPCType->npc_id * 100 + clientlevel]=tmpNPCType; - npc = tmpNPCType; - } - -// Sleep(0); - } - - if (result) { - mysql_free_result(result); - } - } else - std::cerr << "Error loading NPCs from database. Bad query: " << errbuf << std::endl; - safe_delete_array(query); + zone->merctable[tmpNPCType->npc_id * 100 + clientlevel]=tmpNPCType; + npc = tmpNPCType; + } return npc; } -bool ZoneDatabase::LoadMercInfo(Client *c) { - bool loaded = false; +bool ZoneDatabase::LoadMercInfo(Client *client) { - if(c->GetEPP().merc_name[0] != 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - //char name[64]; + if(client->GetEPP().merc_name[0] == 0) + return false; - //CleanMobName(c->GetEPP().merc_name, name); + std::string query = StringFormat("SELECT MercID, Slot, Name, TemplateID, SuspendedTime, " + "IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, " + "Endurance, Face, LuclinHairStyle, LuclinHairColor, " + "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails " + "FROM mercs WHERE OwnerCharacterID = '%i' ORDER BY Slot", client->CharacterID()); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT MercID, Slot, Name, TemplateID, SuspendedTime, IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails FROM mercs WHERE OwnerCharacterID = '%i' ORDER BY Slot", c->CharacterID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - uint8 slot = atoi(DataRow[1]); + for (auto row = results.begin(); row != results.end(); ++row) { + uint8 slot = atoi(row[1]); - if(slot >= MAXMERCS) { - continue; - } + if(slot >= MAXMERCS) + continue; - c->GetMercInfo(slot).mercid = atoi(DataRow[0]); - c->GetMercInfo(slot).slot = slot; - snprintf(c->GetMercInfo(slot).merc_name, 64, "%s", std::string(DataRow[2]).c_str()); - c->GetMercInfo(slot).MercTemplateID = atoi(DataRow[3]); - c->GetMercInfo(slot).SuspendedTime = atoi(DataRow[4]); - c->GetMercInfo(slot).IsSuspended = atoi(DataRow[5]) == 1 ? true : false; - c->GetMercInfo(slot).MercTimerRemaining = atoi(DataRow[6]); - c->GetMercInfo(slot).Gender = atoi(DataRow[7]); - c->GetMercInfo(slot).State = 5; - c->GetMercInfo(slot).Stance = atoi(DataRow[8]); - c->GetMercInfo(slot).hp = atoi(DataRow[9]); - c->GetMercInfo(slot).mana = atoi(DataRow[10]); - c->GetMercInfo(slot).endurance = atoi(DataRow[11]); - c->GetMercInfo(slot).face = atoi(DataRow[12]); - c->GetMercInfo(slot).luclinHairStyle = atoi(DataRow[13]); - c->GetMercInfo(slot).luclinHairColor = atoi(DataRow[14]); - c->GetMercInfo(slot).luclinEyeColor = atoi(DataRow[15]); - c->GetMercInfo(slot).luclinEyeColor2 = atoi(DataRow[16]); - c->GetMercInfo(slot).luclinBeardColor = atoi(DataRow[17]); - c->GetMercInfo(slot).luclinBeard = atoi(DataRow[18]); - c->GetMercInfo(slot).drakkinHeritage = atoi(DataRow[19]); - c->GetMercInfo(slot).drakkinTattoo = atoi(DataRow[20]); - c->GetMercInfo(slot).drakkinDetails = atoi(DataRow[21]); - loaded = true; - } + client->GetMercInfo(slot).mercid = atoi(row[0]); + client->GetMercInfo(slot).slot = slot; + snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[2]); + client->GetMercInfo(slot).MercTemplateID = atoi(row[3]); + client->GetMercInfo(slot).SuspendedTime = atoi(row[4]); + client->GetMercInfo(slot).IsSuspended = atoi(row[5]) == 1 ? true : false; + client->GetMercInfo(slot).MercTimerRemaining = atoi(row[6]); + client->GetMercInfo(slot).Gender = atoi(row[7]); + client->GetMercInfo(slot).State = 5; + client->GetMercInfo(slot).Stance = atoi(row[8]); + client->GetMercInfo(slot).hp = atoi(row[9]); + client->GetMercInfo(slot).mana = atoi(row[10]); + client->GetMercInfo(slot).endurance = atoi(row[11]); + client->GetMercInfo(slot).face = atoi(row[12]); + client->GetMercInfo(slot).luclinHairStyle = atoi(row[13]); + client->GetMercInfo(slot).luclinHairColor = atoi(row[14]); + client->GetMercInfo(slot).luclinEyeColor = atoi(row[15]); + client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[16]); + client->GetMercInfo(slot).luclinBeardColor = atoi(row[17]); + client->GetMercInfo(slot).luclinBeard = atoi(row[18]); + client->GetMercInfo(slot).drakkinHeritage = atoi(row[19]); + client->GetMercInfo(slot).drakkinTattoo = atoi(row[20]); + client->GetMercInfo(slot).drakkinDetails = atoi(row[21]); + } - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return loaded; + return true; } -bool ZoneDatabase::LoadCurrentMerc(Client *c) { - bool loaded = false; +bool ZoneDatabase::LoadCurrentMerc(Client *client) { - if(c->GetEPP().merc_name[0] != 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - //char name[64]; + if(client->GetEPP().merc_name[0] == 0) + return false; - uint8 slot = c->GetMercSlot(); + uint8 slot = client->GetMercSlot(); - if(slot > MAXMERCS) { - return false; - } + if(slot > MAXMERCS) + return false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT MercID, Name, TemplateID, SuspendedTime, IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails FROM mercs WHERE OwnerCharacterID = '%i' AND Slot = '%u'", c->CharacterID(), slot), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - c->GetMercInfo(slot).mercid = atoi(DataRow[0]); - c->GetMercInfo(slot).slot = slot; - snprintf(c->GetMercInfo(slot).merc_name, 64, "%s", std::string(DataRow[1]).c_str()); - c->GetMercInfo(slot).MercTemplateID = atoi(DataRow[2]); - c->GetMercInfo(slot).SuspendedTime = atoi(DataRow[3]); - c->GetMercInfo(slot).IsSuspended = atoi(DataRow[4]) == 1 ? true : false; - c->GetMercInfo(slot).MercTimerRemaining = atoi(DataRow[5]); - c->GetMercInfo(slot).Gender = atoi(DataRow[6]); - c->GetMercInfo(slot).State = atoi(DataRow[7]); - c->GetMercInfo(slot).hp = atoi(DataRow[8]); - c->GetMercInfo(slot).mana = atoi(DataRow[9]); - c->GetMercInfo(slot).endurance = atoi(DataRow[10]); - c->GetMercInfo(slot).face = atoi(DataRow[11]); - c->GetMercInfo(slot).luclinHairStyle = atoi(DataRow[12]); - c->GetMercInfo(slot).luclinHairColor = atoi(DataRow[13]); - c->GetMercInfo(slot).luclinEyeColor = atoi(DataRow[14]); - c->GetMercInfo(slot).luclinEyeColor2 = atoi(DataRow[15]); - c->GetMercInfo(slot).luclinBeardColor = atoi(DataRow[16]); - c->GetMercInfo(slot).luclinBeard = atoi(DataRow[17]); - c->GetMercInfo(slot).drakkinHeritage = atoi(DataRow[18]); - c->GetMercInfo(slot).drakkinTattoo = atoi(DataRow[19]); - c->GetMercInfo(slot).drakkinDetails = atoi(DataRow[20]); - loaded = true; - } + std::string query = StringFormat("SELECT MercID, Name, TemplateID, SuspendedTime, " + "IsSuspended, TimerRemaining, Gender, StanceID, HP, " + "Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, " + "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, " + "LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails " + "FROM mercs WHERE OwnerCharacterID = '%i' AND Slot = '%u'", + client->CharacterID(), slot); + auto results = database.QueryDatabase(query); - mysql_free_result(DatasetResult); - } + if(!results.Success()) + return false; - safe_delete_array(Query); + + for (auto row = results.begin(); row != results.end(); ++row) { + client->GetMercInfo(slot).mercid = atoi(row[0]); + client->GetMercInfo(slot).slot = slot; + snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[1]); + client->GetMercInfo(slot).MercTemplateID = atoi(row[2]); + client->GetMercInfo(slot).SuspendedTime = atoi(row[3]); + client->GetMercInfo(slot).IsSuspended = atoi(row[4]) == 1? true: false; + client->GetMercInfo(slot).MercTimerRemaining = atoi(row[5]); + client->GetMercInfo(slot).Gender = atoi(row[6]); + client->GetMercInfo(slot).State = atoi(row[7]); + client->GetMercInfo(slot).hp = atoi(row[8]); + client->GetMercInfo(slot).mana = atoi(row[9]); + client->GetMercInfo(slot).endurance = atoi(row[10]); + client->GetMercInfo(slot).face = atoi(row[11]); + client->GetMercInfo(slot).luclinHairStyle = atoi(row[12]); + client->GetMercInfo(slot).luclinHairColor = atoi(row[13]); + client->GetMercInfo(slot).luclinEyeColor = atoi(row[14]); + client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[15]); + client->GetMercInfo(slot).luclinBeardColor = atoi(row[16]); + client->GetMercInfo(slot).luclinBeard = atoi(row[17]); + client->GetMercInfo(slot).drakkinHeritage = atoi(row[18]); + client->GetMercInfo(slot).drakkinTattoo = atoi(row[19]); + client->GetMercInfo(slot).drakkinDetails = atoi(row[20]); } - return loaded; + return true; } bool ZoneDatabase::SaveMerc(Merc *merc) { Client *owner = merc->GetMercOwner(); - bool Result = false; - std::string errorMessage; - if(!owner) { + if(!owner) return false; - } - - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - uint32 affectedRows = 0; if(merc->GetMercID() == 0) { // New merc record uint32 TempNewMercID = 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO mercs (OwnerCharacterID, Slot, Name, TemplateID, SuspendedTime, IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails) VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')", merc->GetMercCharacterID(), owner->GetNumMercs(), merc->GetCleanName(), merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(), merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(), merc->GetDrakkinTattoo(), merc->GetDrakkinDetails() ), TempErrorMessageBuffer, 0, &affectedRows, &TempNewMercID)) { - errorMessage = std::string(TempErrorMessageBuffer); + std::string query = StringFormat("INSERT INTO mercs " + "(OwnerCharacterID, Slot, Name, TemplateID, " + "SuspendedTime, IsSuspended, TimerRemaining, " + "Gender, StanceID, HP, Mana, Endurance, Face, " + "LuclinHairStyle, LuclinHairColor, LuclinEyeColor, " + "LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails) " + "VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', " + "'%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i')", + merc->GetMercCharacterID(), owner->GetNumMercs(), + merc->GetCleanName(), merc->GetMercTemplateID(), + owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), + owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), + merc->GetStance(), merc->GetHP(), merc->GetMana(), + merc->GetEndurance(), merc->GetLuclinFace(), + merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), + merc->GetEyeColor2(), merc->GetBeardColor(), + merc->GetBeard(), merc->GetDrakkinHeritage(), + merc->GetDrakkinTattoo(), merc->GetDrakkinDetails()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + owner->Message(13, results.ErrorMessage().c_str()); + return false; + } else if (results.RowsAffected() != 1) { + owner->Message(13, "Unable to save merc to the database."); + return false; } - else { - merc->SetMercID(TempNewMercID); - Result = true; - } - } - else { - // Update existing merc record - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', Name = '%s', TemplateID = '%u', SuspendedTime = '%u', IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', DrakkinDetails = '%i' WHERE MercID = '%u'", merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(), merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(), merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(), merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), merc->GetMercID()), TempErrorMessageBuffer, 0, &affectedRows)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - Result = true; - //time(&_startTotalPlayTime); - } - } - safe_delete_array(Query); - - if(!errorMessage.empty() || (Result && affectedRows != 1)) { - if(owner && !errorMessage.empty()) - owner->Message(13, errorMessage.c_str()); - else if(owner) - owner->Message(13, std::string("Unable to save merc to the database.").c_str()); - - Result = false; - } - else { - merc->UpdateMercInfo(owner); + merc->SetMercID(TempNewMercID); + merc->UpdateMercInfo(owner); database.SaveMercBuffs(merc); - //database.SaveMercStance(this); - //database.SaveMercTimers(this); + return true; } - return Result; + // Update existing merc record + std::string query = StringFormat("UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', " + "Name = '%s', TemplateID = '%u', SuspendedTime = '%u', " + "IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', " + "StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', " + "Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', " + "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', " + "LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', " + "DrakkinDetails = '%i' WHERE MercID = '%u'", + merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(), + merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, + merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, + merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(), + merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(), + merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(), + merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(), + merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), merc->GetMercID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + owner->Message(13, results.ErrorMessage().c_str()); + return false; + } else if (results.RowsAffected() != 1) { + owner->Message(13, "Unable to save merc to the database."); + return false; + } + + merc->UpdateMercInfo(owner); + database.SaveMercBuffs(merc); + + return true; } void ZoneDatabase::SaveMercBuffs(Merc *merc) { + Buffs_Struct *buffs = merc->GetBuffs(); - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; - int InsertCount = 0; - uint32 buff_count = merc->GetMaxBuffSlots(); - 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 merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - } + // Remove any existing buff saves + std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error While Deleting Merc Buffs before save: %s", results.ErrorMessage().c_str()); + return; + } - int IsPersistent = 0; + for (int buffCount = 0; buffCount <= BUFF_COUNT; buffCount++) { + if(buffs[buffCount].spellid == 0 || buffs[buffCount].spellid == SPELL_UNKNOWN) + continue; - if(buffs[BuffCount].persistant_buff) - IsPersistent = 1; - else - IsPersistent = 0; + int IsPersistent = buffs[buffCount].persistant_buff? 1: 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO merc_buffs (MercId, 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);", - merc->GetMercID(), 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()) { - LogFile->write(EQEMuLog::Error, "Error Saving Merc Buffs: %s", errorMessage.c_str()); + query = StringFormat("INSERT INTO merc_buffs (MercId, 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);", + merc->GetMercID(), 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); + results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Saving Merc Buffs: %s", results.ErrorMessage().c_str()); + break; + } } } void ZoneDatabase::LoadMercBuffs(Merc *merc) { Buffs_Struct *buffs = merc->GetBuffs(); uint32 max_slots = merc->GetMaxBuffSlots(); - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + bool BuffsLoaded = false; - - 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 merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; - - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; - - buffs[BuffCount].spellid = atoi(DataRow[0]); - buffs[BuffCount].casterlevel = atoi(DataRow[1]); - buffs[BuffCount].ticsremaining = atoi(DataRow[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; - - bool IsPersistent = false; - - if(atoi(DataRow[13])) - IsPersistent = true; - - buffs[BuffCount].caston_y = atoi(DataRow[13]); - buffs[BuffCount].caston_z = atoi(DataRow[14]); - buffs[BuffCount].ExtraDIChance = atoi(DataRow[15]); - - buffs[BuffCount].persistant_buff = IsPersistent; - - BuffCount++; - } - - mysql_free_result(DatasetResult); - - BuffsLoaded = true; + 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 merc_buffs WHERE MercId = %u", + merc->GetMercID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); + return; } - safe_delete_array(Query); + int buffCount = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++buffCount) { + if(buffCount == BUFF_COUNT) + break; - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - safe_delete_array(Query); - } + 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(row[4]); + + if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[5]); + + if(CalculateCurseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[6]); + + if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[7]); + + buffs[buffCount].numhits = atoi(row[8]); + buffs[buffCount].melee_rune = atoi(row[9]); + buffs[buffCount].magic_rune = atoi(row[10]); + buffs[buffCount].dot_rune = atoi(row[11]); + buffs[buffCount].caston_x = atoi(row[12]); + buffs[buffCount].casterid = 0; + + bool IsPersistent = atoi(row[13])? true: false; + + buffs[buffCount].caston_y = atoi(row[13]); + buffs[buffCount].caston_z = atoi(row[14]); + buffs[buffCount].ExtraDIChance = atoi(row[15]); + + buffs[buffCount].persistant_buff = IsPersistent; + + } + + query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", errorMessage.c_str()); - } } bool ZoneDatabase::DeleteMerc(uint32 merc_id) { - std::string errorMessage; - bool Result = false; - int TempCounter = 0; - if(merc_id > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(merc_id == 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. + bool firstQueryWorked = false; + // TODO: These queries need to be ran together as a transaction.. ie, + // if one or more fail then they all will fail to commit to the database. + std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercID = '%u'", merc_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str()); + else + firstQueryWorked = true; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM merc_buffs WHERE MercID = '%u'", merc_id), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM mercs WHERE MercID = '%u'", merc_id); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str()); + return false; + } - safe_delete_array(Query); - - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM mercs WHERE MercID = '%u'", merc_id), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; - - safe_delete_array(Query); - - if(TempCounter == 2) - Result = true; - } - - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", errorMessage.c_str()); - } - - return Result; + return firstQueryWorked; } void ZoneDatabase::LoadMercEquipment(Merc *merc) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT item_id FROM merc_inventory WHERE merc_subtype_id = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = '%u' AND tier_id = '%u') AND min_level <= %u AND max_level >= %u", merc->GetClass(), merc->GetTierID(), merc->GetLevel(), merc->GetLevel()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int itemCount = 0; - - while(DataRow = mysql_fetch_row(DatasetResult)) { - if (itemCount == EmuConstants::EQUIPMENT_SIZE) - break; - - if(atoi(DataRow[0]) > 0) { - merc->AddItem(itemCount, atoi(DataRow[0])); - - itemCount++; - } - } - - mysql_free_result(DatasetResult); + std::string query = StringFormat("SELECT item_id FROM merc_inventory " + "WHERE merc_subtype_id = (" + "SELECT merc_subtype_id FROM merc_subtypes " + "WHERE class_id = '%u' AND tier_id = '%u') " + "AND min_level <= %u AND max_level >= %u", + merc->GetClass(), merc->GetTierID(), + merc->GetLevel(), merc->GetLevel()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Loading Merc Inventory: %s", results.ErrorMessage().c_str()); + return; } - safe_delete_array(Query); - Query = 0; + int itemCount = 0; + for(auto row = results.begin(); row != results.end(); ++row) { + if (itemCount == EmuConstants::EQUIPMENT_SIZE) + break; - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error Loading Merc Inventory: %s", errorMessage.c_str()); - } + if(atoi(row[0]) == 0) + continue; + + merc->AddItem(itemCount, atoi(row[0])); + itemCount++; + } } uint8 ZoneDatabase::GetGridType(uint32 grid, uint32 zoneid ) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - int type = 0; - if (RunQuery(query, MakeAnyLenString(&query,"SELECT type from grid where id = %i and zoneid = %i",grid,zoneid),errbuf,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - type = atoi( row[0] ); - } - mysql_free_result(result); - } else { - std::cerr << "Error in GetGridType query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + std::string query = StringFormat("SELECT type FROM grid WHERE id = %i AND zoneid = %i", grid, zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetGridType query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - return type; + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } - - void ZoneDatabase::SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "replace into merchantlist_temp (npcid,slot,itemid,charges) values(%d,%d,%d,%d)", npcid, slot, item, charges), errbuf)) { - std::cerr << "Error in SaveMerchantTemp query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("REPLACE INTO merchantlist_temp (npcid, slot, itemid, charges) " + "VALUES(%d, %d, %d, %d)", npcid, slot, item, charges); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in SaveMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; } + void ZoneDatabase::DeleteMerchantTemp(uint32 npcid, uint32 slot){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "delete from merchantlist_temp where npcid=%d and slot=%d", npcid, slot), errbuf)) { - std::cerr << "Error in DeleteMerchantTemp query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM merchantlist_temp WHERE npcid=%d AND slot=%d", npcid, slot); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in DeleteMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; + } - bool ZoneDatabase::UpdateZoneSafeCoords(const char* zonename, float x=0, float y=0, float z=0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE zone SET safe_x='%f', safe_y='%f', safe_z='%f' WHERE short_name='%s';", x, y, z, zonename), errbuf, 0, &affected_rows)) { - safe_delete_array(query); + std::string query = StringFormat("UPDATE zone SET safe_x='%f', safe_y='%f', safe_z='%f' " + "WHERE short_name='%s';", x, y, z, zonename); + auto results = QueryDatabase(query); + if (!results.Success() || results.RowsAffected() == 0) return false; - } - safe_delete_array(query); - - if (affected_rows == 0) - { - return false; - } return true; } - uint8 ZoneDatabase::GetUseCFGSafeCoords() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT value FROM variables WHERE varname='UseCFGSafeCoords'"), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - - uint8 usecoords = atoi(row[0]); - mysql_free_result(result); - return usecoords; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - - std::cerr << "Error in GetUseCFGSafeCoords query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return 0; - -} - - -uint32 ZoneDatabase::GetServerFilters(char* name, ServerSideFilters_Struct *ssfs) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - - MYSQL_ROW row; - - - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT serverfilters FROM account WHERE name='%s'", name), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(ServerSideFilters_Struct)) { - memcpy(ssfs, row[0], sizeof(ServerSideFilters_Struct)); - } - else { - std::cerr << "Player profile length mismatch in ServerSideFilters" << std::endl; - mysql_free_result(result); - return 0; - } - } - else { - mysql_free_result(result); - return 0; - - } - uint32 len = lengths[0]; - mysql_free_result(result); - return len; - } - else { - std::cerr << "Error in ServerSideFilters query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + const std::string query = "SELECT value FROM variables WHERE varname='UseCFGSafeCoords'"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetUseCFGSafeCoords query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } -bool ZoneDatabase::SetServerFilters(char* name, ServerSideFilters_Struct *ssfs) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char query[256+sizeof(ServerSideFilters_Struct)*2+1]; - char* end = query; - - //if (strlen(name) > 15) - // return false; - - /*for (int i=0; i 'z') && - (name[i] < 'A' || name[i] > 'Z') && - (name[i] < '0' || name[i] > '9')) - return 0; -}*/ - - - end += sprintf(end, "UPDATE account SET serverfilters="); - *end++ = '\''; - end += DoEscapeString(end, (char*)ssfs, sizeof(ServerSideFilters_Struct)); - *end++ = '\''; - end += sprintf(end," WHERE name='%s'", name); - - uint32 affected_rows = 0; - if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { - std::cerr << "Error in SetServerSideFilters query " << errbuf << std::endl; - return false; - } - - if (affected_rows == 0) { - return false; - } - - return true; -} - - //New functions for timezone uint32 ZoneDatabase::GetZoneTZ(uint32 zoneid, uint32 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT timezone FROM zone WHERE zoneidnumber=%i AND (version=%i OR version=0) ORDER BY version DESC", zoneid, version), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - uint32 tmp = atoi(row[0]); - mysql_free_result(result); - return tmp; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetZoneTZ query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return 0; + std::string query = StringFormat("SELECT timezone FROM zone WHERE zoneidnumber = %i " + "AND (version = %i OR version = 0) ORDER BY version DESC", + zoneid, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } bool ZoneDatabase::SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE zone SET timezone=%i WHERE zoneidnumber=%i AND version=%i", tz, zoneid, version), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - - if (affected_rows == 1) - return true; - else - return false; - } - else { - std::cerr << "Error in SetZoneTZ query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("UPDATE zone SET timezone = %i " + "WHERE zoneidnumber = %i AND version = %i", + tz, zoneid, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } + } - return false; + return results.RowsAffected() == 1; } -//End new timezone functions. -void ZoneDatabase::RefreshGroupFromDB(Client *c){ - if(!c){ +void ZoneDatabase::RefreshGroupFromDB(Client *client){ + if(!client) return; - } - Group *g = c->GetGroup(); + Group *group = client->GetGroup(); - if(!g){ + if(!group) return; - } EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate2_Struct)); GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer; gu->action = groupActUpdate; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - strcpy(gu->yourname, c->GetName()); - GetGroupLeadershipInfo(g->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); - gu->NPCMarkerID = g->GetNPCMarkerID(); + strcpy(gu->yourname, client->GetName()); + GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); + gu->NPCMarkerID = group->GetNPCMarkerID(); int index = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name from group_id where groupid=%d", g->GetID()), errbuf, &result)) { - while((row = mysql_fetch_row(result))){ - if(index < 6){ - if(strcmp(c->GetName(), row[0]) != 0){ - strcpy(gu->membername[index], row[0]); - index++; - } - } - } - mysql_free_result(result); - } - else - { - printf("Error in group update query: %s\n", errbuf); - } - safe_delete_array(query); - c->QueuePacket(outapp); + std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %d", group->GetID()); + auto results = QueryDatabase(query); + if (!results.Success()) + printf("Error in group update query: %s\n", results.ErrorMessage().c_str()); + else + for (auto row = results.begin(); row != results.end(); ++row) { + if(index >= 6) + continue; + + if(strcmp(client->GetName(), row[0]) == 0) + continue; + + strcpy(gu->membername[index], row[0]); + index++; + } + + client->QueuePacket(outapp); safe_delete(outapp); - if(c->GetClientVersion() >= EQClientSoD) { - g->NotifyMainTank(c, 1); - g->NotifyPuller(c, 1); + if(client->GetClientVersion() >= EQClientSoD) { + group->NotifyMainTank(client, 1); + group->NotifyPuller(client, 1); } - g->NotifyMainAssist(c, 1); - - g->NotifyMarkNPC(c); - g->NotifyAssistTarget(c); - g->NotifyTankTarget(c); - g->NotifyPullerTarget(c); - g->SendMarkedNPCsToMember(c); + group->NotifyMainAssist(client, 1); + group->NotifyMarkNPC(client); + group->NotifyAssistTarget(client); + group->NotifyTankTarget(client); + group->NotifyPullerTarget(client); + group->SendMarkedNPCsToMember(client); } -uint8 ZoneDatabase::GroupCount(uint32 groupid){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint8 count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(charid) FROM group_id WHERE groupid=%d", groupid), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr) - count = atoi(row[0]); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::GroupCount query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; +uint8 ZoneDatabase::GroupCount(uint32 groupid) { + + std::string query = StringFormat("SELECT count(charid) FROM group_id WHERE groupid = %d", groupid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::GroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } - uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint8 count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(charid) FROM raid_members WHERE raidid=%d AND groupid=%d;", raidid, groupid), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr) - count = atoi(row[0]); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::RaidGroupCount query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; +uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) { + + std::string query = StringFormat("SELECT count(charid) FROM raid_members " + "WHERE raidid = %d AND groupid = %d;", raidid, groupid); + auto results = QueryDatabase(query); + + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::RaidGroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } int32 ZoneDatabase::GetBlockedSpellsCount(uint32 zoneid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - sprintf(query, "SELECT count(*) FROM blocked_spells WHERE zoneid=%d", zoneid); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row != nullptr && row[0] != 0) { - int32 ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetBlockedSpellsCount query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT count(*) FROM blocked_spells WHERE zoneid = %d", zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetBlockedSpellsCount query '" << query << "' " << results.ErrorMessage() << std::endl; return -1; } - return -1; + if (results.RowCount() == 0) + return -1; + + auto row = results.begin(); + + return atoi(row[0]); } bool ZoneDatabase::LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid) { LogFile->write(EQEMuLog::Status, "Loading Blocked Spells from database..."); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - MakeAnyLenString(&query, "SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message " - "FROM blocked_spells WHERE zoneid=%d ORDER BY id asc", zoneid); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - int32 r; - for(r = 0; (row = mysql_fetch_row(result)); r++) { - if(r >= blockedSpellsCount) { - std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl; - break; - } - memset(&into[r], 0, sizeof(ZoneSpellsBlocked)); - if(row){ - into[r].spellid = atoi(row[1]); - into[r].type = atoi(row[2]); - into[r].x = atof(row[3]); - into[r].y = atof(row[4]); - into[r].z = atof(row[5]); - into[r].xdiff = atof(row[6]); - into[r].ydiff = atof(row[7]); - into[r].zdiff = atof(row[8]); - strn0cpy(into[r].message, row[9], 255); - } - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in LoadBlockedSpells query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message " + "FROM blocked_spells WHERE zoneid = %d ORDER BY id ASC", zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in LoadBlockedSpells query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } + } + + if (results.RowCount() == 0) + return true; + + int32 index = 0; + for(auto row = results.begin(); row != results.end(); ++row, ++index) { + if(index >= blockedSpellsCount) { + std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl; + break; + } + + memset(&into[index], 0, sizeof(ZoneSpellsBlocked)); + into[index].spellid = atoi(row[1]); + into[index].type = atoi(row[2]); + into[index].x = atof(row[3]); + into[index].y = atof(row[4]); + into[index].z = atof(row[5]); + into[index].xdiff = atof(row[6]); + into[index].ydiff = atof(row[7]); + into[index].zdiff = atof(row[8]); + strn0cpy(into[index].message, row[9], 255); + } + return true; } int ZoneDatabase::getZoneShutDownDelay(uint32 zoneID, uint32 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT shutdowndelay FROM zone " + "WHERE zoneidnumber = %i AND (version=%i OR version=0) " + "ORDER BY version DESC", zoneID, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in getZoneShutDownDelay query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; + return (RuleI(Zone, AutoShutdownDelay)); + } - if (RunQuery(query, MakeAnyLenString(&query, "SELECT shutdowndelay FROM zone WHERE zoneidnumber=%i AND (version=%i OR version=0) ORDER BY version DESC", zoneID, version), errbuf, &result)) - { - if (mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - int retVal = atoi(row[0]); + if (results.RowCount() == 0) { + std::cerr << "Error in getZoneShutDownDelay no result '" << query << "' " << std::endl; + return (RuleI(Zone, AutoShutdownDelay)); + } - mysql_free_result(result); - safe_delete_array(query); - return (retVal); - } - else { - std::cerr << "Error in getZoneShutDownDelay no result '" << query << "' " << errbuf << std::endl; - mysql_free_result(result); - safe_delete_array(query); - return (RuleI(Zone, AutoShutdownDelay)); - } - } - else { - std::cerr << "Error in getZoneShutDownDelay query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return (RuleI(Zone, AutoShutdownDelay)); + auto row = results.begin(); + + return atoi(row[0]); } uint32 ZoneDatabase::GetKarma(uint32 acct_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 ret_val = 0; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `karma` from `account` where `id`='%i' limit 1", - acct_id),errbuf,&result)) - { - safe_delete_array(query); + std::string query = StringFormat("SELECT `karma` FROM `account` WHERE `id` = '%i' LIMIT 1", acct_id); + auto results = QueryDatabase(query); + if (!results.Success()) return 0; - } - safe_delete_array(query); - row = mysql_fetch_row(result); + auto row = results.begin(); - ret_val = atoi(row[0]); - - mysql_free_result(result); - - return ret_val; + return atoi(row[0]); } void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; + std::string query = StringFormat("UPDATE account SET karma = %i WHERE id = %i", amount, acct_id); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in UpdateKarma query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE account set karma=%i where id=%i", amount, acct_id), errbuf, 0, &affected_rows)){ - safe_delete_array(query);} - else { - std::cerr << "Error in UpdateKarma query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } } -void ZoneDatabase::ListAllInstances(Client* c, uint32 charid) +void ZoneDatabase::ListAllInstances(Client* client, uint32 charid) { - if(!c) + if(!client) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT instance_list.id, zone, version " + "FROM instance_list JOIN instance_list_player " + "ON instance_list.id = instance_list_player.id " + "WHERE instance_list_player.charid = %lu", + (unsigned long)charid); + auto results = QueryDatabase(query); + if (!results.Success()) + return; + char name[64]; + database.GetCharName(charid, name); + client->Message(0, "%s is part of the following instances:", name); - if (RunQuery(query,MakeAnyLenString(&query, "SELECT instance_list.id, zone, version FROM instance_list JOIN" - " instance_list_player ON instance_list.id = instance_list_player.id" - " WHERE instance_list_player.charid=%lu", (unsigned long)charid),errbuf,&result)) - { - safe_delete_array(query); - - char name[64]; - database.GetCharName(charid, name); - c->Message(0, "%s is part of the following instances:", name); - while(row = mysql_fetch_row(result)) - { - c->Message(0, "%s - id: %lu, version: %lu", database.GetZoneName(atoi(row[1])), + for (auto row = results.begin(); row != results.end(); ++row) { + client->Message(0, "%s - id: %lu, version: %lu", database.GetZoneName(atoi(row[1])), (unsigned long)atoi(row[0]), (unsigned long)atoi(row[2])); - } - - mysql_free_result(result); - } - else - { - safe_delete_array(query); - } + } } void ZoneDatabase::QGlobalPurge() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM quest_globals WHERE expdate < UNIX_TIMESTAMP()"), - errbuf); - safe_delete_array(query); + const std::string query = "DELETE FROM quest_globals WHERE expdate < UNIX_TIMESTAMP()"; + database.QueryDatabase(query); } void ZoneDatabase::InsertDoor(uint32 ddoordbid, uint16 ddoorid, const char* ddoor_name, float dxpos, float dypos, float dzpos, float dheading, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "replace into doors (id, doorid,zone,version,name,pos_x,pos_y,pos_z,heading,opentype,guild,lockpick,keyitem,door_param,invert_state,incline,size) values('%i','%i','%s','%i', '%s','%f','%f','%f','%f','%i','%i','%i', '%i','%i','%i','%i','%i')", ddoordbid ,ddoorid ,zone->GetShortName(), zone->GetInstanceVersion(), ddoor_name, dxpos, dypos, dzpos, dheading, dopentype, dguildid, dlockpick, dkeyitem, ddoor_param, dinvert, dincline, dsize), errbuf)) { - std::cerr << "Error in InsertDoor" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + + std::string query = StringFormat("REPLACE INTO doors (id, doorid, zone, version, name, " + "pos_x, pos_y, pos_z, heading, opentype, guild, lockpick, " + "keyitem, door_param, invert_state, incline, size) " + "VALUES('%i', '%i', '%s', '%i', '%s', '%f', '%f', " + "'%f', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')", + ddoordbid, ddoorid, zone->GetShortName(), zone->GetInstanceVersion(), + ddoor_name, dxpos, dypos, dzpos, dheading, dopentype, dguildid, + dlockpick, dkeyitem, ddoor_param, dinvert, dincline, dsize); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in InsertDoor" << query << "' " << results.ErrorMessage() << std::endl; } void ZoneDatabase::LoadAltCurrencyValues(uint32 char_id, std::map ¤cy) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT currency_id, amount FROM character_alt_currency where char_id='%u'", char_id), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - currency[atoi(row[0])] = atoi(row[1]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadAltCurrencyValues query '%s': %s", query, errbuf); - safe_delete_array(query); - } + std::string query = StringFormat("SELECT currency_id, amount " + "FROM character_alt_currency " + "WHERE char_id = '%u'", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadAltCurrencyValues query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + currency[atoi(row[0])] = atoi(row[1]); + } void ZoneDatabase::UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO character_alt_currency (char_id, currency_id, amount)" - " VALUES('%u', '%u', '%u')", char_id, currency_id, value), - errbuf); - safe_delete_array(query); + + std::string query = StringFormat("REPLACE INTO character_alt_currency (char_id, currency_id, amount) " + "VALUES('%u', '%u', '%u')", char_id, currency_id, value); + database.QueryDatabase(query); + } -void ZoneDatabase::SaveBuffs(Client *c) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; +void ZoneDatabase::SaveBuffs(Client *client) { - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_buffs` WHERE `character_id`='%u'", c->CharacterID()), - errbuf); + std::string query = StringFormat("DELETE FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID()); + database.QueryDatabase(query); + + uint32 buff_count = client->GetMaxBuffSlots(); + Buffs_Struct *buffs = client->GetBuffs(); + + for (int index = 0; index < buff_count; index++) { + if(buffs[index].spellid == SPELL_UNKNOWN) + continue; + + query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " + "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, " + "magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance) " + "VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', " + "'%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid, + buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining, + buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune, + buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune, + buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z, + buffs[index].ExtraDIChance); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - uint32 buff_count = c->GetMaxBuffSlots(); - Buffs_Struct *buffs = c->GetBuffs(); - for (int i = 0; i < buff_count; i++) { - if(buffs[i].spellid != SPELL_UNKNOWN) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " - "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " - "caston_x, caston_y, caston_z, ExtraDIChance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i')", - c->CharacterID(), i, buffs[i].spellid, buffs[i].casterlevel, buffs[i].caster_name, buffs[i].ticsremaining, - buffs[i].counters, buffs[i].numhits, buffs[i].melee_rune, buffs[i].magic_rune, buffs[i].persistant_buff, - buffs[i].dot_rune, buffs[i].caston_x, buffs[i].caston_y, buffs[i].caston_z, buffs[i].ExtraDIChance), - errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query, errbuf); - } - } } - safe_delete_array(query); } -void ZoneDatabase::LoadBuffs(Client *c) { - Buffs_Struct *buffs = c->GetBuffs(); - uint32 max_slots = c->GetMaxBuffSlots(); - for(int i = 0; i < max_slots; ++i) { - buffs[i].spellid = SPELL_UNKNOWN; - } +void ZoneDatabase::LoadBuffs(Client *client) { + Buffs_Struct *buffs = client->GetBuffs(); + uint32 max_slots = client->GetMaxBuffSlots(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, counters, " - "numhits, melee_rune, magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance FROM `character_buffs` WHERE " - "`character_id`='%u'", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - uint32 slot_id = atoul(row[1]); - if(slot_id >= c->GetMaxBuffSlots()) { - continue; - } + for(int index = 0; index < max_slots; ++index) + buffs[index].spellid = SPELL_UNKNOWN; - uint32 spell_id = atoul(row[0]); - if(!IsValidSpell(spell_id)) { - continue; - } - - Client *caster = entity_list.GetClientByName(row[3]); - uint32 caster_level = atoi(row[2]); - uint32 ticsremaining = atoul(row[4]); - uint32 counters = atoul(row[5]); - uint32 numhits = atoul(row[6]); - uint32 melee_rune = atoul(row[7]); - uint32 magic_rune = atoul(row[8]); - uint8 persistent = atoul(row[9]); - uint32 dot_rune = atoul(row[10]); - int32 caston_x = atoul(row[11]); - int32 caston_y = atoul(row[12]); - int32 caston_z = atoul(row[13]); - int32 ExtraDIChance = atoul(row[14]); - - buffs[slot_id].spellid = spell_id; - buffs[slot_id].casterlevel = caster_level; - if(caster) { - buffs[slot_id].casterid = caster->GetID(); - strcpy(buffs[slot_id].caster_name, caster->GetName()); - buffs[slot_id].client = true; - } else { - buffs[slot_id].casterid = 0; - strcpy(buffs[slot_id].caster_name, ""); - buffs[slot_id].client = false; - } - - buffs[slot_id].ticsremaining = ticsremaining; - buffs[slot_id].counters = counters; - buffs[slot_id].numhits = numhits; - buffs[slot_id].melee_rune = melee_rune; - buffs[slot_id].magic_rune = magic_rune; - buffs[slot_id].persistant_buff = persistent ? true : false; - buffs[slot_id].dot_rune = dot_rune; - buffs[slot_id].caston_x = caston_x; - buffs[slot_id].caston_y = caston_y; - buffs[slot_id].caston_z = caston_z; - buffs[slot_id].ExtraDIChance = ExtraDIChance; - buffs[slot_id].RootBreakChance = 0; - buffs[slot_id].UpdateClient = false; - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadBuffs query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, " + "counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " + "caston_x, caston_y, caston_z, ExtraDIChance " + "FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; - } + } - max_slots = c->GetMaxBuffSlots(); - for(int i = 0; i < max_slots; ++i) { - if(!IsValidSpell(buffs[i].spellid)) { + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 slot_id = atoul(row[1]); + if(slot_id >= client->GetMaxBuffSlots()) continue; - } - for(int j = 0; j < 12; ++j) { - bool cont = false; - switch(spells[buffs[i].spellid].effectid[j]) { - case SE_Charm: - buffs[i].spellid = SPELL_UNKNOWN; - cont = true; - break; - case SE_Illusion: - if(!buffs[i].persistant_buff) { - buffs[i].spellid = SPELL_UNKNOWN; - cont = true; - } - break; - } + uint32 spell_id = atoul(row[0]); + if(!IsValidSpell(spell_id)) + continue; - if(cont) { + Client *caster = entity_list.GetClientByName(row[3]); + uint32 caster_level = atoi(row[2]); + uint32 ticsremaining = atoul(row[4]); + uint32 counters = atoul(row[5]); + uint32 numhits = atoul(row[6]); + uint32 melee_rune = atoul(row[7]); + uint32 magic_rune = atoul(row[8]); + uint8 persistent = atoul(row[9]); + uint32 dot_rune = atoul(row[10]); + int32 caston_x = atoul(row[11]); + int32 caston_y = atoul(row[12]); + int32 caston_z = atoul(row[13]); + int32 ExtraDIChance = atoul(row[14]); + + buffs[slot_id].spellid = spell_id; + buffs[slot_id].casterlevel = caster_level; + + if(caster) { + buffs[slot_id].casterid = caster->GetID(); + strcpy(buffs[slot_id].caster_name, caster->GetName()); + buffs[slot_id].client = true; + } else { + buffs[slot_id].casterid = 0; + strcpy(buffs[slot_id].caster_name, ""); + buffs[slot_id].client = false; + } + + buffs[slot_id].ticsremaining = ticsremaining; + buffs[slot_id].counters = counters; + buffs[slot_id].numhits = numhits; + buffs[slot_id].melee_rune = melee_rune; + buffs[slot_id].magic_rune = magic_rune; + buffs[slot_id].persistant_buff = persistent? true: false; + buffs[slot_id].dot_rune = dot_rune; + buffs[slot_id].caston_x = caston_x; + buffs[slot_id].caston_y = caston_y; + buffs[slot_id].caston_z = caston_z; + buffs[slot_id].ExtraDIChance = ExtraDIChance; + buffs[slot_id].RootBreakChance = 0; + buffs[slot_id].UpdateClient = false; + + } + + max_slots = client->GetMaxBuffSlots(); + for(int index = 0; index < max_slots; ++index) { + if(!IsValidSpell(buffs[index].spellid)) + continue; + + for(int effectIndex = 0; effectIndex < 12; ++effectIndex) { + + if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) { + buffs[index].spellid = SPELL_UNKNOWN; + break; + } + + if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) { + if(buffs[index].persistant_buff) + break; + + buffs[index].spellid = SPELL_UNKNOWN; break; } } } } -void ZoneDatabase::SavePetInfo(Client *c) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int i = 0; - PetInfo *petinfo = c->GetPetInfo(0); - PetInfo *suspended = c->GetPetInfo(1); +void ZoneDatabase::SavePetInfo(Client *client) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_pet_buffs` WHERE `char_id`=%u", c->CharacterID()), - errbuf)) { - safe_delete_array(query); + PetInfo *petinfo = client->GetPetInfo(0); + PetInfo *suspended = client->GetPetInfo(1); + + std::string query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) return; - } - safe_delete_array(query); - if (!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_pet_inventory` WHERE `char_id`=%u", c->CharacterID()), - errbuf)) { - safe_delete_array(query); - // error report + + query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); + results = database.QueryDatabase(query); + if(!results.Success()) return; - } - safe_delete_array(query); - if(!database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_info` (`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " - "values (%u, 0, '%s', %i, %u, %u, %u, %f) " - "ON DUPLICATE KEY UPDATE `petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u, `size`=%f", - c->CharacterID(), petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size, - petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size), - errbuf)) - { - safe_delete_array(query); + query = StringFormat("INSERT INTO `character_pet_info` " + "(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " + "VALUES (%u, 0, '%s', %i, %u, %u, %u, %f) " + "ON DUPLICATE KEY UPDATE `petname` = '%s', `petpower` = %i, `spell_id` = %u, " + "`hp` = %u, `mana` = %u, `size` = %f", + client->CharacterID(), petinfo->Name, petinfo->petpower, petinfo->SpellID, + petinfo->HP, petinfo->Mana, petinfo->size, petinfo->Name, petinfo->petpower, + petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size); + results = database.QueryDatabase(query); + if(!results.Success()) return; - } - safe_delete_array(query); - for(i=0; i < RuleI(Spells, MaxTotalSlotsPET); i++) { - if (petinfo->Buffs[i].spellid != SPELL_UNKNOWN && petinfo->Buffs[i].spellid != 0) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_buffs` (`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " - "`ticsremaining`, `counters`) values " - "(%u, 0, %u, %u, %u, %u, %d)", - c->CharacterID(), i, petinfo->Buffs[i].spellid, petinfo->Buffs[i].level, petinfo->Buffs[i].duration, - petinfo->Buffs[i].counters), - errbuf); - safe_delete_array(query); - } - if (suspended->Buffs[i].spellid != SPELL_UNKNOWN && suspended->Buffs[i].spellid != 0) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_buffs` (`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " - "`ticsremaining`, `counters`) values " - "(%u, 1, %u, %u, %u, %u, %d)", - c->CharacterID(), i, suspended->Buffs[i].spellid, suspended->Buffs[i].level, suspended->Buffs[i].duration, - suspended->Buffs[i].counters), - errbuf); - safe_delete_array(query); - } + for(int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { + if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) + continue; + + query = StringFormat("INSERT INTO `character_pet_buffs` " + "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " + "`ticsremaining`, `counters`) " + "VALUES (%u, 0, %u, %u, %u, %u, %d)", + client->CharacterID(), index, petinfo->Buffs[index].spellid, + petinfo->Buffs[index].level, petinfo->Buffs[index].duration, + petinfo->Buffs[index].counters); + database.QueryDatabase(query); + } + + for(int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { + if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) + continue; + + query = StringFormat("INSERT INTO `character_pet_buffs` " + "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " + "`ticsremaining`, `counters`) " + "VALUES (%u, 1, %u, %u, %u, %u, %d)", + client->CharacterID(), index, suspended->Buffs[index].spellid, + suspended->Buffs[index].level, suspended->Buffs[index].duration, + suspended->Buffs[index].counters); + database.QueryDatabase(query); } - for (i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) { - if(petinfo->Items[i]) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_inventory` (`char_id`, `pet`, `slot`, `item_id`) values (%u, 0, %u, %u)", - c->CharacterID(), i, petinfo->Items[i]), errbuf); - // should check for errors - safe_delete_array(query); - } + for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { + if(!petinfo->Items[index]) + continue; + + query = StringFormat("INSERT INTO `character_pet_inventory` " + "(`char_id`, `pet`, `slot`, `item_id`) " + "VALUES (%u, 0, %u, %u)", + client->CharacterID(), index, petinfo->Items[index]); + database.QueryDatabase(query); } - - if(!database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_info` (`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " - "values (%u, 1, '%s', %u, %u, %u, %u, %f) " - "ON DUPLICATE KEY UPDATE `petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u, `size`=%f", - c->CharacterID(), suspended->Name, suspended->petpower, suspended->SpellID, suspended->HP, suspended->Mana, suspended->size, - suspended->Name, suspended->petpower, suspended->SpellID, suspended->HP, suspended->Mana, suspended->size), - errbuf)) - { - safe_delete_array(query); + query = StringFormat("INSERT INTO `character_pet_info` " + "(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " + "VALUES (%u, 1, '%s', %u, %u, %u, %u, %f) " + "ON DUPLICATE KEY UPDATE " + "`petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u, `size`=%f", + client->CharacterID(), suspended->Name, suspended->petpower, suspended->SpellID, + suspended->HP, suspended->Mana, suspended->size, suspended->Name, suspended->petpower, + suspended->SpellID, suspended->HP, suspended->Mana, suspended->size); + results = database.QueryDatabase(query); + if(!results.Success()) return; - } - safe_delete_array(query); - for (i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) { - if(suspended->Items[i]) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_inventory` (`char_id`, `pet`, `slot`, `item_id`) values (%u, 1, %u, %u)", - c->CharacterID(), i, suspended->Items[i]), errbuf); - // should check for errors - safe_delete_array(query); - } + + for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { + if(!suspended->Items[index]) + continue; + + query = StringFormat("INSERT INTO `character_pet_inventory` " + "(`char_id`, `pet`, `slot`, `item_id`) " + "VALUES (%u, 1, %u, %u)", + client->CharacterID(), index, suspended->Items[index]); + database.QueryDatabase(query); } } -void ZoneDatabase::RemoveTempFactions(Client *c){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; +void ZoneDatabase::RemoveTempFactions(Client *client) { + + std::string query = StringFormat("DELETE FROM faction_values " + "WHERE temp = 1 AND char_id = %u", + client->CharacterID()); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in RemoveTempFactions query '" << query << "' " << results.ErrorMessage() << std::endl; - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM faction_values WHERE temp = 1 AND char_id=%u", c->CharacterID()), errbuf)) { - std::cerr << "Error in RemoveTempFactions query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); } -void ZoneDatabase::LoadPetInfo(Client *c) { +void ZoneDatabase::LoadPetInfo(Client *client) { + // Load current pet and suspended pet - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - PetInfo *petinfo = c->GetPetInfo(0); - PetInfo *suspended = c->GetPetInfo(1); - PetInfo *pi; - uint16 pet; + PetInfo *petinfo = client->GetPetInfo(0); + PetInfo *suspended = client->GetPetInfo(1); memset(petinfo, 0, sizeof(PetInfo)); memset(suspended, 0, sizeof(PetInfo)); - if(database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size` from `character_pet_info` where `char_id`=%u", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - pet = atoi(row[0]); - if (pet == 0) - pi = petinfo; - else if (pet == 1) - pi = suspended; - else - continue; - - strncpy(pi->Name,row[1],64); - pi->petpower = atoi(row[2]); - pi->SpellID = atoi(row[3]); - pi->HP = atoul(row[4]); - pi->Mana = atoul(row[5]); - pi->size = atof(row[6]); - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `pet`, `petname`, `petpower`, `spell_id`, " + "`hp`, `mana`, `size` FROM `character_pet_info` " + "WHERE `char_id` = %u", client->CharacterID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } + PetInfo *pi; + for (auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, " - "`ticsremaining`, `counters` FROM `character_pet_buffs` " - "WHERE `char_id`=%u", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - pet = atoi(row[0]); - if (pet == 0) - pi = petinfo; - else if (pet == 1) - pi = suspended; - else - continue; + if (pet == 0) + pi = petinfo; + else if (pet == 1) + pi = suspended; + else + continue; - uint32 slot_id = atoul(row[1]); - if(slot_id >= RuleI(Spells, MaxTotalSlotsPET)) { - continue; - } - - uint32 spell_id = atoul(row[2]); - if(!IsValidSpell(spell_id)) { - continue; - } - uint32 caster_level = atoi(row[3]); - int caster_id = 0; - // The castername field is currently unused - //Client *caster = entity_list.GetClientByName(row[4]); - //if (caster) { caster_id = caster->GetID(); } - uint32 ticsremaining = atoul(row[5]); - uint32 counters = atoul(row[6]); - - pi->Buffs[slot_id].spellid = spell_id; - pi->Buffs[slot_id].level = caster_level; - pi->Buffs[slot_id].player_id = caster_id; - pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs - - pi->Buffs[slot_id].duration = ticsremaining; - pi->Buffs[slot_id].counters = counters; - } - mysql_free_result(result); + strncpy(pi->Name,row[1],64); + pi->petpower = atoi(row[2]); + pi->SpellID = atoi(row[3]); + pi->HP = atoul(row[4]); + pi->Mana = atoul(row[5]); + pi->size = atof(row[6]); } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); - safe_delete_array(query); + + query = StringFormat("SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, " + "`ticsremaining`, `counters` FROM `character_pet_buffs` " + "WHERE `char_id` = %u", client->CharacterID()); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); + if (pet == 0) + pi = petinfo; + else if (pet == 1) + pi = suspended; + else + continue; + + uint32 slot_id = atoul(row[1]); + if(slot_id >= RuleI(Spells, MaxTotalSlotsPET)) + continue; + + uint32 spell_id = atoul(row[2]); + if(!IsValidSpell(spell_id)) + continue; + + uint32 caster_level = atoi(row[3]); + int caster_id = 0; + // The castername field is currently unused + uint32 ticsremaining = atoul(row[5]); + uint32 counters = atoul(row[6]); + + pi->Buffs[slot_id].spellid = spell_id; + pi->Buffs[slot_id].level = caster_level; + pi->Buffs[slot_id].player_id = caster_id; + pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs + + pi->Buffs[slot_id].duration = ticsremaining; + pi->Buffs[slot_id].counters = counters; + } + + query = StringFormat("SELECT `pet`, `slot`, `item_id` " + "FROM `character_pet_inventory` " + "WHERE `char_id`=%u",client->CharacterID()); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `pet`, `slot`, `item_id` FROM `character_pet_inventory` WHERE `char_id`=%u", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - pet = atoi(row[0]); - if (pet == 0) - pi = petinfo; - else if (pet == 1) - pi = suspended; - else - continue; - - int slot = atoi(row[1]); - if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END) - continue; + for(auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); + if (pet == 0) + pi = petinfo; + else if (pet == 1) + pi = suspended; + else + continue; + + int slot = atoi(row[1]); + if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END) + continue; + + pi->Items[slot] = atoul(row[2]); + } - pi->Items[slot] = atoul(row[2]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); - safe_delete_array(query); - return; - } } bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id) { @@ -3764,23 +3155,18 @@ bool ZoneDatabase::GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, in //o-------------------------------------------------------------- bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM faction_values WHERE char_id=%i AND faction_id = %i", - char_id, faction_id), errbuf)) { - std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM faction_values " + "WHERE char_id=%i AND faction_id = %i", + char_id, faction_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } + } if(value == 0) - { - safe_delete_array(query); return true; - } if(temp == 2) temp = 0; @@ -3788,90 +3174,64 @@ bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, in if(temp == 3) temp = 1; - if (!RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO faction_values (char_id,faction_id,current_value,temp) VALUES (%i,%i,%i,%i)", - char_id, faction_id,value,temp), errbuf, 0, &affected_rows)) { - std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + query = StringFormat("INSERT INTO faction_values (char_id, faction_id, current_value, temp) " + "VALUES (%i, %i, %i, %i)", char_id, faction_id, value, temp); + results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); - - if (affected_rows == 0) - { + if (results.RowsAffected() == 0) return false; - } val_list[faction_id] = value; - return(true); + return true; } bool ZoneDatabase::LoadFactionData() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - strcpy(query, "SELECT MAX(id) FROM faction_list"); - - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row && row[0]) - { - max_faction = atoi(row[0]); - faction_array = new Faction*[max_faction+1]; - for(unsigned int i=0; iname, row[1], 50); - faction_array[index]->base = atoi(row[2]); - - char sec_errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *sec_result; - MYSQL_ROW sec_row; - MakeAnyLenString(&query, "SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id=%u", index); - if (RunQuery(query, strlen(query), sec_errbuf, &sec_result)) { - while((sec_row = mysql_fetch_row(sec_result))) - { - faction_array[index]->mods[sec_row[1]] = atoi(sec_row[0]); - } - mysql_free_result(sec_result); - } - safe_delete_array(query); - } - mysql_free_result(result); - } - else { - std::cerr << "Error in LoadFactionData '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - } - else { - mysql_free_result(result); - } - } - else { - std::cerr << "Error in LoadFactionData '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = "SELECT MAX(id) FROM faction_list"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in LoadFactionData '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - return true; + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + max_faction = atoi(row[0]); + faction_array = new Faction*[max_faction+1]; + for(unsigned int index=0; indexname, row[1], 50); + faction_array[index]->base = atoi(row[2]); + + + query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index); + auto modResults = QueryDatabase(query); + if (!modResults.Success()) + continue; + + for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow) + faction_array[index]->mods[modRow[1]] = atoi(modRow[0]); + + } } bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction) { diff --git a/zone/zonedump.h b/zone/zonedump.h index 0d2bcb1d2..a96d276cd 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -111,6 +111,7 @@ struct NPCType uint8 spawn_limit; //only this many may be in zone at a time (0=no limit) uint8 mount_color; //only used by horse class float attack_speed; //%+- on attack delay of the mob. + uint8 attack_delay; //delay between attacks in 10ths of a second int accuracy_rating; //10 = 1% accuracy int avoidance_rating; //10 = 1% avoidance bool findable; //can be found with find command