diff --git a/.travis.yml b/.travis.yml index 5bdd64324..71606e74c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,7 +8,9 @@ script: - make - ./bin/tests branches: - only: master + only: + - master + - stable notifications: email: false irc: diff --git a/CMakeLists.txt b/CMakeLists.txt index 1d0b6a632..4e2f5f9e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -126,6 +126,7 @@ ENDIF(MSVC) IF(UNIX) IF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") ADD_DEFINITIONS(-DFREEBSD) + ADD_DEFINITIONS(-D_GLIBCXX_USE_C99) SET(FREEBSD TRUE) ENDIF(CMAKE_SYSTEM_NAME MATCHES "FreeBSD") IF(CMAKE_SYSTEM_NAME MATCHES "Darwin") @@ -259,7 +260,10 @@ OPTION(EQEMU_BUILD_CLIENT_FILES "Build Client Import/Export Data Programs." ON) #C++11 stuff IF(NOT MSVC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x") + IF("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-reserved-user-defined-literal") + ENDIF() ENDIF(NOT MSVC) #Various definitions @@ -289,6 +293,7 @@ ADD_DEFINITIONS(-DLOG_LEVEL_DEBUG=${EQEMU_LOG_LEVEL_DEBUG}) ADD_DEFINITIONS(-DLOG_LEVEL_QUEST=${EQEMU_LOG_LEVEL_QUEST}) ADD_DEFINITIONS(-DLOG_LEVEL_COMMANDS=${EQEMU_LOG_LEVEL_COMMANDS}) ADD_DEFINITIONS(-DLOG_LEVEL_CRASH=${EQEMU_LOG_LEVEL_CRASH}) +ADD_DEFINITIONS(-DGLM_FORCE_RADIANS) IF(EQEMU_STREAM_RETRANSMIT_ACKED_PACKETS) ADD_DEFINITIONS(-DRETRANSMIT_ACKED_PACKETS=true) diff --git a/changelog.txt b/changelog.txt index a006c9a50..915f7a812 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,218 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 10/20/2014 == +demonstar55: Inspect Buffs rank 1 will now show NPC buffs in target window (SoD+) + +== 10/19/2014 == +Uleat: Updated command #peekinv to display item links properly in RoF clients +demonstar55: Group Mentoring in raids +demonstar55: Inspect Buffs (text only version) works in raid groups +demonstar55: Make use of the Inspect Buffs op/packet. 62 SOL until someone finds its op + +== 10/18/2014== +demonstar55: Implement group mentor, sharing leadership exp (SoF+ only) +demonstar55: Add gaining of group leadership while in raids + +== 10/16/2014 == +Uleat: Fixed the auto-conversion view naming error and renamed the views in the script files. Added a fix sql for databases that auto-converted. +Fix SQL: ../sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql + +== 10/15/2014 == +Uleat: Cleaned up load/drop bots sqls, added '../utils/sql/git/bots/deprecated' and '../deprecated/load_bots_old.sql' (use this file on pre-player blob conversion databases.) +Notes: I modifed the behavior of both load and drop bots to fail on the first operation if their modifications have been performed already. + 'load_bots.sql' will explicitly add bot schema, while 'drop_bots.sql' will explicitly drop it. I also added a few lines to change + a few altered tables back to their original state - as of the date in the file. + +== 10/13/2014 == +demonstar55: Partially implement leadership and raids + Currently working: client side only effects and stat bonuses. + Not working: Mark NPC, and other stuff that need extra server side support + Currently only UF tested (Tit and 62 may just work, others need packet work) + +== 10/12/2014 == +Akkadius: Fix for LDON Character Stat load + +== 10/11/2014 == +demonstar55: Implement Raid MOTD for UF + Don't forget 2014_10_11_RaidMOTD.sql! + +== 10/09/2014 == +Uleat: Added 'BOTS' conversion code to supplement the database 'PlayerProfile' blob conversion that Akkadius recently implemented. +Note: This automatic conversion uses the view `vwbotcharactermobs` as an update vector. If you need/would like for the converter to run on +previously and/or manually changed code, or just have a need for it to re-run, change the following in the database view and save: + "c.`last_login`," to "c.`timelaston`," + "c.`zone_id`" to "c.`zoneid`" + "FROM `character_data` AS c" to "FROM `character_old` AS c" +** This will only work if you haven't deleted your `character_old` table ** + +== 10/07/2014 == +demonstar55: Identified tutorial flag in all charcreate packets, reworked logic to correctly set homes binds + +== 10/05/2014 == +Uleat: Added Server<->Corpse slot translators needed for re-enumeration (inactive until phased in) + +== 10/03/2014 == +Uleat: Fixed Ti(6.2) OP_AugmentInfo translation that I broke (does not currently need and I mis-read a process) +Uleat: Moved client patch OP_LootItem slot translation to external handlers + +== 10/02/2014 == +Kayen: Exported to PERL $client->SendSpellAnim(targetid, spellid) +This function sends the spell graphic of a spell without actually having to cast the spell. + +== 10/02/2014 == +Uleat: First round of Ti/6.2 translators added - needed for re-enumeration + +== 10/01/2014 == +Kayen: Exported to PERL $client->SendColoredText(color, msg) +demonstar55: Exported SendColoredText to lua + +== 09/30/2014 == +Uleat: Implemented click-casting from bag slots for clients that natively support it (RoF) + +== 09/28/2014 == +demonstar55: Add support for post June 18, 2014 Hundred Hands Effect spells (they changed the formula and stuff) +set Spells:Jun182014HundredHandsRevamp to true if you're using a spell file from June 18, 2014+ + +== 09/27/2014 == +Kayen: Implemented perl function $mob->GetSpellStat(spell_id, identifier, slot); +Note: identifier is the stat field in spells_new, slot is used for certain effects like effectid, base,base2, max ect. +Example $mob->GetSpellStat(121, "range"); //Returns spell range +Example $mob->GetSpellStat(121, "effectid", 1); //Returns the the value of effectid1 +This will allow you to pull almost all the data for any spell in quest files. +demonstar55: Move the client's SetAttackTimer to the end of Client::CalcBonuses to keep the haste in sync +demonstar55: Correct haste/slow "stacking" rules +demonstar55: Correct SE_AttackSpeed4 to respect unslowable +demonstar55: Make the haste be between 1-225 like the client (<100 = slow, >100 = haste) to ... +demonstar55: Correct Hundred Hands effect and use formula provided by devs + +== 09/24/2014 == +Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.) +demonstar55: Added helper function bool EQEmu::IsTradeskill(uint32 skill) + +== 09/23/2014 == +Kayen: Spell recourse effects will now be applied AFTER the base spells effects have been applied (consistent with live). +Kayen: SE_ApplySpell and SE_TriggerSpell will now be applied based on which effect slot they are used in (instead of always before all spell effects are checked). +Note: If a spell has multiple SE_TriggerSpell effects within it. Only one will be able to trigger. (If you want multiple spells use SE_ApplySpell) + +== 09/22/2014 == +Akkadius: #resetaa now covers the function of #resetaa and #refundaa + - #resetaa will wipe all AA data, refund the spent points into the available points and send character to character select properly +Akkadius: Removed #refundaa +Akkadius: Removed a lot of debug code for blob conversion +Akkadius: Changed status logging for loads/saves to Debug category + +== 09/21/2014 == +Akkadius: Player Profile Blob to Database Conversion + - Summary: HUGE difference in database speeds reads/writes and 1:10 datasize difference + - The new character storage engine unlike the character_ table before, is able to properly index data and make use of + proper MySQL/MariaDB caching optimizations and performance has increased phenominally + PERFORMANCE AND STATISTICS FIGURES (Varies on hardware): + - EZ Server Character data size of 2.6GB `character_` table alone now takes up approx 600MB + - Character Data Loads take approx .03 seconds BEFORE MySQL/MariaDB cache + - Character Data Loads take approx .001-.0035 seconds AFTER MySQL/MariaDB cache + - Character Data Saves take approx .0001 - .003 for any particular save operation + - Database Auto Conversion: When the 'character_' table exists, World boot-up will queue an auto-conversion prompt and convert all of your characters, BACKUP + YOUR DATABASE BEFORE CONVERTING, here is an EASY backup script: http://wiki.eqemulator.org/p?MySQL_DB_Backup_Script + - On auto conversion, the following tables are created automatically: + - Table: `character_skills` - Stores Character Skills + - Table: `character_languages` - Stores Character Language + - Table: `character_bind` - Stores Character Bind point and Home Bind point designated by is_home bool field + - Table: `character_alternate_abilities` - Stores all Character AA + - Table: `character_currency` - Stores all Platinum/Gold/Silver/Copper and character related currencies + - Table: `character_data` - Stores basic character data (Fields from `character_` table migrated to this table) + - Table: `character_spells` - Stores character spells + - Table: `character_memmed_spells` - Stores character memorized spells + - Table: `character_disciplines` - Stores character disciplines + - Table: `character_material` - Stores character armor dye textures + - Table: `character_tribute` - Stores character tributes + - Table: `character_bandolier` - Stores character bandoliers + - Table: `character_inspect_messages` - Stores character inspection messages (Moved from `character_` table) + - Table: `character_leadership_abilities` - Stores character Leadership AAs + - Loads: Majority of Player profile loads now occur at Client::Handle_Connect_OP_ZoneEntry + LoadCharacterFactionValues(uint32 character_id, faction_map & val_list); + LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); + LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); + LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); + - Saves: Occur all over the code now instead of calling full saves + SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); + SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); + SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); + SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); + SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value); + SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); + SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); + SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); + SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); + - Deletes: + DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + DeleteCharacterDisc(uint32 character_id, uint32 slot_id); + DeleteCharacterBandolier(uint32 character_id, uint32 band_id); + DeleteCharacterLeadershipAAs(uint32 character_id); + - Now occur all over the code and only trigger when necessary + - Two FULL saves when looting a corpse, this has been reduced to just currency saves on initial loot and trimmed to one save since AddToMoneyPP did it already + - Every time a player moves coin with any situation (Splits/Trades/Merchant/Skills/Bank Coin Exchange/Coin Moves), a full save is made, this is now just a currency save + - Every time a player skilled up at a skill vendor, a full blob save hit was made, this is not just a currency hit + - Every time an AA was purchased, a full save was made + - Every time a spell was scribed/swapped, disc was trained + - When a client exists a zone, when a client enters a zone + - NOTE: These amount of excessive saves have caused scalability issues that cause the `character_` table to hang which causes process hangs that affect the whole server + because of the slowness of the `character_` table and the blob not allowing any indexing to occur + - All functions that once depended on the `character_` table are now rewritten to appropriately read from the `character_data` table + - Database query errors that occur during conversion or from and load/save/delete character functions are now leveraged via ThrowDBError and logs now go to + Server_Folder_Root/eqemu_query_error_log.txt (You cannot log errors natively through MySQL) + - DBASYNC IS NOW COMPLETELY REMOVED - This was mainly for Character data async loads/saves and merchantlist loads + - Side implementations: + Perl Exports: + - quest::crosszonesetentityvariablebynpctypeid(npctype_id, id, m_var) - Sets entity variables world wide with specified npctype_id + - quest::crosszonesignalnpcbynpctypeid(npctype_id, data) - Signals all NPC entities world wide with specified npctype_id + - $client->GetTaskActivityDoneCount(TaskID, ActivityID) - Gets task activity done count by task id and activity id for client entity + + VIEW TABLE SIZE AFTER CONVERT: + + SELECT CONCAT(table_schema, '.', table_name) as table_name, + CONCAT(ROUND(table_rows / 1000000, 2), 'M') rows, + CONCAT(ROUND(data_length / ( 1024 * 1024 * 1024 ), 2), 'G') DATA, + CONCAT(ROUND(index_length / ( 1024 * 1024 * 1024 ), 2), 'G') idx, + CONCAT(ROUND(( data_length + index_length ) / ( 1024 * 1024 * 1024 ), 2), 'G') total_size, + ROUND(index_length / data_length, 2) idxfrac + FROM information_schema.TABLES + WHERE `table_name` LIKE 'character_%' + ORDER BY DATA DESC; + +== 09/20/2014 == +demonstar55: Fix crash in SendEnterWorld on illegally long names +demonstar55: The client only lets you enter 15 characters for your name (UF at least) +demonstar55: Add rule Spells:SHDProcIDOffByOne for pre-UF spell file, set to true, UF+ set to false +KLS: #suspend and #ban now have required messages to record the reason for the ban/suspension. + +== 09/19/2014 == +demonstar55: Added Client::Tell_StringID (used in tell queue messages) +demonstar55: Tell queues (and offline) messages now show correctly +demonstar55: Fix starting with capital check + +== 09/18/2014== +demonstar55: Implement tell queues + Currently set to a limit of 20 by default (World:TellQueueSize) I was unable to hit the limit on live though (100+) + The required SQL nukes the old tell queue table, which may or may not be in your DB + Optional SQL adds the rule to the DB to allow easy of change + Note: this does not play well with multiple sessions with the same name on (crash and relog and have multiple sessions) but normal tells don't play well either + == 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. diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index a6a25f612..ac42f5e4e 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -8,7 +8,6 @@ SET(common_sources crc16.cpp crc32.cpp database.cpp - dbasync.cpp dbcore.cpp debug.cpp emu_opcodes.cpp @@ -56,8 +55,9 @@ SET(common_sources rulesys.cpp serverinfo.cpp shareddb.cpp + skills.cpp spdat.cpp - string_util.cpp + string_util.cpp struct_strategy.cpp tcp_connection.cpp tcp_server.cpp @@ -103,8 +103,8 @@ SET(common_headers crash.h crc16.h crc32.h + data_verification.h database.h - dbasync.h dbcore.h debug.h deity.h diff --git a/common/clientversions.h b/common/clientversions.h index 47a7d0204..ee70d481c 100644 --- a/common/clientversions.h +++ b/common/clientversions.h @@ -17,7 +17,8 @@ static const uint32 BIT_RoFAndLater = 0xFFFFFFE0; static const uint32 BIT_RoF2AndLater = 0xFFFFFFC0; static const uint32 BIT_AllClients = 0xFFFFFFFF; -typedef enum { +typedef enum +{ EQClientUnknown = 0, EQClient62, // Build: 'Aug 4 2005 15:40:59' EQClientTitanium, // Build: 'Oct 31 2005 10:33:37' @@ -26,17 +27,50 @@ typedef enum { EQClientUnderfoot, // Build: 'Jun 8 2010 16:44:32' EQClientRoF, // Build: 'Dec 10 2012 17:35:44' EQClientRoF2, // Build: 'May 10 2013 23:30:08' - + _EQClientCount, // place new clients before this point (preferably, in release/attribute order) - + // Values below are not implemented, as yet... - + EmuNPC = _EQClientCount, EmuMerc, EmuBot, EmuPet, - + _EmuClientCount // array size for EQLimits } EQClientVersion; +static const char* EQClientVersionName(EQClientVersion version) +{ + switch (version) + { + case EQClientUnknown: + return "EQClientUnknown"; + case EQClient62: + return "EQClient62"; + case EQClientTitanium: + return "EQClientTitanium"; + case EQClientSoF: + return "EQClientSoF"; + case EQClientSoD: + return "EQClientSoD"; + case EQClientUnderfoot: + return "EQClientUnderfoot"; + case EQClientRoF: + return "EQClientRoF"; + case EQClientRoF2: + return "EQClientRoF2"; + case EmuNPC: + return "EmuNPC"; + case EmuMerc: + return "EmuMerc"; + case EmuBot: + return "EmuBot"; + case EmuPet: + return "EmuPet"; + default: + return "ERROR: Invalid EQClientVersion"; + }; +} + #endif /* CLIENTVERSIONS_H */ diff --git a/common/data_verification.h b/common/data_verification.h new file mode 100644 index 000000000..9da85a579 --- /dev/null +++ b/common/data_verification.h @@ -0,0 +1,48 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#ifndef COMMON_DATA_VERIFICATION_H +#define COMMON_DATA_VERIFICATION_H + +#include + +namespace EQEmu +{ + +template +T Clamp(const T& value, const T& lower, const T& upper) { + return std::max(lower, std::min(value, upper)); +} + +template +T ClampLower(const T& value, const T& lower) { + return std::max(lower, value); +} + +template +T ClampUpper(const T& value, const T& upper) { + return std::min(value, upper); +} + +template +bool ValueWithin(const T& value, const T& lower, const T& upper) { + return value >= lower && value <= upper; +} + +} + +#endif diff --git a/common/database.cpp b/common/database.cpp index 88f01a676..599d615a7 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -18,6 +18,8 @@ #include "../common/debug.h" #include "../common/rulesys.h" #include +#include +#include #include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include #include // Disgrace: for windows compile @@ -47,6 +50,16 @@ #include "extprofile.h" extern Client client; +#ifdef _WINDOWS +#if _MSC_VER > 1700 // greater than 2012 (2013+) +# define _ISNAN_(a) std::isnan(a) +#else +# include +# define _ISNAN_(a) _isnan(a) +#endif +#else +# define _ISNAN_(a) std::isnan(a) +#endif /* Establish a connection to a mysql database with the supplied parameters @@ -70,55 +83,25 @@ Database::Database(const char* host, const char* user, const char* passwd, const Connect(host, user, passwd, database, port); } -bool Database::Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port) -{ +bool Database::Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port) { uint32 errnum= 0; char errbuf[MYSQL_ERRMSG_SIZE]; - if (!Open(host, user, passwd, database, port, &errnum, errbuf)) - { + if (!Open(host, user, passwd, database, port, &errnum, errbuf)) { LogFile->write(EQEMuLog::Error, "Failed to connect to database: Error: %s", errbuf); - HandleMysqlError(errnum); - return false; + return false; } - else - { + else { LogFile->write(EQEMuLog::Status, "Using database '%s' at %s:%d",database,host,port); return true; } } -void Database::DBInitVars() { - +void Database::DBInitVars() { varcache_array = 0; varcache_max = 0; varcache_lastupdate = 0; } - -void Database::HandleMysqlError(uint32 errnum) { -/* switch(errnum) { - case 0: - break; - case 1045: // Access Denied - case 2001: { - AddEQEMuError(EQEMuError_Mysql_1405, true); - break; - } - case 2003: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2003, true); - break; - } - case 2005: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2005, true); - break; - } - case 2007: { // Unable to connect - AddEQEMuError(EQEMuError_Mysql_2007, true); - break; - } - }*/ -} - /* Close the connection to the database @@ -193,24 +176,18 @@ bool Database::CheckBannedIPs(const char* loginIP) return false; } -bool Database::AddBannedIP(char* bannedIP, const char* notes) -{ - std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); - - auto results = QueryDatabase(query); - - if (!results.Success()) - { - std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; +bool Database::AddBannedIP(char* bannedIP, const char* notes) { + std::string query = StringFormat("INSERT into Banned_IPs SET ip_address='%s', notes='%s'", bannedIP, notes); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in Database::AddBannedIP query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } - + } return true; } bool Database::CheckGMIPs(const char* ip_address, uint32 account_id) { - std::string query = StringFormat("SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id); - + std::string query = StringFormat("SELECT * FROM `gm_ips` WHERE `ip_address` = '%s' AND `account_id` = %i", ip_address, account_id); auto results = QueryDatabase(query); if (!results.Success()) @@ -223,32 +200,24 @@ bool Database::AddBannedIP(char* bannedIP, const char* notes) } bool Database::AddGMIP(char* ip_address, char* name) { - std::string query = StringFormat("INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name); - - auto results = QueryDatabase(query); - + std::string query = StringFormat("INSERT into `gm_ips` SET `ip_address` = '%s', `name` = '%s'", ip_address, name); + auto results = QueryDatabase(query); return results.Success(); } -void Database::LoginIP(uint32 AccountID, const char* LoginIP) -{ - std::string query = StringFormat("INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP); - - auto results = QueryDatabase(query); - +void Database::LoginIP(uint32 AccountID, const char* LoginIP) { + std::string query = StringFormat("INSERT INTO account_ip SET accid=%i, ip='%s' ON DUPLICATE KEY UPDATE count=count+1, lastused=now()", AccountID, LoginIP); + auto results = QueryDatabase(query); if (!results.Success()) std::cerr << "Error in Log IP query '" << query << "' " << results.ErrorMessage() << std::endl; } -int16 Database::CheckStatus(uint32 account_id) -{ +int16 Database::CheckStatus(uint32 account_id) { std::string query = StringFormat("SELECT `status`, UNIX_TIMESTAMP(`suspendeduntil`) as `suspendeduntil`, UNIX_TIMESTAMP() as `current`" " FROM `account` WHERE `id` = %i", account_id); - auto results = QueryDatabase(query); - - if (!results.Success()) - { + auto results = QueryDatabase(query); + if (!results.Success()) { std::cerr << "Error in CheckStatus query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -256,10 +225,8 @@ int16 Database::CheckStatus(uint32 account_id) if (results.RowCount() != 1) return 0; - auto row = results.begin(); - - int16 status = atoi(row[0]); - + auto row = results.begin(); + int16 status = atoi(row[0]); int32 suspendeduntil = 0; // MariaDB initalizes with NULL if unix_timestamp() is out of range @@ -283,12 +250,10 @@ uint32 Database::CreateAccount(const char* name, const char* password, int16 sta else query = StringFormat("INSERT INTO account SET name='%s', status=%i, lsaccount_id=%i, time_creation=UNIX_TIMESTAMP();",name, status, lsaccount_id); - std::cerr << "Account Attempting to be created:" << name << " " << (int16) status << std::endl; - + std::cerr << "Account Attempting to be created:" << name << " " << (int16) status << std::endl; auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in CreateAccount query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -303,8 +268,7 @@ uint32 Database::CreateAccount(const char* name, const char* password, int16 sta } bool Database::DeleteAccount(const char* name) { - std::string query = StringFormat("DELETE FROM account WHERE name='%s';",name); - + std::string query = StringFormat("DELETE FROM account WHERE name='%s';",name); std::cout << "Account Attempting to be deleted:" << name << std::endl; auto results = QueryDatabase(query); @@ -344,279 +308,450 @@ bool Database::SetAccountStatus(const char* name, int16 status) { if (results.RowsAffected() == 0) { std::cout << "Account: " << name << " does not exist, therefore it cannot be flagged\n"; - return false; + return false; } return true; } -bool Database::ReserveName(uint32 account_id, char* name) -{ - std::string query = StringFormat("INSERT into character_ SET account_id=%i, name='%s', profile=NULL", account_id, name); - +/* This initially creates the character during character create */ +bool Database::ReserveName(uint32 account_id, char* name) { + std::string query = StringFormat("INSERT INTO `character_data` SET `account_id` = %i, `name` = '%s'", account_id, name); auto results = QueryDatabase(query); - - if (!results.Success()) - { - std::cerr << "Error in ReserveName query '" << query << "' " << results.ErrorMessage() << std::endl; - return false; - } - + if (!results.Success() || results.ErrorMessage() != ""){ return false; } return true; } +bool Database::ThrowDBError(std::string ErrorMessage, std::string query_title, std::string query){ + if (ErrorMessage != ""){ + std::cout << "\nERROR " << query_title << ": " << ErrorMessage << "\n\n" << query << "\n" << std::endl; + + /* Write to log file */ + std::ofstream log("eqemu_query_error_log.txt", std::ios_base::app | std::ios_base::out); + log << "ERROR " << query_title << ": " << ErrorMessage << "\n" << query << "\n"; + log.close(); + + return true; + } + return false; +} + /* -Delete the character with the name "name" -returns false on failure, true otherwise + Delete the character with the name "name" + returns false on failure, true otherwise */ -bool Database::DeleteCharacter(char *name) -{ - std::string query=StringFormat("SELECT id from character_ WHERE name='%s'", name); - int charid; - - if(!name || !strlen(name)) - { +bool Database::DeleteCharacter(char *name) { + uint32 charid = 0; + printf("Database::DeleteCharacter name : %s \n", name); + if(!name || !strlen(name)) { std::cerr << "DeleteCharacter: request to delete without a name (empty char slot)" << std::endl; return false; } -// get id from character_ before deleting record so we can clean up inventory and qglobal - -#if DEBUG >= 5 - std::cout << "DeleteCharacter: Attempting to delete '" << name << "'" << std::endl; -#endif - + /* Get id from character_data before deleting record so we can clean up the rest of the tables */ + std::string query = StringFormat("SELECT `id` from `character_data` WHERE `name` = '%s'", name); auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { charid = atoi(row[0]); } + if (charid <= 0){ std::cerr << "Database::DeleteCharacter :: Character not found, stopping delete...\n"; return false; } - if(results.RowCount() != 1) - { - std::cerr << "DeleteCharacter error: got " << results.RowCount() << " rows matching '" << name << "'" << std::endl; - return false; + query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); + query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::DeleteCharacter", query); +#ifdef BOTS + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d' AND GetMobTypeById(%i) = 'C'", charid); +#else + query = StringFormat("DELETE FROM `guild_members` WHERE `char_id` = '%d'", charid); +#endif + QueryDatabase(query); + + return true; +} + +bool Database::SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "REPLACE INTO `character_data` (" + "id," + "account_id," + "`name`," + "last_name," + "gender," + "race," + "class," + "`level`," + "deity," + "birthday," + "last_login," + "time_played," + "pvp_status," + "level2," + "anon," + "gm," + "intoxication," + "hair_color," + "beard_color," + "eye_color_1," + "eye_color_2," + "hair_style," + "beard," + "ability_time_seconds," + "ability_number," + "ability_time_minutes," + "ability_time_hours," + "title," + "suffix," + "exp," + "points," + "mana," + "cur_hp," + "str," + "sta," + "cha," + "dex," + "`int`," + "agi," + "wis," + "face," + "y," + "x," + "z," + "heading," + "pvp2," + "pvp_type," + "autosplit_enabled," + "zone_change_count," + "drakkin_heritage," + "drakkin_tattoo," + "drakkin_details," + "toxicity," + "hunger_level," + "thirst_level," + "ability_up," + "zone_id," + "zone_instance," + "leadership_exp_on," + "ldon_points_guk," + "ldon_points_mir," + "ldon_points_mmc," + "ldon_points_ruj," + "ldon_points_tak," + "ldon_points_available," + "tribute_time_remaining," + "show_helm," + "career_tribute_points," + "tribute_points," + "tribute_active," + "endurance," + "group_leadership_exp," + "raid_leadership_exp," + "group_leadership_points," + "raid_leadership_points," + "air_remaining," + "pvp_kills," + "pvp_deaths," + "pvp_current_points," + "pvp_career_points," + "pvp_best_kill_streak," + "pvp_worst_death_streak," + "pvp_current_kill_streak," + "aa_points_spent," + "aa_exp," + "aa_points," + "group_auto_consent," + "raid_auto_consent," + "guild_auto_consent," + "RestTimer) " + "VALUES (" + "%u," // id + "%u," // account_id + "'%s'," // `name` + "'%s'," // last_name + "%u," // gender + "%u," // race + "%u," // class + "%u," // `level` + "%u," // deity + "%u," // birthday + "%u," // last_login + "%u," // time_played + "%u," // pvp_status + "%u," // level2 + "%u," // anon + "%u," // gm + "%u," // intoxication + "%u," // hair_color + "%u," // beard_color + "%u," // eye_color_1 + "%u," // eye_color_2 + "%u," // hair_style + "%u," // beard + "%u," // ability_time_seconds + "%u," // ability_number + "%u," // ability_time_minutes + "%u," // ability_time_hours + "'%s'," // title + "'%s'," // suffix + "%u," // exp + "%u," // points + "%u," // mana + "%u," // cur_hp + "%u," // str + "%u," // sta + "%u," // cha + "%u," // dex + "%u," // `int` + "%u," // agi + "%u," // wis + "%u," // face + "%f," // y + "%f," // x + "%f," // z + "%f," // heading + "%u," // pvp2 + "%u," // pvp_type + "%u," // autosplit_enabled + "%u," // zone_change_count + "%u," // drakkin_heritage + "%u," // drakkin_tattoo + "%u," // drakkin_details + "%i," // toxicity + "%i," // hunger_level + "%i," // thirst_level + "%u," // ability_up + "%u," // zone_id + "%u," // zone_instance + "%u," // leadership_exp_on + "%u," // ldon_points_guk + "%u," // ldon_points_mir + "%u," // ldon_points_mmc + "%u," // ldon_points_ruj + "%u," // ldon_points_tak + "%u," // ldon_points_available + "%u," // tribute_time_remaining + "%u," // show_helm + "%u," // career_tribute_points + "%u," // tribute_points + "%u," // tribute_active + "%u," // endurance + "%u," // group_leadership_exp + "%u," // raid_leadership_exp + "%u," // group_leadership_point + "%u," // raid_leadership_points + "%u," // air_remaining + "%u," // pvp_kills + "%u," // pvp_deaths + "%u," // pvp_current_points + "%u," // pvp_career_points + "%u," // pvp_best_kill_streak + "%u," // pvp_worst_death_streak + "%u," // pvp_current_kill_strea + "%u," // aa_points_spent + "%u," // aa_exp + "%u," // aa_points + "%u," // group_auto_consent + "%u," // raid_auto_consent + "%u," // guild_auto_consent + "%u" // RestTimer + ")", + character_id, // " id, " + account_id, // " account_id, " + EscapeString(pp->name).c_str(), // " `name`, " + EscapeString(pp->last_name).c_str(), // " last_name, " + pp->gender, // " gender, " + pp->race, // " race, " + pp->class_, // " class, " + pp->level, // " `level`, " + pp->deity, // " deity, " + pp->birthday, // " birthday, " + pp->lastlogin, // " last_login, " + pp->timePlayedMin, // " time_played, " + pp->pvp, // " pvp_status, " + pp->level2, // " level2, " + pp->anon, // " anon, " + pp->gm, // " gm, " + pp->intoxication, // " intoxication, " + pp->haircolor, // " hair_color, " + pp->beardcolor, // " beard_color, " + pp->eyecolor1, // " eye_color_1, " + pp->eyecolor2, // " eye_color_2, " + pp->hairstyle, // " hair_style, " + pp->beard, // " beard, " + pp->ability_time_seconds, // " ability_time_seconds, " + pp->ability_number, // " ability_number, " + pp->ability_time_minutes, // " ability_time_minutes, " + pp->ability_time_hours, // " ability_time_hours, " + EscapeString(pp->title).c_str(), // " title, " + EscapeString(pp->suffix).c_str(), // " suffix, " + pp->exp, // " exp, " + pp->points, // " points, " + pp->mana, // " mana, " + pp->cur_hp, // " cur_hp, " + pp->STR, // " str, " + pp->STA, // " sta, " + pp->CHA, // " cha, " + pp->DEX, // " dex, " + pp->INT, // " `int`, " + pp->AGI, // " agi, " + pp->WIS, // " wis, " + pp->face, // " face, " + pp->y, // " y, " + pp->x, // " x, " + pp->z, // " z, " + pp->heading, // " heading, " + pp->pvp2, // " pvp2, " + pp->pvptype, // " pvp_type, " + pp->autosplit, // " autosplit_enabled, " + pp->zone_change_count, // " zone_change_count, " + pp->drakkin_heritage, // " drakkin_heritage, " + pp->drakkin_tattoo, // " drakkin_tattoo, " + pp->drakkin_details, // " drakkin_details, " + pp->toxicity, // " toxicity, " + pp->hunger_level, // " hunger_level, " + pp->thirst_level, // " thirst_level, " + pp->ability_up, // " ability_up, " + pp->zone_id, // " zone_id, " + pp->zoneInstance, // " zone_instance, " + pp->leadAAActive, // " leadership_exp_on, " + pp->ldon_points_guk, // " ldon_points_guk, " + pp->ldon_points_mir, // " ldon_points_mir, " + pp->ldon_points_mmc, // " ldon_points_mmc, " + pp->ldon_points_ruj, // " ldon_points_ruj, " + pp->ldon_points_tak, // " ldon_points_tak, " + pp->ldon_points_available, // " ldon_points_available, " + pp->tribute_time_remaining, // " tribute_time_remaining, " + pp->showhelm, // " show_helm, " + pp->career_tribute_points, // " career_tribute_points, " + pp->tribute_points, // " tribute_points, " + pp->tribute_active, // " tribute_active, " + pp->endurance, // " endurance, " + pp->group_leadership_exp, // " group_leadership_exp, " + pp->raid_leadership_exp, // " raid_leadership_exp, " + pp->group_leadership_points, // " group_leadership_points, " + pp->raid_leadership_points, // " raid_leadership_points, " + pp->air_remaining, // " air_remaining, " + pp->PVPKills, // " pvp_kills, " + pp->PVPDeaths, // " pvp_deaths, " + pp->PVPCurrentPoints, // " pvp_current_points, " + pp->PVPCareerPoints, // " pvp_career_points, " + pp->PVPBestKillStreak, // " pvp_best_kill_streak, " + pp->PVPWorstDeathStreak, // " pvp_worst_death_streak, " + pp->PVPCurrentKillStreak, // " pvp_current_kill_streak, " + pp->aapoints_spent, // " aa_points_spent, " + pp->expAA, // " aa_exp, " + pp->aapoints, // " aa_points, " + pp->groupAutoconsent, // " group_auto_consent, " + pp->raidAutoconsent, // " raid_auto_consent, " + pp->guildAutoconsent, // " guild_auto_consent, " + pp->RestTimer // " RestTimer) " + ); + auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Character Data", query); + /* Save Bind Points */ + query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i), " + "(%u, %u, %u, %f, %f, %f, %f, %i)", + character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading, 0, + character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading, 1 + ); results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Bind Point", query); + + /* Save Skills */ + int firstquery = 0; + for (int i = 0; i < MAX_PP_SKILL; i++){ + if (pp->skills[i] > 0){ + if (firstquery != 1){ + firstquery = 1; + query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); + } + else{ + query = query + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); + } + } } + results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Starting Skills", query); - auto row = results.begin(); - charid = atoi(row[0]); - -#if DEBUG >= 5 - std::cout << "DeleteCharacter: found '" << name << "' with char id: " << charid << std::endl; - std::cout << "DeleteCharacter: deleting << '" << name << "' (id " << charid << "): " << std::endl; - std::cout << " quest_globals"; -#endif - - query = StringFormat("DELETE from quest_globals WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " character_tasks"; -#endif - - query = StringFormat("DELETE from character_tasks WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " character_activities"; -#endif - - query = StringFormat("DELETE from character_activities WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " character_enabledtasks"; -#endif - - query = StringFormat("DELETE from character_enabledtasks WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " completed_tasks"; -#endif - - query = StringFormat("DELETE from completed_tasks WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " friends"; -#endif - - query = StringFormat("DELETE from friends WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " mail"; -#endif - - query = StringFormat( "DELETE from mail WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " ptimers"; -#endif - - query = StringFormat("DELETE from timers WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " inventory"; -#endif - - query = StringFormat("DELETE from inventory WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " guild_members"; -#endif - -#ifdef BOTS - query = StringFormat("DELETE FROM guild_members WHERE char_id='%d' AND GetMobTypeById(%i) = 'C'", charid); -#else - query = StringFormat("DELETE FROM guild_members WHERE char_id='%d'", charid); -#endif - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " recipes"; -#endif - - query = StringFormat("DELETE FROM char_recipe_list WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " adventure_stats"; -#endif - - query = StringFormat("DELETE FROM adventure_stats WHERE player_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " zone_flags"; -#endif - - query = StringFormat("DELETE FROM zone_flags WHERE charID='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " titles"; -#endif - - query = StringFormat("DELETE FROM titles WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " titlesets"; -#endif - - query = StringFormat("DELETE FROM player_titlesets WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " keyring"; -#endif - - query = StringFormat("DELETE FROM keyring WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " factions"; -#endif - - query = StringFormat("DELETE FROM faction_values WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " instances"; -#endif - - query = StringFormat("DELETE FROM instance_list_player WHERE charid='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << " _character"; -#endif - - query = StringFormat("DELETE from character_ WHERE id='%d'", charid); - results = QueryDatabase(query); - - if(results.RowsAffected() != 1) // here we have to have a match or it's an error - { - LogFile->write(EQEMuLog::Error, "DeleteCharacter: error: delete operation affected %d rows\n", results.RowsAffected()); - return false; + /* Save Language */ + firstquery = 0; + for (int i = 0; i < MAX_PP_LANGUAGE; i++){ + if (pp->languages[i] > 0){ + if (firstquery != 1){ + firstquery = 1; + query = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); + } + else{ + query = query + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); + } + } } - -#if DEBUG >= 5 - std::cout << " alternate currency"; -#endif - - query = StringFormat("DELETE FROM character_alt_currency WHERE char_id='%d'", charid); - QueryDatabase(query); - -#if DEBUG >= 5 - std::cout << std::endl; -#endif - std::cout << "DeleteCharacter: successfully deleted '" << name << "' (id " << charid << ")" << std::endl; + results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "Database::SaveCharacterCreate Starting Languages", query); return true; } -// Store new character information into the character_ and inventory tables -bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext) -{ - char query[256+sizeof(PlayerProfile_Struct)*2+sizeof(ExtendedProfile_Struct)*2+5]; - char* end = query; - uint32 charid = 0; - char zone[50]; - float x, y, z; - +/* This only for new Character creation storing */ +bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv) { + uint32 charid = 0; + char zone[50]; + float x, y, z; charid = GetCharacterID(pp->name); - if(!charid) - { + if(!charid) { LogFile->write(EQEMuLog::Error, "StoreCharacter: no character id"); return false; } const char *zname = GetZoneName(pp->zone_id); if(zname == nullptr) { - //zone not in the DB, something to prevent crash... + /* Zone not in the DB, something to prevent crash... */ strn0cpy(zone, "qeynos", 49); pp->zone_id = 1; - } else - strn0cpy(zone, zname, 49); - - x=pp->x; - y=pp->y; - z=pp->z; - - // construct the character_ query - end += sprintf(end, - "UPDATE character_ SET timelaston=0, " - "zonename=\'%s\', x=%f, y=%f, z=%f, profile=\'", - zone, x, y, z - ); - end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end, "\', extprofile=\'"); - end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end, "\' WHERE account_id=%d AND name='%s'",account_id, pp->name); - - auto results = QueryDatabase(query, (uint32) (end - query)); - // stack assigned query, no need to delete it. - - if(!results.RowsAffected()) - { - LogFile->write(EQEMuLog::Error, "StoreCharacter query '%s' %s", query, results.ErrorMessage().c_str()); - return false; } + else{ strn0cpy(zone, zname, 49); } - // now the inventory + x = pp->x; + y = pp->y; + z = pp->z; + + /* Saves Player Profile Data */ + SaveCharacterCreate(charid, account_id, pp); + + /* Insert starting inventory... */ std::string invquery; - for (int16 i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::BANK_BAGS_END;) - { + for (int16 i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::BANK_BAGS_END;) { const ItemInst* newinv = inv->GetItem(i); - if (newinv) - { + if (newinv) { invquery = StringFormat("INSERT INTO `inventory` (charid, slotid, itemid, charges, color) VALUES (%u, %i, %u, %i, %u)", charid, i, newinv->GetItem()->ID, newinv->GetCharges(), newinv->GetColor()); - auto results = QueryDatabase(invquery); + auto results = QueryDatabase(invquery); if (!results.RowsAffected()) LogFile->write(EQEMuLog::Error, "StoreCharacter inventory failed. Query '%s' %s", invquery.c_str(), results.ErrorMessage().c_str()); @@ -626,40 +761,38 @@ bool Database::StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inven #endif } - if (i == MainCursor) { - i = EmuConstants::GENERAL_BAGS_BEGIN; + if (i == MainCursor) { + i = EmuConstants::GENERAL_BAGS_BEGIN; continue; } - else if (i == EmuConstants::CURSOR_BAG_END) { - i = EmuConstants::BANK_BEGIN; - continue; + else if (i == EmuConstants::CURSOR_BAG_END) { + i = EmuConstants::BANK_BEGIN; + continue; } - else if (i == EmuConstants::BANK_END) { - i = EmuConstants::BANK_BAGS_BEGIN; - continue; - } - + else if (i == EmuConstants::BANK_END) { + i = EmuConstants::BANK_BAGS_BEGIN; + continue; + } i++; } - return true; } -//0=failure, otherwise returns the char ID for the given char name. uint32 Database::GetCharacterID(const char *name) { - uint32 cid = 0; - if(GetAccountIDByChar(name, &cid) == 0) - return(0); - return(cid); + std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name` = '%s'", name); + auto results = QueryDatabase(query); + auto row = results.begin(); + if (row[0]){ return atoi(row[0]); } + return 0; } /* -This function returns the account_id that owns the character with -the name "name" or zero if no character with that name was found -Zero will also be returned if there is a database error. + This function returns the account_id that owns the character with + the name "name" or zero if no character with that name was found + Zero will also be returned if there is a database error. */ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { - std::string query = StringFormat("SELECT account_id, id FROM character_ WHERE name='%s'", charname); + std::string query = StringFormat("SELECT `account_id`, `id` FROM `character_data` WHERE name='%s'", EscapeString(charname).c_str()); auto results = QueryDatabase(query); @@ -670,7 +803,7 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { } if (results.RowCount() != 1) - return 0; + return 0; auto row = results.begin(); @@ -684,12 +817,9 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { // Retrieve account_id for a given char_id uint32 Database::GetAccountIDByChar(uint32 char_id) { - std::string query = StringFormat("SELECT account_id FROM character_ WHERE id=%i", char_id); - - auto results = QueryDatabase(query); - - if (!results.Success()) - { + std::string query = StringFormat("SELECT `account_id` FROM `character_data` WHERE `id` = %i LIMIT 1", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in GetAccountIDByChar query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return 0; } @@ -697,8 +827,7 @@ uint32 Database::GetAccountIDByChar(uint32 char_id) { if (results.RowCount() != 1) return 0; - auto row = results.begin(); - + auto row = results.begin(); return atoi(row[0]); } @@ -706,11 +835,10 @@ uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* if (!isAlphaNumeric(accname)) return 0; - std::string query = StringFormat("SELECT id, status, lsaccount_id FROM account WHERE name='%s'", accname); + std::string query = StringFormat("SELECT `id`, `status`, `lsaccount_id` FROM `account` WHERE `name` = '%s' LIMIT 1", accname); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetAccountIDByAcc query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -725,8 +853,7 @@ uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* if (status) *status = atoi(row[1]); - if (lsid) - { + if (lsid) { if (row[2]) *lsid = atoi(row[2]); else @@ -737,12 +864,10 @@ uint32 Database::GetAccountIDByName(const char* accname, int16* status, uint32* } void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID) { - std::string query = StringFormat("SELECT name, lsaccount_id FROM account WHERE id='%i'", accountid); - + std::string query = StringFormat("SELECT `name`, `lsaccount_id` FROM `account` WHERE `id` = '%i'", accountid); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetAccountName query '" << query << "' " << results.ErrorMessage() << std::endl; return; } @@ -759,15 +884,13 @@ void Database::GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID } -void Database::GetCharName(uint32 char_id, char* name) { - - std::string query = StringFormat("SELECT name FROM character_ WHERE id='%i'", char_id); +void Database::GetCharName(uint32 char_id, char* name) { + std::string query = StringFormat("SELECT `name` FROM `character_data` WHERE id='%i'", char_id); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetCharName query '" << query << "' " << results.ErrorMessage() << std::endl; - return; + return; } auto row = results.begin(); @@ -776,6 +899,1194 @@ void Database::GetCharName(uint32 char_id, char* name) { } } +static inline void loadbar(unsigned int x, unsigned int n, unsigned int w = 50) { + if ((x != n) && (x % (n / 100 + 1) != 0)) return; + float ratio = x / (float)n; + int c = ratio * w; + std::cout << std::setw(3) << (int)(ratio * 100) << "% ["; + for (int x = 0; xexpended_aa > 4000000){ e_pp->expended_aa = 0; } + + /* Loading Status on conversion */ + if (runconvert == 1){ + std::cout << "\r" << char_iter_count << "/" << number_of_characters << " " << std::flush; + loadbar(char_iter_count, number_of_characters, 50); + + /* Run inspect message convert */ + if (inspectmessage != ""){ + std::string rquery = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message)" + "VALUES (%u, '%s')", + character_id, + EscapeString(inspectmessage).c_str() + ); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Character Inspect Message Convert", rquery); + } + + /* Run Currency Convert */ + std::string rquery = StringFormat("REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," + "platinum_bank, gold_bank, silver_bank, copper_bank," + "platinum_cursor, gold_cursor, silver_cursor, copper_cursor, " + "radiant_crystals, career_radiant_crystals, ebon_crystals, career_ebon_crystals)" + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", + character_id, + pp->platinum, + pp->gold, + pp->silver, + pp->copper, + pp->platinum_bank, + pp->gold_bank, + pp->silver_bank, + pp->copper_bank, + pp->platinum_cursor, + pp->gold_cursor, + pp->silver_cursor, + pp->copper_cursor, + pp->currentRadCrystals, + pp->careerRadCrystals, + pp->currentEbonCrystals, + pp->careerEbonCrystals + ); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Character Currency Convert", rquery); + + if (pp->tribute_time_remaining < 0 || pp->tribute_time_remaining == 4294967295){ pp->tribute_time_remaining = 0; } + + /* Run Character Data Convert */ + rquery = StringFormat( + "REPLACE INTO `character_data` (" + "id," + "account_id," + "`name`," + "last_name," + "gender," + "race," + "class," + "`level`," + "deity," + "birthday," + "last_login," + "time_played," + "pvp_status," + "level2," + "anon," + "gm," + "intoxication," + "hair_color," + "beard_color," + "eye_color_1," + "eye_color_2," + "hair_style," + "beard," + "ability_time_seconds," + "ability_number," + "ability_time_minutes," + "ability_time_hours," + "title," + "suffix," + "exp," + "points," + "mana," + "cur_hp," + "str," + "sta," + "cha," + "dex," + "`int`," + "agi," + "wis," + "face," + "y," + "x," + "z," + "heading," + "pvp2," + "pvp_type," + "autosplit_enabled," + "zone_change_count," + "drakkin_heritage," + "drakkin_tattoo," + "drakkin_details," + "toxicity," + "hunger_level," + "thirst_level," + "ability_up," + "zone_id," + "zone_instance," + "leadership_exp_on," + "ldon_points_guk," + "ldon_points_mir," + "ldon_points_mmc," + "ldon_points_ruj," + "ldon_points_tak," + "ldon_points_available," + "tribute_time_remaining," + "show_helm," + "career_tribute_points," + "tribute_points," + "tribute_active," + "endurance," + "group_leadership_exp," + "raid_leadership_exp," + "group_leadership_points," + "raid_leadership_points," + "air_remaining," + "pvp_kills," + "pvp_deaths," + "pvp_current_points," + "pvp_career_points," + "pvp_best_kill_streak," + "pvp_worst_death_streak," + "pvp_current_kill_streak," + "aa_points_spent," + "aa_exp," + "aa_points," + "group_auto_consent," + "raid_auto_consent," + "guild_auto_consent," + "RestTimer," + "firstlogon," + "lfg," + "lfp," + "mailkey," + "xtargets," + "e_aa_effects," + "e_percent_to_aa," + "e_expended_aa_spent" + ")" + "VALUES (" + "%u," // id + "%u," // account_id + "'%s'," // `name` + "'%s'," // last_name + "%u," // gender + "%u," // race + "%u," // class + "%u," // `level` + "%u," // deity + "%u," // birthday + "%u," // last_login + "%u," // time_played + "%u," // pvp_status + "%u," // level2 + "%u," // anon + "%u," // gm + "%u," // intoxication + "%u," // hair_color + "%u," // beard_color + "%u," // eye_color_1 + "%u," // eye_color_2 + "%u," // hair_style + "%u," // beard + "%u," // ability_time_seconds + "%u," // ability_number + "%u," // ability_time_minutes + "%u," // ability_time_hours + "'%s'," // title + "'%s'," // suffix + "%u," // exp + "%u," // points + "%u," // mana + "%u," // cur_hp + "%u," // str + "%u," // sta + "%u," // cha + "%u," // dex + "%u," // `int` + "%u," // agi + "%u," // wis + "%u," // face + "%f," // y + "%f," // x + "%f," // z + "%f," // heading + "%u," // pvp2 + "%u," // pvp_type + "%u," // autosplit_enabled + "%u," // zone_change_count + "%u," // drakkin_heritage + "%u," // drakkin_tattoo + "%u," // drakkin_details + "%i," // toxicity + "%u," // hunger_level + "%u," // thirst_level + "%u," // ability_up + "%u," // zone_id + "%u," // zone_instance + "%u," // leadership_exp_on + "%u," // ldon_points_guk + "%u," // ldon_points_mir + "%u," // ldon_points_mmc + "%u," // ldon_points_ruj + "%u," // ldon_points_tak + "%u," // ldon_points_available + "%u," // tribute_time_remaining + "%u," // show_helm + "%u," // career_tribute_points + "%u," // tribute_points + "%u," // tribute_active + "%u," // endurance + "%u," // group_leadership_exp + "%u," // raid_leadership_exp + "%u," // group_leadership_points + "%u," // raid_leadership_points + "%u," // air_remaining + "%u," // pvp_kills + "%u," // pvp_deaths + "%u," // pvp_current_points + "%u," // pvp_career_points + "%u," // pvp_best_kill_streak + "%u," // pvp_worst_death_streak + "%u," // pvp_current_kill_streak + "%u," // aa_points_spent + "%u," // aa_exp + "%u," // aa_points + "%u," // group_auto_consent + "%u," // raid_auto_consent + "%u," // guild_auto_consent + "%u," // RestTimer + "%u," // First Logon - References online status for EVENT_CONNECT/EVENT_DISCONNECt + "%u," // Looking for Group + "%u," // Looking for P? + "'%s'," // Mailkey + "%u," // X Targets + "%u," // AA Effects + "%u," // Percent to AA + "%u" // e_expended_aa_spent + ")", + character_id, + account_id, + EscapeString(pp->name).c_str(), + EscapeString(pp->last_name).c_str(), + pp->gender, + pp->race, + pp->class_, + pp->level, + pp->deity, + pp->birthday, + pp->lastlogin, + pp->timePlayedMin, + pp->pvp, + pp->level2, + pp->anon, + pp->gm, + pp->intoxication, + pp->haircolor, + pp->beardcolor, + pp->eyecolor1, + pp->eyecolor2, + pp->hairstyle, + pp->beard, + pp->ability_time_seconds, + pp->ability_number, + pp->ability_time_minutes, + pp->ability_time_hours, + EscapeString(pp->title).c_str(), + EscapeString(pp->suffix).c_str(), + pp->exp, + pp->points, + pp->mana, + pp->cur_hp, + pp->STR, + pp->STA, + pp->CHA, + pp->DEX, + pp->INT, + pp->AGI, + pp->WIS, + pp->face, + pp->y, + pp->x, + pp->z, + pp->heading, + pp->pvp2, + pp->pvptype, + pp->autosplit, + pp->zone_change_count, + pp->drakkin_heritage, + pp->drakkin_tattoo, + pp->drakkin_details, + pp->toxicity, + pp->hunger_level, + pp->thirst_level, + pp->ability_up, + pp->zone_id, + pp->zoneInstance, + pp->leadAAActive == 0 ? 0 : 1, + pp->ldon_points_guk, + pp->ldon_points_mir, + pp->ldon_points_mmc, + pp->ldon_points_ruj, + pp->ldon_points_tak, + pp->ldon_points_available, + pp->tribute_time_remaining, + pp->showhelm, + pp->career_tribute_points, + pp->tribute_points, + pp->tribute_active, + pp->endurance, + pp->group_leadership_exp, + pp->raid_leadership_exp, + pp->group_leadership_points, + pp->raid_leadership_points, + pp->air_remaining, + pp->PVPKills, + pp->PVPDeaths, + pp->PVPCurrentPoints, + pp->PVPCareerPoints, + pp->PVPBestKillStreak, + pp->PVPWorstDeathStreak, + pp->PVPCurrentKillStreak, + pp->aapoints_spent, + pp->expAA, + pp->aapoints, + pp->groupAutoconsent, + pp->raidAutoconsent, + pp->guildAutoconsent, + pp->RestTimer, + firstlogon, + lfg, + lfp, + mailkey.c_str(), + xtargets, + e_pp->aa_effects, + e_pp->perAA, + e_pp->expended_aa + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Character Data Convert", rquery); + + + /* + We set a first entry variable because we need the first initial piece of the query to be declared + This is to speed up the INSERTS and trim down the amount of individual sends during the process. + The speed difference is dramatic + */ + /* Run AA Convert */ + int first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_AA_ARRAY; i++){ + if (pp->aa_array[i].AA > 0 && pp->aa_array[i].value > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + " VALUES (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); + first_entry = 1; + } else { + rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, i, pp->aa_array[i].AA, pp->aa_array[i].value); + } + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "AA Convert", rquery); } + + /* Run Bind Home Convert */ + if(pp->binds[4].zoneId < 999 && !_ISNAN_(pp->binds[4].x) && !_ISNAN_(pp->binds[4].y) && !_ISNAN_(pp->binds[4].z) && !_ISNAN_(pp->binds[4].heading)) { + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 1)", + character_id, pp->binds[4].zoneId, 0, pp->binds[4].x, pp->binds[4].y, pp->binds[4].z, pp->binds[4].heading); + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Bind Home Convert", rquery); } + } + + /* Run Bind Convert */ + if(pp->binds[0].zoneId < 999 && !_ISNAN_(pp->binds[0].x) && !_ISNAN_(pp->binds[0].y) && !_ISNAN_(pp->binds[0].z) && !_ISNAN_(pp->binds[0].heading)) { + rquery = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, 0)", + character_id, pp->binds[0].zoneId, 0, pp->binds[0].x, pp->binds[0].y, pp->binds[0].z, pp->binds[0].heading); + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Bind Convert", rquery); } + } + /* Run Language Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_LANGUAGE; i++){ + if (pp->languages[i] > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, i, pp->languages[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->languages[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Language Convert", rquery); } + /* Run Skill Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_SKILL; i++){ + if (pp->skills[i] > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, i, pp->skills[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->skills[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Skills Convert Convert", rquery); } + /* Run Spell Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++){ + if (pp->spell_book[i] > 0 && pp->spell_book[i] != 4294967295 && pp->spell_book[i] < 40000 && pp->spell_book[i] != 1){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->spell_book[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->spell_book[i]); + } + } + // std::cout << rquery << "\n"; + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Spell Convert", rquery); } + /* Run Max Memmed Spell Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_REF_MEMSPELL; i++){ + if (pp->mem_spells[i] > 0 && pp->mem_spells[i] != 65535 && pp->mem_spells[i] != 4294967295){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, i, pp->mem_spells[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->mem_spells[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Memmed Spells Convert", rquery); } + /* Run Discipline Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_PP_DISCIPLINES; i++){ + if(pp->disciplines.values[i] > 0 && pp->disciplines.values[i] < 60000){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, i, pp->disciplines.values[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Discipline Convert", rquery); } + /* Run Material Color Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < _MaterialCount; i++){ + if (pp->item_tint[i].color > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_material` (id, slot, blue, green, red, use_tint, color) VALUES (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u)", character_id, i, pp->item_tint[i].rgb.blue, pp->item_tint[i].rgb.green, pp->item_tint[i].rgb.red, pp->item_tint[i].rgb.use_tint, pp->item_tint[i].color); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Material Convert", rquery); } + /* Run Tribute Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != 4294967295){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Tribute Convert", rquery); } + /* Run Bandolier Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < EmuConstants::BANDOLIERS_COUNT; i++){ + if(strlen(pp->bandoliers[i].name) < 32) { + for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + if (pp->bandoliers[i].items[si].item_id > 0){ + if (first_entry != 1) { + rquery = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %i, %u, %u, '%s')", character_id, i, si, pp->bandoliers[i].items[si].item_id, pp->bandoliers[i].items[si].icon, pp->bandoliers[i].name); + } + } + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Bandolier Convert", rquery); } + /* Run Potion Belt Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ + if (pp->potionbelt.items[i].item_id > 0){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %u, %u)", character_id, i, pp->potionbelt.items[i].item_id, pp->potionbelt.items[i].icon); + + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Potion Belt Convert", rquery); } + /* Run Leadership AA Convert */ + first_entry = 0; rquery = ""; + for (i = 0; i < MAX_LEADERSHIP_AA_ARRAY; i++){ + if(pp->leader_abilities.ranks[i] > 0 && pp->leader_abilities.ranks[i] < 6){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + } + } + if (rquery != ""){ results = QueryDatabase(rquery); ThrowDBError(results.ErrorMessage(), "Character Leadership AA Convert", rquery); } + } + } + if (runconvert == 1){ + std::string rquery = StringFormat("RENAME TABLE `character_` TO `character_old`"); QueryDatabase(rquery); + printf("\n\nRenaming `character_` table to `character_old`, this is a LARGE table so when you don't need it anymore, I would suggest deleting it yourself...\n"); + printf("\n\nCharacter blob conversion complete, continuing world bootup...\n"); + } + + +#ifdef BOTS + + int runbotsconvert = 0; + + /* Check For Legacy Bot References */ + rquery = StringFormat("SHOW CREATE VIEW `vwBotCharacterMobs`"); + results = QueryDatabase(rquery); + if (results.RowCount() == 1){ + auto row = results.begin(); + std::string table_check = row[1]; + + if (table_check.find("character_data") == -1){ + runbotsconvert = 1; + printf("\n\n::: Legacy Bot Views and Function Detected... \n"); + printf("----------------------------------------------------------\n\n"); + printf(" Database currently has bot view/function linkage to obselete \n"); + printf(" table references and will now be converted...\n\n"); + printf(" It is recommended that you backup your database \n"); + printf(" before continuing the automatic conversion process...\n\n"); + printf("----------------------------------------------------------\n\n"); + std::cout << "Press ENTER to continue....." << std::endl << std::endl; + std::cin.ignore(1); + } + } + else{ + ThrowDBError(results.ErrorMessage(), "Bot View Discovery", rquery); + } + + if (runbotsconvert == 1){ + printf("Running bot views/function database conversion... \n"); + + /* Update view `vwbotcharactermobs` */ + rquery = StringFormat("DROP VIEW `vwBotCharacterMobs`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwBotCharacterMobs`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwBotCharacterMobs` AS\n" + "SELECT _utf8'C' AS mobtype,\n" // Natedog: '_utf8' + "c.`id`,\n" + "c.`name`,\n" + "c.`class`,\n" + "c.`level`,\n" + "c.`last_login`,\n" + "c.`zone_id`\n" + "FROM `character_data` AS c\n" + "UNION ALL\n" + "SELECT _utf8'B' AS mobtype,\n" // Natedog: '_utf8' + "b.`BotID` AS id,\n" + "b.`Name` AS name,\n" + "b.`Class` AS class,\n" + "b.`BotLevel` AS level,\n" + "0 AS timelaston,\n" + "0 AS zoneid\n" + "FROM bots AS b;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwBotCharacterMobs`", rquery); + + + /* Update function `GetMobType` */ + rquery = StringFormat("DROP FUNCTION IF EXISTS `GetMobType`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop Function `GetMobType`", rquery); + + rquery = StringFormat( + "CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1)\n" + "BEGIN\n" + " DECLARE Result CHAR(1);\n" + "\n" + " SET Result = NULL;\n" + "\n" + " IF (SELECT COUNT(*) FROM `character_data` WHERE `name` = mobname) > 0 THEN\n" + " SET Result = 'C';\n" + " ELSEIF (SELECT COUNT(*) FROM `bots` WHERE `Name` = mobname) > 0 THEN\n" + " SET Result = 'B';\n" + " END IF;\n " + "\n" + " RETURN Result;\n" + "END" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create Function `GetMobType`", rquery); + + + /* Update view `vwgroups` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwGroups`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwGroups`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwGroups` AS\n" + "SELECT g.`groupid` AS groupid,\n" + "GetMobType(g.`name`) AS mobtype,\n" + "g.`name` AS name,\n" + "g.`charid` AS mobid,\n" + "IFNULL(c.`level`, b.`BotLevel`) AS level\n" + "FROM `group_id` AS g\n" + "LEFT JOIN `character_data` AS c ON g.`name` = c.`name`\n" + "LEFT JOIN `bots` AS b ON g.`name` = b.`Name`;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwGroups`", rquery); + + + /* Update view `vwbotgroups` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwBotGroups`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwBotGroups`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwBotGroups` AS\n" + "SELECT g.`BotGroupId`,\n" + "g.`BotGroupName`,\n" + "g.`BotGroupLeaderBotId`,\n" + "b.`Name` AS BotGroupLeaderName,\n" + "b.`BotOwnerCharacterId`,\n" + "c.`name` AS BotOwnerCharacterName\n" + "FROM `botgroup` AS g\n" + "JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID`\n" + "JOIN `character_data` AS c ON b.`BotOwnerCharacterID` = c.`id`\n" + "ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwBotGroups`", rquery); + + + /* Update view `vwguildmembers` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwGuildMembers`;"); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Drop View `vwGuildMembers`", rquery); + + rquery = StringFormat( + "CREATE VIEW `vwGuildMembers` AS\n" + "SELECT 'C' AS mobtype,\n" + "cm.`char_id`,\n" + "cm.`guild_id`,\n" + "cm.`rank`,\n" + "cm.`tribute_enable`,\n" + "cm.`total_tribute`,\n" + "cm.`last_tribute`,\n" + "cm.`banker`,\n" + "cm.`public_note`,\n" + "cm.`alt`\n" + "FROM `guild_members` AS cm\n" + "UNION ALL\n" + "SELECT 'B' AS mobtype,\n" + "bm.`char_id`,\n" + "bm.`guild_id`,\n" + "bm.`rank`,\n" + "bm.`tribute_enable`,\n" + "bm.`total_tribute`,\n" + "bm.`last_tribute`,\n" + "bm.`banker`,\n" + "bm.`public_note`,\n" + "bm.`alt`\n" + "FROM `botguildmembers` AS bm;" + ); + results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "Create View `vwGuildMembers`", rquery); + } + + if (runbotsconvert == 1){ + printf("\n\nBot views/function conversion complete, continuing world bootup...\n"); + } + +#endif + + return true; +} + bool Database::LoadVariables() { char *query = nullptr; @@ -1140,7 +2451,7 @@ bool Database::CheckNameFilter(const char* name, bool surname) else { // the minimum 4 is enforced by the client too - if(!name || strlen(name) < 4 || strlen(name) > 64) + if(!name || strlen(name) < 4 || strlen(name) > 15) { return false; } @@ -1221,7 +2532,7 @@ bool Database::AddToNameFilter(const char* name) { } uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* oStatus) { - + uint32 account_id = 0; std::string query = StringFormat("SELECT id, name, status FROM account WHERE lsaccount_id=%i", iLSID); auto results = QueryDatabase(query); @@ -1234,14 +2545,14 @@ uint32 Database::GetAccountIDFromLSID(uint32 iLSID, char* oAccountName, int16* o if (results.RowCount() != 1) return 0; - auto row = results.begin(); + for (auto row = results.begin(); row != results.end(); ++row) { + account_id = atoi(row[0]); - uint32 account_id = atoi(row[0]); - - if (oAccountName) - strcpy(oAccountName, row[1]); - if (oStatus) - *oStatus = atoi(row[2]); + if (oAccountName) + strcpy(oAccountName, row[1]); + if (oStatus) + *oStatus = atoi(row[2]); + } return account_id; } @@ -1277,11 +2588,9 @@ void Database::ClearMerchantTemp(){ std::cerr << "Error in ClearMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; } -bool Database::UpdateName(const char* oldname, const char* newname) { - - std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; - - std::string query = StringFormat("UPDATE character_ SET name='%s' WHERE name='%s';", newname, oldname); +bool Database::UpdateName(const char* oldname, const char* newname) { + std::cout << "Renaming " << oldname << " to " << newname << "..." << std::endl; + std::string query = StringFormat("UPDATE `character_data` SET `name` = '%s' WHERE `name` = '%s';", newname, oldname); auto results = QueryDatabase(query); if (!results.Success()) @@ -1294,13 +2603,10 @@ bool Database::UpdateName(const char* oldname, const char* newname) { } // If the name is used or an error occurs, it returns false, otherwise it returns true -bool Database::CheckUsedName(const char* name) -{ - std::string query = StringFormat("SELECT id FROM character_ where name='%s'", name); - auto results = QueryDatabase(query); - - if (!results.Success()) - { +bool Database::CheckUsedName(const char* name) { + std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name` = '%s'", name); + auto results = QueryDatabase(query); + if (!results.Success()) { std::cerr << "Error in CheckUsedName query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -1311,13 +2617,10 @@ bool Database::CheckUsedName(const char* name) return true; } -uint8 Database::GetServerType() -{ - std::string query("SELECT value FROM variables WHERE varname='ServerType'"); - auto results = QueryDatabase(query); - - if (!results.Success()) - { +uint8 Database::GetServerType() { + std::string query("SELECT `value` FROM `variables` WHERE `varname` = 'ServerType' LIMIT 1"); + auto results = QueryDatabase(query); + if (!results.Success()) { std::cerr << "Error in GetServerType query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -1329,15 +2632,14 @@ uint8 Database::GetServerType() return atoi(row[0]); } -bool Database::MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid) { +bool Database::MoveCharacterToZone(const char* charname, const char* zonename, uint32 zoneid) { if(zonename == nullptr || strlen(zonename) == 0) return false; - std::string query = StringFormat("UPDATE character_ SET zonename = '%s',zoneid=%i,x=-1, y=-1, z=-1 WHERE name='%s'", zonename,zoneid, charname); + std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `name` = '%s'", zoneid, charname); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in MoveCharacterToZone(name) query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -1352,13 +2654,11 @@ bool Database::MoveCharacterToZone(const char* charname, const char* zonename) { return MoveCharacterToZone(charname, zonename, GetZoneID(zonename)); } -bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { - - std::string query = StringFormat("UPDATE character_ SET zonename = '%s', zoneid=%i, x=-1, y=-1, z=-1 WHERE id=%i", iZonename, GetZoneID(iZonename), iCharID); +bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { + std::string query = StringFormat("UPDATE `character_data` SET `zone_id` = %i, `x` = -1, `y` = -1, `z` = -1 WHERE `id` = %i", iZonename, GetZoneID(iZonename), iCharID); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in MoveCharacterToZone(id) query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -1366,57 +2666,11 @@ bool Database::MoveCharacterToZone(uint32 iCharID, const char* iZonename) { return results.RowsAffected() != 0; } -uint8 Database::CopyCharacter(const char* oldname, const char* newname, uint32 acctid) { - - PlayerProfile_Struct* pp; - ExtendedProfile_Struct* ext; - - std::string query = StringFormat("SELECT profile, extprofile FROM character_ WHERE name='%s'", oldname); +bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { + std::string query = StringFormat("INSERT INTO `hackers` (account, name, hacked) values('%s','%s','%s')", accountname, charactername, hacked); auto results = QueryDatabase(query); - if (!results.Success()) - { - std::cerr << "Error in CopyCharacter read query '" << query << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - auto row = results.begin(); - - pp = (PlayerProfile_Struct*)row[0]; - strcpy(pp->name, newname); - - ext = (ExtendedProfile_Struct*)row[1]; - - char query2[276 + sizeof(PlayerProfile_Struct)*2 + sizeof(ExtendedProfile_Struct)*2 + 1]; - char* end=query2; - - end += sprintf(end, "INSERT INTO character_ SET zonename=\'%s\', x = %f, y = %f, z = %f, profile=\'", GetZoneName(pp->zone_id), pp->x, pp->y, pp->z); - end += DoEscapeString(end, (char*) pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end,"\', extprofile=\'"); - end += DoEscapeString(end, (char*) ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end, "\', account_id=%d, name='%s'", acctid, newname); - - results = QueryDatabase(query2, (uint32) (end - query2)); - - if (!results.Success()) - { - std::cerr << "Error in CopyCharacter query '" << query2 << "' " << results.ErrorMessage() << std::endl; - return 0; - } - - if (results.RowsAffected() == 0) - return 0; - - return 1; -} - -bool Database::SetHackerFlag(const char* accountname, const char* charactername, const char* hacked) { - - std::string query = StringFormat("INSERT INTO hackers(account,name,hacked) values('%s','%s','%s')", accountname, charactername, hacked); - auto results = QueryDatabase(query); - - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in SetHackerFlag query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } @@ -1424,8 +2678,7 @@ bool Database::SetHackerFlag(const char* accountname, const char* charactername, return results.RowsAffected() != 0; } -bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { - +bool Database::SetMQDetectionFlag(const char* accountname, const char* charactername, const char* hacked, const char* zone) { //Utilize the "hacker" table, but also give zone information. std::string query = StringFormat("INSERT INTO hackers(account,name,hacked,zone) values('%s','%s','%s','%s')", accountname, charactername, hacked, zone); auto results = QueryDatabase(query); @@ -1507,13 +2760,11 @@ uint8 Database::GetSkillCap(uint8 skillid, uint8 in_race, uint8 in_class, uint16 return base_cap; } -uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZoneID, uint32* oInstanceID, float* oX, float* oY, float* oZ) { - - std::string query = StringFormat("SELECT id, account_id, zonename, instanceid, x, y, z FROM character_ WHERE name='%s'", iName); +uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZoneID, uint32* oInstanceID, float* oX, float* oY, float* oZ) { + std::string query = StringFormat("SELECT `id`, `account_id`, `zone_id`, `zone_instance`, `x`, `y`, `z` FROM `character_data` WHERE `name` = '%s'", iName); auto results = QueryDatabase(query); - if (!results.Success()) - { + if (!results.Success()) { std::cerr << "Error in GetCharacterInfo query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } @@ -1522,20 +2773,13 @@ uint32 Database::GetCharacterInfo(const char* iName, uint32* oAccID, uint32* oZo return 0; auto row = results.begin(); - uint32 charid = atoi(row[0]); - if (oAccID) - *oAccID = atoi(row[1]); - if (oZoneID) - *oZoneID = GetZoneID(row[2]); - if(oInstanceID) - *oInstanceID = atoi(row[3]); - if (oX) - *oX = atof(row[4]); - if (oY) - *oY = atof(row[5]); - if (oZ) - *oZ = atof(row[6]); + if (oAccID){ *oAccID = atoi(row[1]); } + if (oZoneID){ *oZoneID = atoi(row[2]); } + if (oInstanceID){ *oInstanceID = atoi(row[3]); } + if (oX){ *oX = atof(row[4]); } + if (oY){ *oY = atof(row[5]); } + if (oZ){ *oZ = atof(row[6]); } return charid; } @@ -1574,45 +2818,35 @@ bool Database::GetLiveChar(uint32 account_id, char* cname) { return true; } -void Database::SetLFP(uint32 CharID, bool LFP) { - - std::string query = StringFormat("update character_ set lfp=%i where id=%i",LFP, CharID); - auto results = QueryDatabase(query); - +void Database::SetLFP(uint32 CharID, bool LFP) { + std::string query = StringFormat("UPDATE `character_data` SET `lfp` = %i WHERE `id` = %i",LFP, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { - - std::string query = StringFormat("update character_ set lfp=%i, lfg=%i, firstlogon=%i where id=%i",LFP, LFG, firstlogon, CharID); - auto results = QueryDatabase(query); - +void Database::SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon) { + std::string query = StringFormat("update `character_data` SET `lfp` = %i, `lfg` = %i, `firstlogon` = %i WHERE `id` = %i",LFP, LFG, firstlogon, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::SetLFG(uint32 CharID, bool LFG) { - - std::string query = StringFormat("update character_ set lfg=%i where id=%i",LFG, CharID); - auto results = QueryDatabase(query); - +void Database::SetLFG(uint32 CharID, bool LFG) { + std::string query = StringFormat("update `character_data` SET `lfg` = %i WHERE `id` = %i",LFG, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating LFP for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { - - std::string query = StringFormat( "update character_ set firstlogon=%i where id=%i",firstlogon, CharID); - auto results = QueryDatabase(query); - +void Database::SetFirstLogon(uint32 CharID, uint8 firstlogon) { + std::string query = StringFormat( "UPDATE `character_data` SET `firstlogon` = %i WHERE `id` = %i",firstlogon, CharID); + auto results = QueryDatabase(query); if (!results.Success()) LogFile->write(EQEMuLog::Error, "Error updating firstlogon for character %i : %s", CharID, results.ErrorMessage().c_str()); } -void Database::AddReport(std::string who, std::string against, std::string lines) -{ - +void Database::AddReport(std::string who, std::string against, std::string lines) { char *escape_str = new char[lines.size()*2+1]; DoEscapeString(escape_str, lines.c_str(), lines.size()); @@ -1624,11 +2858,9 @@ void Database::AddReport(std::string who, std::string against, std::string lines LogFile->write(EQEMuLog::Error, "Error adding a report for %s: %s", who.c_str(), results.ErrorMessage().c_str()); } -void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc){ - +void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ismerc) { std::string query; - if (id == 0) - { + if (id == 0) { // removing from group query = StringFormat("delete from group_id where charid=%i and name='%s' and ismerc=%i",charid, name, ismerc); auto results = QueryDatabase(query); @@ -1639,8 +2871,8 @@ void Database::SetGroupID(const char* name, uint32 id, uint32 charid, uint32 ism return; } - // adding to group - query = StringFormat("replace into group_id set charid=%i, groupid=%i, name='%s', ismerc='%i'",charid, id, name, ismerc); + /* Add to the Group */ + query = StringFormat("REPLACE INTO `group_id` SET `charid` = %i, `groupid` = %i, `name` = '%s', `ismerc` = '%i'", charid, id, name, ismerc); auto results = QueryDatabase(query); if (!results.Success()) @@ -1698,46 +2930,54 @@ uint32 Database::GetGroupID(const char* name){ return atoi(row[0]); } -char* Database::GetGroupLeaderForLogin(const char* name,char* leaderbuf){ - - PlayerProfile_Struct pp; +/* Is this really getting used properly... A half implementation ? Akkadius */ +char* Database::GetGroupLeaderForLogin(const char* name, char* leaderbuf) { + strcpy(leaderbuf, ""); + uint32 group_id = 0; - std::string query = StringFormat("SELECT profile from character_ where name='%s'", name); + std::string query = StringFormat("SELECT `groupid` FROM `group_id` WHERE `name = '%s'", name); auto results = QueryDatabase(query); - if (!results.Success()) - { - std::cout << "Unable to get leader name: " << results.ErrorMessage() << std::endl; - return leaderbuf; - } + for (auto row = results.begin(); row != results.end(); ++row) + if (row[0]) + group_id = atoi(row[0]); - if (results.LengthOfColumn(0) != sizeof(PlayerProfile_Struct)) + if (group_id == 0) return leaderbuf; - auto row = results.begin(); + query = StringFormat("SELECT `leadername` FROM `group_leader` WHERE `gid` = '%u' AND `groupid` = %u LIMIT 1", group_id); + results = QueryDatabase(query); - memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); - strcpy(leaderbuf,pp.groupMembers[0]); + for (auto row = results.begin(); row != results.end(); ++row) + if (row[0]) + strcpy(leaderbuf, row[0]); return leaderbuf; } -void Database::SetGroupLeaderName(uint32 gid, const char* name) { +void Database::SetGroupLeaderName(uint32 gid, const char* name) { + std::string query = StringFormat("UPDATE group_leaders SET leadername = '%s' WHERE gid = %u", EscapeString(name).c_str(), gid); + auto result = QueryDatabase(query); - std::string query = StringFormat("Replace into group_leaders set gid=%lu, leadername='%s'",(unsigned long)gid,name); - auto results = QueryDatabase(query); + if(result.RowsAffected() != 0) { + return; + } - if (!results.Success()) - std::cout << "Unable to set group leader: " << results.ErrorMessage() << std::endl; + query = StringFormat("INSERT INTO group_leaders(gid, leadername, marknpc, leadershipaa, maintank, assist, puller, mentoree, mentor_percent) VALUES(%u, '%s', '', '', '', '', '', '', '0')", + gid, EscapeString(name).c_str()); + result = QueryDatabase(query); + + if(!result.Success()) { + LogFile->write(EQEMuLog::Debug, "Error in Database::SetGroupLeaderName: %s", result.ErrorMessage().c_str()); + } } -char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, GroupLeadershipAA_Struct* GLAA){ - - std::string query = StringFormat("SELECT leadername, maintank, assist, puller, marknpc, leadershipaa FROM group_leaders WHERE gid=%lu",(unsigned long)gid); +char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank, char* assist, char* puller, char *marknpc, char *mentoree, int *mentor_percent, GroupLeadershipAA_Struct* GLAA) +{ + std::string query = StringFormat("SELECT `leadername`, `maintank`, `assist`, `puller`, `marknpc`, `mentoree`, `mentor_percent`, `leadershipaa` FROM `group_leaders` WHERE `gid` = %lu",(unsigned long)gid); auto results = QueryDatabase(query); - if (!results.Success() || results.RowCount() == 0) - { + if (!results.Success() || results.RowCount() == 0) { if(leaderbuf) strcpy(leaderbuf, "UNKNOWN"); @@ -1753,6 +2993,12 @@ char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* mainta if(marknpc) marknpc[0] = '\0'; + if (mentoree) + mentoree[0] = '\0'; + + if (mentor_percent) + *mentor_percent = 0; + return leaderbuf; } @@ -1773,15 +3019,20 @@ char *Database::GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* mainta if(marknpc) strcpy(marknpc, row[4]); - if(GLAA && results.LengthOfColumn(5) == sizeof(GroupLeadershipAA_Struct)) - memcpy(GLAA, row[5], sizeof(GroupLeadershipAA_Struct)); + if (mentoree) + strcpy(mentoree, row[5]); + + if (mentor_percent) + *mentor_percent = atoi(row[6]); + + if(GLAA && results.LengthOfColumn(7) == sizeof(GroupLeadershipAA_Struct)) + memcpy(GLAA, row[7], sizeof(GroupLeadershipAA_Struct)); return leaderbuf; } // Clearing all group leaders -void Database::ClearAllGroupLeaders(void) -{ +void Database::ClearAllGroupLeaders(void) { std::string query("DELETE from group_leaders"); auto results = QueryDatabase(query); @@ -1806,8 +3057,7 @@ void Database::ClearGroupLeader(uint32 gid) { std::cout << "Unable to clear group leader: " << results.ErrorMessage() << std::endl; } -uint8 Database::GetAgreementFlag(uint32 acctid) -{ +uint8 Database::GetAgreementFlag(uint32 acctid) { std::string query = StringFormat("SELECT rulesflag FROM account WHERE id=%i",acctid); auto results = QueryDatabase(query); @@ -1823,14 +3073,12 @@ uint8 Database::GetAgreementFlag(uint32 acctid) return atoi(row[0]); } -void Database::SetAgreementFlag(uint32 acctid) -{ +void Database::SetAgreementFlag(uint32 acctid) { std::string query = StringFormat("UPDATE account SET rulesflag=1 where id=%i", acctid); QueryDatabase(query); } void Database::ClearRaid(uint32 rid) { - if(rid == 0) { //clear all raids @@ -1846,8 +3094,7 @@ void Database::ClearRaid(uint32 rid) { std::cout << "Unable to clear raids: " << results.ErrorMessage() << std::endl; } -void Database::ClearAllRaids(void) -{ +void Database::ClearAllRaids(void) { std::string query("delete from raid_members"); auto results = QueryDatabase(query); @@ -1942,6 +3189,154 @@ const char* Database::GetRaidLeaderName(uint32 rid) return name; } +// maintank, assist, puller, marknpc currently unused +void Database::GetGroupLeadershipInfo(uint32 gid, uint32 rid, char *maintank, + char *assist, char *puller, char *marknpc, char *mentoree, int *mentor_percent, GroupLeadershipAA_Struct *GLAA) +{ + std::string query = StringFormat( + "SELECT maintank, assist, puller, marknpc, mentoree, mentor_percent, leadershipaa FROM raid_leaders WHERE gid = %lu AND rid = %lu", + (unsigned long)gid, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (!results.Success() || results.RowCount() == 0) { + if (maintank) + maintank[0] = '\0'; + + if (assist) + assist[0] = '\0'; + + if (puller) + puller[0] = '\0'; + + if (marknpc) + marknpc[0] = '\0'; + + if (mentoree) + mentoree[0] = '\0'; + + if (mentor_percent) + *mentor_percent = 0; + + return; + } + + auto row = results.begin(); + + if (maintank) + strcpy(maintank, row[0]); + + if (assist) + strcpy(assist, row[1]); + + if (puller) + strcpy(puller, row[2]); + + if (marknpc) + strcpy(marknpc, row[3]); + + if (mentoree) + strcpy(mentoree, row[4]); + + if (mentor_percent) + *mentor_percent = atoi(row[5]); + + if (GLAA && results.LengthOfColumn(6) == sizeof(GroupLeadershipAA_Struct)) + memcpy(GLAA, row[6], sizeof(GroupLeadershipAA_Struct)); + + return; +} + +// maintank, assist, puller, marknpc currently unused +void Database::GetRaidLeadershipInfo(uint32 rid, char *maintank, + char *assist, char *puller, char *marknpc, RaidLeadershipAA_Struct *RLAA) +{ + std::string query = StringFormat( + "SELECT maintank, assist, puller, marknpc, leadershipaa FROM raid_leaders WHERE gid = %lu AND rid = %lu", + (unsigned long)0xFFFFFFFF, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (!results.Success() || results.RowCount() == 0) { + if (maintank) + maintank[0] = '\0'; + + if (assist) + assist[0] = '\0'; + + if (puller) + puller[0] = '\0'; + + if (marknpc) + marknpc[0] = '\0'; + + return; + } + + auto row = results.begin(); + + if (maintank) + strcpy(maintank, row[0]); + + if (assist) + strcpy(assist, row[1]); + + if (puller) + strcpy(puller, row[2]); + + if (marknpc) + strcpy(marknpc, row[3]); + + if (RLAA && results.LengthOfColumn(4) == sizeof(RaidLeadershipAA_Struct)) + memcpy(RLAA, row[4], sizeof(RaidLeadershipAA_Struct)); + + return; +} + +void Database::SetRaidGroupLeaderInfo(uint32 gid, uint32 rid) +{ + std::string query = StringFormat("UPDATE raid_leaders SET leadershipaa = '', WHERE gid = %lu AND rid = %lu", + (unsigned long)gid, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (results.RowsAffected() != 0) + return; + + query = StringFormat("INSERT INTO raid_leaders(gid, rid, marknpc, leadershipaa, maintank, assist, puller, mentoree, mentor_percent) VALUES(%lu, %lu, '', '', '', '', '', '', 0)", + (unsigned long)gid, (unsigned long)rid); + results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to set raid/group leader: " << results.ErrorMessage() << std::endl; + + return; +} + +// Clearing all raid leaders +void Database::ClearAllRaidLeaders(void) +{ + std::string query("DELETE from raid_leaders"); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to clear raid leaders: " << results.ErrorMessage() << std::endl; + + return; +} + +void Database::ClearRaidLeader(uint32 gid, uint32 rid) +{ + if (rid == 0) { + ClearAllRaidLeaders(); + return; + } + + std::string query = StringFormat("DELETE from raid_leaders where gid = %lu and rid = %lu", + (unsigned long)gid, (unsigned long)rid); + auto results = QueryDatabase(query); + + if (!results.Success()) + std::cout << "Unable to clear raid leader: " << results.ErrorMessage() << std::endl; +} + bool Database::VerifyInstanceAlive(uint16 instance_id, uint32 char_id) { //we are not saved to this instance so set our instance to 0 @@ -2478,8 +3873,7 @@ void Database::UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win) QueryDatabase(query); } -bool Database::GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, uint32 &mmc_w, uint32 &ruj_w, - uint32 &tak_w, uint32 &guk_l, uint32 &mir_l, uint32 &mmc_l, uint32 &ruj_l, uint32 &tak_l) +bool Database::GetAdventureStats(uint32 char_id, AdventureStats_Struct *as) { std::string query = StringFormat("SELECT `guk_wins`, `mir_wins`, `mmc_wins`, `ruj_wins`, `tak_wins`, `guk_losses`, " @@ -2494,16 +3888,18 @@ bool Database::GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, u auto row = results.begin(); - guk_w = atoi(row[0]); - mir_w = atoi(row[1]); - mmc_w = atoi(row[2]); - ruj_w = atoi(row[3]); - tak_w = atoi(row[4]); - guk_l = atoi(row[5]); - mir_l = atoi(row[6]); - mmc_l = atoi(row[7]); - ruj_l = atoi(row[8]); - tak_l = atoi(row[9]); + as->success.guk = atoi(row[0]); + as->success.mir = atoi(row[1]); + as->success.mmc = atoi(row[2]); + as->success.ruj = atoi(row[3]); + as->success.tak = atoi(row[4]); + as->failure.guk = atoi(row[5]); + as->failure.mir = atoi(row[6]); + as->failure.mmc = atoi(row[7]); + as->failure.ruj = atoi(row[8]); + as->failure.tak = atoi(row[9]); + as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; + as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; return true; } diff --git a/common/database.h b/common/database.h index f912c18d1..9b388c84a 100644 --- a/common/database.h +++ b/common/database.h @@ -26,12 +26,6 @@ #include "dbcore.h" #include "linked_list.h" #include "eq_packet_structs.h" -/*#include "eq_stream.h" -#include "guilds.h" -#include "misc_functions.h" -#include "mutex.h" -#include "item.h" -#include "extprofile.h"*/ #include #include #include @@ -105,10 +99,16 @@ public: Database(const char* host, const char* user, const char* passwd, const char* database,uint32 port); bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); ~Database(); + bool ThrowDBError(std::string ErrorMessage, std::string query_title, std::string query); + /* * General Character Related Stuff */ + + /* Character Creation */ + bool SaveCharacterCreate(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp); + bool MoveCharacterToZone(const char* charname, const char* zonename); bool MoveCharacterToZone(const char* charname, const char* zonename,uint32 zoneid); bool MoveCharacterToZone(uint32 iCharID, const char* iZonename); @@ -118,9 +118,8 @@ public: bool AddToNameFilter(const char* name); bool ReserveName(uint32 account_id, char* name); bool CreateCharacter(uint32 account_id, char* name, uint16 gender, uint16 race, uint16 class_, uint8 str, uint8 sta, uint8 cha, uint8 dex, uint8 int_, uint8 agi, uint8 wis, uint8 face); - bool StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext); + bool StoreCharacter(uint32 account_id, PlayerProfile_Struct* pp, Inventory* inv); bool DeleteCharacter(char* name); - uint8 CopyCharacter(const char* oldname, const char* newname, uint32 acctid); /* * General Information Getting Queries @@ -175,8 +174,7 @@ public: * Adventure related. */ void UpdateAdventureStatsEntry(uint32 char_id, uint8 theme, bool win); - bool GetAdventureStats(uint32 char_id, uint32 &guk_w, uint32 &mir_w, uint32 &mmc_w, uint32 &ruj_w, uint32 &tak_w, - uint32 &guk_l, uint32 &mir_l, uint32 &mmc_l, uint32 &ruj_l, uint32 &tak_l); + bool GetAdventureStats(uint32 char_id, AdventureStats_Struct *as); /* * Account Related @@ -205,7 +203,7 @@ public: void SetGroupLeaderName(uint32 gid, const char* name); char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, - GroupLeadershipAA_Struct* GLAA = nullptr); + char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); void ClearGroupLeader(uint32 gid = 0); @@ -216,6 +214,14 @@ public: void ClearRaidDetails(uint32 rid = 0); uint32 GetRaidID(const char* name); const char *GetRaidLeaderName(uint32 rid); + void GetGroupLeadershipInfo(uint32 gid, uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, + char *mentoree = nullptr, int *mentor_percent = nullptr, GroupLeadershipAA_Struct* GLAA = nullptr); + void GetRaidLeadershipInfo(uint32 rid, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr, + RaidLeadershipAA_Struct* RLAA = nullptr); + void SetRaidGroupLeaderInfo(uint32 gid, uint32 rid); + void ClearRaidLeader(uint32 gid = 0xFFFFFFFF, uint32 rid = 0); + + bool CheckDatabaseConversions(); /* * Database Variables @@ -250,10 +256,6 @@ public: void SetLoginFlags(uint32 CharID, bool LFP, bool LFG, uint8 firstlogon); void AddReport(std::string who, std::string against, std::string lines); - -protected: - void HandleMysqlError(uint32 errnum); - private: void DBInitVars(); @@ -277,6 +279,7 @@ private: */ void ClearAllRaids(); void ClearAllRaidDetails(); + void ClearAllRaidLeaders(); }; #endif diff --git a/common/dbasync.cpp b/common/dbasync.cpp deleted file mode 100644 index 00714c2a0..000000000 --- a/common/dbasync.cpp +++ /dev/null @@ -1,669 +0,0 @@ -#include "debug.h" -#ifdef _WINDOWS - #include - #include - #include -#endif -#include -#include "dbasync.h" -#include "database.h" -#include -#include -#include -#include "dbcore.h" -#include -//#include "../common/misc_functions.h" -#include "string_util.h" -#define ASYNC_LOOP_GRANULARITY 4 //# of ms between checking our work - -bool DBAsyncCB_LoadVariables(DBAsyncWork* iWork) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result = 0; - DBAsyncQuery* dbaq = iWork->PopAnswer(); - if (dbaq->GetAnswer(errbuf, &result)) - iWork->GetDB()->LoadVariables_result(result); - else - std::cout << "Error: DBAsyncCB_LoadVariables failed: !GetAnswer: '" << errbuf << "'" << std::endl; - return true; -} - -void AsyncLoadVariables(DBAsync *dba, Database *db) { - char* query = 0; - DBAsyncWork* dbaw = new DBAsyncWork(db, &DBAsyncCB_LoadVariables, 0, DBAsync::Read); - dbaw->AddQuery(0, &query, db->LoadVariables_MQ(&query)); - dba->AddWork(&dbaw); -} - - -//we only need to do anything when somebody puts work on the queue -//so instead of checking all the time, we will wait on a condition -//which will get signaled when somebody puts something on the queue -ThreadReturnType DBAsyncLoop(void* tmp) { - DBAsync* dba = (DBAsync*) tmp; - -#ifndef WIN32 - _log(COMMON__THREADS, "Starting DBAsyncLoop with thread ID %d", pthread_self()); -#endif - - dba->MLoopRunning.lock(); - while (dba->RunLoop()) { - //wait before working so we check the loop condition - //as soon as were done working - dba->CInList.Wait(); - //we could check dba->RunLoop() again to see if we - //got turned off while we were waiting - { - dba->Process(); - } - } - dba->MLoopRunning.unlock(); - -#ifndef WIN32 - _log(COMMON__THREADS, "Ending DBAsyncLoop with thread ID %d", pthread_self()); -#endif - - THREAD_RETURN(nullptr); -} - -DBAsync::DBAsync(DBcore* iDBC) -: Timeoutable(10000) -{ - pDBC = iDBC; - pRunLoop = true; - pNextID = 1; -#ifdef _WINDOWS - _beginthread(DBAsyncLoop, 0, this); -#else - pthread_t thread; - pthread_create(&thread, nullptr, DBAsyncLoop, this); -#endif -} - -DBAsync::~DBAsync() { - StopThread(); -} - -bool DBAsync::StopThread() { - bool ret; - MRunLoop.lock(); - ret = pRunLoop; - pRunLoop = false; - MRunLoop.unlock(); - - //signal the condition so we exit the loop if were waiting - CInList.Signal(); - - //this effectively waits for the processing thread to finish - MLoopRunning.lock(); - MLoopRunning.unlock(); - - return ret; -} - -uint32 DBAsync::AddWork(DBAsyncWork** iWork, uint32 iDelay) { - MInList.lock(); - uint32 ret = GetNextID(); - if (!(*iWork)->SetWorkID(ret)) { - MInList.unlock(); - return 0; - } - InList.Append(*iWork); - (*iWork)->SetStatus(Queued); - if (iDelay) - (*iWork)->pExecuteAfter = Timer::GetCurrentTime() + iDelay; -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "Adding AsyncWork #" << (*iWork)->GetWorkID() << std::endl; - std::cout << "ExecuteAfter = " << (*iWork)->pExecuteAfter << " (" << Timer::GetCurrentTime() << " + " << iDelay << ")" << std::endl; -#endif - *iWork = 0; - MInList.unlock(); - - //wake up the processing thread and tell it to get to work. - CInList.Signal(); - - return ret; -} - -bool DBAsync::CancelWork(uint32 iWorkID) { - if (iWorkID == 0) - return false; -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "DBAsync::CancelWork: " << iWorkID << std::endl; -#endif - MCurrentWork.lock(); - if (CurrentWork && CurrentWork->GetWorkID() == iWorkID) { - CurrentWork->Cancel(); - MCurrentWork.unlock(); - return true; - } - MCurrentWork.unlock(); - MInList.lock(); - LinkedListIterator iterator(InList); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->GetWorkID() == iWorkID) { - iterator.RemoveCurrent(true); - MInList.unlock(); - return true; - } - iterator.Advance(); - } - MInList.unlock(); - return false; -} - -bool DBAsync::RunLoop() { - bool ret; - MRunLoop.lock(); - ret = pRunLoop; - MRunLoop.unlock(); - return ret; -} - -DBAsyncWork* DBAsync::InListPop() { - DBAsyncWork* ret = 0; - MInList.lock(); - LinkedListIterator iterator(InList); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->pExecuteAfter <= Timer::GetCurrentTime()) { - ret = iterator.GetData(); -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "Poping AsyncWork #" << ret->GetWorkID() << std::endl; - std::cout << ret->pExecuteAfter << " <= " << Timer::GetCurrentTime() << std::endl; -#endif - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MInList.unlock(); - return ret; -} - -DBAsyncWork* DBAsync::InListPopWrite() { - MInList.lock(); - LinkedListIterator iterator(InList); - - DBAsyncWork* ret = 0; - DBAsync::Type tmpType; - iterator.Reset(); - while (iterator.MoreElements()) { - tmpType = iterator.GetData()->Type(); - if (tmpType == Write || tmpType == Both) { - ret = iterator.GetData(); - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MInList.unlock(); - return ret; -} - -void DBAsync::AddFQ(DBAsyncFinishedQueue* iDBAFQ) { - MFQList.lock(); - DBAsyncFinishedQueue** tmp = new DBAsyncFinishedQueue*; - *tmp = iDBAFQ; - FQList.Append(tmp); - MFQList.unlock(); -} - -void DBAsync::Process() { - DBAsyncWork* tmpWork; - MCurrentWork.lock(); - while ((CurrentWork = InListPop())) { - MCurrentWork.unlock(); - //move from queued to executing - Status tmpStatus = CurrentWork->SetStatus(Executing); - if (tmpStatus == Queued) { - //execute the work - ProcessWork(CurrentWork); - tmpWork = CurrentWork; - MCurrentWork.lock(); - CurrentWork = 0; - MCurrentWork.unlock(); - //move from executing to finished - tmpStatus = tmpWork->SetStatus(DBAsync::Finished); - if (tmpStatus != Executing) { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::Process #1" << std::endl; - } - MCurrentWork.lock(); - safe_delete(tmpWork); - } - else { - //call callbacks or put results on finished queue - DispatchWork(tmpWork); - Sleep(25); - MCurrentWork.lock(); - } - } - else { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::Process #2" << std::endl; - } - MCurrentWork.lock(); - safe_delete(CurrentWork); - } - } - MCurrentWork.unlock(); -} - -void DBAsync::CheckTimeout() { - try{ - MFQList.lock(); - LinkedListIterator iterator(FQList); - - iterator.Reset(); - while (iterator.MoreElements()) { - (*iterator.GetData())->CheckTimeouts(); - iterator.Advance(); - } - MFQList.unlock(); - } - catch(...){ - - } -} - -void DBAsync::CommitWrites() { -#if DEBUG_MYSQL_QUERIES >= 2 - std::cout << "DBAsync::CommitWrites() called." << std::endl; -#endif - DBAsyncWork* tmpWork; - while ((tmpWork = InListPopWrite())) { - Status tmpStatus = tmpWork->SetStatus(Executing); - if (tmpStatus == Queued) { - ProcessWork(tmpWork); - tmpStatus = tmpWork->SetStatus(DBAsync::Finished); - if (tmpStatus != Executing) { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::CommitWrites #1" << std::endl; - } - safe_delete(tmpWork); - } - else { - DispatchWork(tmpWork); - } - } - else { - if (tmpStatus != Canceled) { - std::cout << "Error: Unexpected DBAsyncWork->Status in DBAsync::CommitWrites #2" << std::endl; - } - safe_delete(tmpWork); - } - } -} - -void DBAsync::ProcessWork(DBAsyncWork* iWork, bool iSleep) { - DBAsyncQuery* CurrentQuery; - while ((CurrentQuery = iWork->PopQuery())) { - CurrentQuery->Process(pDBC); - iWork->PushAnswer(CurrentQuery); - if (iSleep) - Sleep(1); - } -} - -void DBAsync::DispatchWork(DBAsyncWork* iWork) { - //if this work has a callback, call it - //otherwise, stick the work on the finish queue - if (iWork->pCB) { - if (iWork->pCB(iWork)) - safe_delete(iWork); - } - else { - if (!iWork->pDBAFQ->Push(iWork)) - safe_delete(iWork); - } -} - - - -DBAsyncFinishedQueue::DBAsyncFinishedQueue(uint32 iTimeout) { - pTimeout = iTimeout; -} - -DBAsyncFinishedQueue::~DBAsyncFinishedQueue() { -} - -void DBAsyncFinishedQueue::CheckTimeouts() { - if (pTimeout == 0xFFFFFFFF) - return; - MLock.lock(); - LinkedListIterator iterator(list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->CheckTimeout(pTimeout)) - iterator.RemoveCurrent(true); - iterator.Advance(); - } - MLock.unlock(); -} - -DBAsyncWork* DBAsyncFinishedQueue::Pop() { - DBAsyncWork* ret = 0; - MLock.lock(); - ret = list.Pop(); - MLock.unlock(); - return ret; -} - -DBAsyncWork* DBAsyncFinishedQueue::Find(uint32 iWorkID) { - DBAsyncWork* ret = 0; - MLock.lock(); - LinkedListIterator iterator(list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->GetWorkID() == iWorkID) { - ret = iterator.GetData(); - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MLock.unlock(); - return ret; -} - -DBAsyncWork* DBAsyncFinishedQueue::PopByWPT(uint32 iWPT) { - DBAsyncWork* ret = 0; - MLock.lock(); - LinkedListIterator iterator(list); - - iterator.Reset(); - while (iterator.MoreElements()) { - if (iterator.GetData()->WPT() == iWPT) { - ret = iterator.GetData(); - iterator.RemoveCurrent(false); - break; - } - iterator.Advance(); - } - MLock.unlock(); - return ret; -} - -bool DBAsyncFinishedQueue::Push(DBAsyncWork* iDBAW) { - if (!this) - return false; - MLock.lock(); - list.Append(iDBAW); - MLock.unlock(); - return true; -} - - - -DBAsyncWork::DBAsyncWork(Database *db, DBAsyncFinishedQueue* iDBAFQ, uint32 iWPT, DBAsync::Type iType, uint32 iTimeout) -: m_db(db) -{ - pstatus = DBAsync::AddingWork; - pType = iType; - pExecuteAfter = 0; - pWorkID = 0; - pDBAFQ = iDBAFQ; - pCB = 0; - pWPT = iWPT; - pQuestionCount = 0; - pAnswerCount = 0; - pTimeout = iTimeout; - pTSFinish = 0; -} - -DBAsyncWork::DBAsyncWork(Database *db, DBWorkCompleteCallBack iCB, uint32 iWPT, DBAsync::Type iType, uint32 iTimeout) -: m_db(db) -{ - pstatus = DBAsync::AddingWork; - pType = iType; - pExecuteAfter = 0; - pWorkID = 0; - pDBAFQ = 0; - pCB = iCB; - pWPT = iWPT; - pQuestionCount = 0; - pAnswerCount = 0; - pTimeout = iTimeout; - pTSFinish = 0; -} - -DBAsyncWork::~DBAsyncWork() { - DBAsyncQuery* dbaq = 0; - while ((dbaq = todo.pop())) - safe_delete(dbaq); - while ((dbaq = done.pop())) - safe_delete(dbaq); - while ((dbaq = todel.pop())) - safe_delete(dbaq); -} - -bool DBAsyncWork::AddQuery(DBAsyncQuery** iDBAQ) { - bool ret; - MLock.lock(); - if (pstatus != DBAsync::AddingWork) - ret = false; - else { - ret = true; - pQuestionCount++; - todo.push(*iDBAQ); - (*iDBAQ)->pstatus = DBAsync::Queued; - *iDBAQ = 0; - } - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::AddQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen, bool iGetResultSet, bool iGetErrbuf) { - DBAsyncQuery* DBAQ = new DBAsyncQuery(iQPT, iQuery, iQueryLen, iGetResultSet, iGetErrbuf); - if (AddQuery(&DBAQ)) - return true; - else { - safe_delete(DBAQ); - return false; - } -} - -bool DBAsyncWork::SetWorkID(uint32 iWorkID) { - bool ret = true; - MLock.lock(); - if (pWorkID) - ret = false; - else - pWorkID = iWorkID; - MLock.unlock(); - return ret; -} - -uint32 DBAsyncWork::GetWorkID() { - uint32 ret; - MLock.lock(); - ret = pWorkID; - MLock.unlock(); - return ret; -} - -uint32 DBAsyncWork::WPT() { - uint32 ret; - MLock.lock(); - ret = pWPT; - MLock.unlock(); - return ret; -} - -DBAsync::Type DBAsyncWork::Type() { - DBAsync::Type ret; - MLock.lock(); - ret = pType; - MLock.unlock(); - return ret; -} - -DBAsyncQuery* DBAsyncWork::PopAnswer() { - DBAsyncQuery* ret; - MLock.lock(); - ret = done.pop(); - if (ret) - pAnswerCount--; - todel.push(ret); - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::CheckTimeout(uint32 iFQTimeout) { - if (pTimeout == 0xFFFFFFFF) - return false; - bool ret = false; - MLock.lock(); - if (pTimeout > iFQTimeout) - iFQTimeout = pTimeout; - if (Timer::GetCurrentTime() > (pTSFinish + iFQTimeout)) - ret = true; - MLock.unlock(); - return ret; -} - -//sets the work's status to the supplied value and returns -//the revious status -DBAsync::Status DBAsyncWork::SetStatus(DBAsync::Status iStatus) { - DBAsync::Status ret; - MLock.lock(); - if (iStatus == DBAsync::Finished) - pTSFinish = Timer::GetCurrentTime(); - ret = pstatus; - pstatus = iStatus; - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::Cancel() { - bool ret; - MLock.lock(); - if (pstatus != DBAsync::Finished) { - pstatus = DBAsync::Canceled; - ret = true; - } - else - ret = false; - MLock.unlock(); - return ret; -} - -bool DBAsyncWork::IsCancled() { - bool ret; - MLock.lock(); - ret = (bool) (pstatus == DBAsync::Canceled); - MLock.unlock(); - return ret; -} - -DBAsyncQuery* DBAsyncWork::PopQuery() { - DBAsyncQuery* ret = 0; - MLock.lock(); - ret = todo.pop(); - if (ret) - pQuestionCount--; - MLock.unlock(); - return ret; -} - -void DBAsyncWork::PushAnswer(DBAsyncQuery* iDBAQ) { - MLock.lock(); - done.push(iDBAQ); - pAnswerCount++; - MLock.unlock(); -} - - -DBAsyncQuery::DBAsyncQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen, bool iGetResultSet, bool iGetErrbuf) { - if (iQueryLen == 0xFFFFFFFF) - pQueryLen = strlen(*iQuery); - else - pQueryLen = iQueryLen; - pQuery = *iQuery; - *iQuery = 0; - Init(iQPT, iGetResultSet, iGetErrbuf); -} - -DBAsyncQuery::DBAsyncQuery(uint32 iQPT, const char* iQuery, uint32 iQueryLen, bool iGetResultSet, bool iGetErrbuf) { - if (iQueryLen == 0xFFFFFFFF) - pQueryLen = strlen(iQuery); - else - pQueryLen = iQueryLen; - pQuery = strn0cpy(new char[pQueryLen+1], iQuery, pQueryLen+1); - Init(iQPT, iGetResultSet, iGetErrbuf); -} - -void DBAsyncQuery::Init(uint32 iQPT, bool iGetResultSet, bool iGetErrbuf) { - pstatus = DBAsync::AddingWork; - pQPT = iQPT; - pGetResultSet = iGetResultSet; - pGetErrbuf = iGetErrbuf; - - pmysqlsuccess = false; - perrbuf = 0; - perrnum = 0; - presult = 0; - paffected_rows = 0; - plast_insert_id = 0; -} - -DBAsyncQuery::~DBAsyncQuery() { - safe_delete_array(perrbuf); - safe_delete_array(pQuery); - if (presult) - mysql_free_result(presult); -} - -bool DBAsyncQuery::GetAnswer(char* errbuf, MYSQL_RES** result, uint32* affected_rows, uint32* last_insert_id, uint32* errnum) { - if (pstatus != DBAsync::Finished) { - if (errbuf) - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "Error: Query not finished."); - if (errnum) - *errnum = UINT_MAX; - return false; - } - if (errbuf) { - if (pGetErrbuf) { - if (perrbuf) - strn0cpy(errbuf, perrbuf, MYSQL_ERRMSG_SIZE); - else - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "Error message should've been saved, but hasnt. errno: %u", perrnum); - } - else - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "Error message not saved. errno: %u", perrnum); - } - if (errnum) - *errnum = perrnum; - if (affected_rows) - *affected_rows = paffected_rows; - if (last_insert_id) - *last_insert_id = plast_insert_id; - if (result) - *result = presult; - return pmysqlsuccess; -} - -void DBAsyncQuery::Process(DBcore* iDBC) { - pstatus = DBAsync::Executing; - if (pGetErrbuf) - perrbuf = new char[MYSQL_ERRMSG_SIZE]; - MYSQL_RES** resultPP = 0; - if (pGetResultSet) - resultPP = &presult; - pmysqlsuccess = iDBC->RunQuery(pQuery, pQueryLen, perrbuf, resultPP, &paffected_rows, &plast_insert_id, &perrnum); - pstatus = DBAsync::Finished; -} - - - - - - - - - diff --git a/common/dbasync.h b/common/dbasync.h deleted file mode 100644 index f65f9cb58..000000000 --- a/common/dbasync.h +++ /dev/null @@ -1,176 +0,0 @@ -#ifndef DBASYNC_H -#define DBASYNC_H -#include "../common/dbcore.h" -#include "../common/timeoutmgr.h" - - -class DBAsyncFinishedQueue; -class DBAsyncWork; -class DBAsyncQuery; -class Database; - -// Big daddy that owns the threads and does the work -class DBAsync : private Timeoutable { -public: - enum Status { AddingWork, Queued, Executing, Finished, Canceled }; - enum Type { Read, Write, Both }; - - DBAsync(DBcore* iDBC); - ~DBAsync(); - bool StopThread(); - - uint32 AddWork(DBAsyncWork** iWork, uint32 iDelay = 0); - bool CancelWork(uint32 iWorkID); - void CommitWrites(); - - void AddFQ(DBAsyncFinishedQueue* iDBAFQ); -protected: - //things related to the processing thread: - friend ThreadReturnType DBAsyncLoop(void* tmp); - Mutex MLoopRunning; - Condition CInList; - bool RunLoop(); - void Process(); - -private: - virtual void CheckTimeout(); - - void ProcessWork(DBAsyncWork* iWork, bool iSleep = true); - void DispatchWork(DBAsyncWork* iWork); - inline uint32 GetNextID() { return pNextID++; } - DBAsyncWork* InListPop(); - DBAsyncWork* InListPopWrite(); // Ignores delay - void OutListPush(DBAsyncWork* iDBAW); - - Mutex MRunLoop; - bool pRunLoop; - - DBcore* pDBC; - uint32 pNextID; - Mutex MInList; - LinkedList InList; - - Mutex MFQList; - LinkedList FQList; - - // Mutex for outside access to current work & when current work is being changed. - // NOT locked when CurrentWork is being accessed by the DBAsync thread. - // Never change pointer from outside DBAsync thread! - // Only here for access to thread-safe DBAsyncWork functions. - Mutex MCurrentWork; - DBAsyncWork* CurrentWork; - -}; - -/* - DB Work Complete Callback: - This will be called under the DBAsync thread! Never access any non-threadsafe - data/functions/classes. (ie: zone, entitylist, client, etc are not threadsafe) - Function prototype: - return value: true if we should delete the data, false if we should keep it -*/ -typedef bool(*DBWorkCompleteCallBack)(DBAsyncWork*); - -class DBAsyncFinishedQueue { -public: - DBAsyncFinishedQueue(uint32 iTimeout = 90000); - ~DBAsyncFinishedQueue(); - - DBAsyncWork* Pop(); - DBAsyncWork* PopByWPT(uint32 iWPT); - DBAsyncWork* Find(uint32 iWPT); - bool Push(DBAsyncWork* iDBAW); - - void CheckTimeouts(); -private: - Mutex MLock; - uint32 pTimeout; - LinkedList list; -}; - -// Container class for multiple queries -class DBAsyncWork { -public: - DBAsyncWork(Database *db, DBAsyncFinishedQueue* iDBAFQ, uint32 iWPT = 0, DBAsync::Type iType = DBAsync::Both, uint32 iTimeout = 0); - DBAsyncWork(Database *db, DBWorkCompleteCallBack iCB, uint32 iWPT = 0, DBAsync::Type iType = DBAsync::Both, uint32 iTimeout = 0); - ~DBAsyncWork(); - - bool AddQuery(DBAsyncQuery** iDBAQ); - bool AddQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen = 0xFFFFFFFF, bool iGetResultSet = true, bool iGetErrbuf = true); - uint32 WPT(); - DBAsync::Type Type(); - - // Pops finished queries off the work - DBAsyncQuery* PopAnswer(); - uint32 QueryCount(); - - Database *GetDB() const { return(m_db); } - - bool CheckTimeout(uint32 iFQTimeout); - bool SetWorkID(uint32 iWorkID); - uint32 GetWorkID(); -protected: - friend class DBAsync; - DBAsync::Status SetStatus(DBAsync::Status iStatus); - bool Cancel(); - bool IsCancled(); - DBAsyncQuery* PopQuery(); // Get query to be run - void PushAnswer(DBAsyncQuery* iDBAQ); // Push answer back into workset - - // not mutex'd cause only to be accessed from dbasync class - uint32 pExecuteAfter; -private: - Mutex MLock; - uint32 pQuestionCount; - uint32 pAnswerCount; - uint32 pWorkID; - uint32 pWPT; - uint32 pTimeout; - uint32 pTSFinish; // timestamp when finished - DBAsyncFinishedQueue* pDBAFQ; //we do now own this pointer - DBWorkCompleteCallBack pCB; - DBAsync::Status pstatus; - DBAsync::Type pType; - MyQueue todo; - MyQueue done; - MyQueue todel; - Database *const m_db; //we do now own this pointer -}; - -// Container class for the query information -class DBAsyncQuery { -public: - DBAsyncQuery(uint32 iQPT, char** iQuery, uint32 iQueryLen = 0xFFFFFFFF, bool iGetResultSet = true, bool iGetErrbuf = true); - DBAsyncQuery(uint32 iQPT, const char* iQuery, uint32 iQueryLen = 0xFFFFFFFF, bool iGetResultSet = true, bool iGetErrbuf = true); - ~DBAsyncQuery(); - - bool GetAnswer(char* errbuf = 0, MYSQL_RES** result = 0, uint32* affected_rows = 0, uint32* last_insert_id = 0, uint32* errnum = 0); - inline uint32 QPT() { return pQPT; } -protected: - friend class DBAsyncWork; - uint32 pQPT; - - friend class DBAsync; - void Process(DBcore* iDBC); - - void Init(uint32 iQPT, bool iGetResultSet, bool iGetErrbuf); - DBAsync::Status pstatus; - char* pQuery; - uint32 pQueryLen; - bool pGetResultSet; - bool pGetErrbuf; - - bool pmysqlsuccess; - char* perrbuf; - uint32 perrnum; - uint32 paffected_rows; - uint32 plast_insert_id; - MYSQL_RES* presult; -}; - - -void AsyncLoadVariables(DBAsync *dba, Database *db); - - -#endif - diff --git a/common/debug.h b/common/debug.h index 6184e4cbc..5e2fff368 100644 --- a/common/debug.h +++ b/common/debug.h @@ -80,14 +80,15 @@ public: ~EQEMuLog(); enum LogIDs { - Status = 0, //this must stay the first entry in this list - Normal, - Error, - Debug, - Quest, - Commands, - Crash, - MaxLogID + Status = 0, /* This must stay the first entry in this list */ + Normal, /* Normal Logs */ + Error, /* Error Logs */ + Debug, /* Debug Logs */ + Quest, /* Quest Logs */ + Commands, /* Issued Comamnds */ + Crash, /* Crash Logs */ + Save, /* Client Saves */ + MaxLogID /* Max, used in functions to get the max log ID */ }; //these are callbacks called for each @@ -113,6 +114,7 @@ private: Mutex MOpen; Mutex MLog[MaxLogID]; FILE* fp[MaxLogID]; + /* LogStatus: bitwise variable 1 = output to file 2 = output to stdout diff --git a/common/emu_oplist.h b/common/emu_oplist.h index c1ceb3c80..e8ce3b986 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -1,543 +1,548 @@ +// system use N(OP_ExploreUnknown), -N(OP_Heartbeat), -N(OP_ReloadUI), -N(OP_IncreaseStats), -N(OP_ApproveZone), -N(OP_Dye), -N(OP_Stamina), -N(OP_ControlBoat), -N(OP_MobUpdate), //not used anymore, here for lecacy reasons (eqextractor) -N(OP_ClientUpdate), -N(OP_ChannelMessage), -N(OP_SimpleMessage), -N(OP_FormattedMessage), -N(OP_TGB), -N(OP_Bind_Wound), -N(OP_Charm), -N(OP_Begging), -N(OP_MoveCoin), -N(OP_SpawnDoor), -N(OP_Sneak), -N(OP_ExpUpdate), -N(OP_DumpName), -N(OP_RespondAA), -N(OP_UpdateAA), -N(OP_SendAAStats), -N(OP_SendAATable), +// start (please add new opcodes in descending order and re-order any name changes where applicable) +N(OP_0x0193), +N(OP_0x0347), N(OP_AAAction), -N(OP_BoardBoat), -N(OP_LeaveBoat), -N(OP_AdventureInfoRequest), -N(OP_AdventureInfo), -N(OP_AdventureRequest), -N(OP_AdventureDetails), -N(OP_LDoNButton), +N(OP_AAExpUpdate), +N(OP_AcceptNewTask), +N(OP_AckPacket), +N(OP_Action), +N(OP_Action2), N(OP_AdventureData), +N(OP_AdventureDetails), N(OP_AdventureFinish), -N(OP_LeaveAdventure), +N(OP_AdventureInfo), +N(OP_AdventureInfoRequest), +N(OP_AdventureLeaderboardReply), +N(OP_AdventureLeaderboardRequest), +N(OP_AdventureMerchantPurchase), +N(OP_AdventureMerchantRequest), +N(OP_AdventureMerchantResponse), +N(OP_AdventureMerchantSell), +N(OP_AdventurePointsUpdate), +N(OP_AdventureRequest), +N(OP_AdventureStatsReply), +N(OP_AdventureStatsRequest), N(OP_AdventureUpdate), -N(OP_SendExpZonein), -N(OP_RaidUpdate), -N(OP_GuildLeader), -N(OP_GuildPeace), -N(OP_GuildRemove), -N(OP_GuildMemberList), -N(OP_GuildMemberUpdate), -N(OP_GuildMemberLevelUpdate), -N(OP_GuildInvite), -N(OP_GuildMOTD), -N(OP_SetGuildMOTD), -N(OP_GuildPublicNote), -N(OP_GetGuildsList), -N(OP_GuildDemote), -N(OP_GuildInviteAccept), -N(OP_GuildWar), -N(OP_GuildDelete), -N(OP_GuildManageRemove), -N(OP_GuildManageAdd), -N(OP_GuildManageStatus), -N(OP_GuildManageBanker), -N(OP_GetGuildMOTD), -N(OP_Trader), -N(OP_Bazaar), -N(OP_BecomeTrader), -N(OP_TraderItemUpdate), -N(OP_TraderShop), -N(OP_TraderBuy), -N(OP_PetCommands), -N(OP_TradeSkillCombine), +N(OP_AltCurrency), +N(OP_AltCurrencyMerchantReply), +N(OP_AltCurrencyMerchantRequest), +N(OP_AltCurrencyPurchase), +N(OP_AltCurrencyReclaim), +N(OP_AltCurrencySell), +N(OP_AltCurrencySellSelection), +N(OP_Animation), +N(OP_AnnoyingZoneUnknown), +N(OP_ApplyPoison), +N(OP_ApproveName), +N(OP_ApproveWorld), +N(OP_ApproveZone), +N(OP_Assist), +N(OP_AssistGroup), +N(OP_AugmentInfo), N(OP_AugmentItem), -N(OP_ItemName), -N(OP_ShopItem), -N(OP_ShopPlayerBuy), -N(OP_ShopPlayerSell), -N(OP_ShopDelItem), -N(OP_ShopRequest), -N(OP_ShopEnd), -N(OP_LFGCommand), -N(OP_LFGAppearance), -N(OP_GroupUpdate), -N(OP_GroupInvite), +N(OP_AutoAttack), +N(OP_AutoAttack2), +N(OP_AutoFire), +N(OP_Bandolier), +N(OP_BankerChange), +N(OP_Barter), +N(OP_Bazaar), +N(OP_BazaarInspect), +N(OP_BazaarSearch), +N(OP_BecomeCorpse), +N(OP_BecomeTrader), +N(OP_Begging), +N(OP_BeginCast), +N(OP_Bind_Wound), +N(OP_BlockedBuffs), +N(OP_BoardBoat), +N(OP_Buff), +N(OP_BuffCreate), +N(OP_BuffRemoveRequest), +N(OP_Bug), +N(OP_CameraEffect), +N(OP_Camp), +N(OP_CancelTask), +N(OP_CancelTrade), +N(OP_CastSpell), +N(OP_ChangeSize), +N(OP_ChannelMessage), +N(OP_CharacterCreate), +N(OP_CharacterCreateRequest), +N(OP_CharInventory), +N(OP_Charm), +N(OP_ChatMessage), +N(OP_ClearBlockedBuffs), +N(OP_ClearNPCMarks), +N(OP_ClearObject), +N(OP_ClearSurname), +N(OP_ClickDoor), +N(OP_ClickObject), +N(OP_ClickObjectAction), +N(OP_ClientError), +N(OP_ClientReady), +N(OP_ClientTimeStamp), +N(OP_ClientUpdate), +N(OP_CloseContainer), +N(OP_CloseTributeMaster), +N(OP_ColoredText), +N(OP_CombatAbility), +N(OP_Command), +N(OP_CompletedTasks), +N(OP_ConfirmDelete), +N(OP_Consent), +N(OP_ConsentDeny), +N(OP_ConsentResponse), +N(OP_Consider), +N(OP_ConsiderCorpse), +N(OP_Consume), +N(OP_ControlBoat), +N(OP_CorpseDrag), +N(OP_CorpseDrop), +N(OP_CrashDump), +N(OP_CrystalCountUpdate), +N(OP_CrystalCreate), +N(OP_CrystalReclaim), +N(OP_CustomTitles), +N(OP_Damage), +N(OP_Death), +N(OP_DelegateAbility), +N(OP_DeleteCharacter), +N(OP_DeleteCharge), +N(OP_DeleteItem), +N(OP_DeletePetition), +N(OP_DeleteSpawn), +N(OP_DeleteSpell), +N(OP_DenyResponse), +N(OP_Disarm), +N(OP_DisarmTraps), +N(OP_DisciplineTimer), +N(OP_DisciplineUpdate), +N(OP_DiscordMerchantInventory), +N(OP_DoGroupLeadershipAbility), +N(OP_DuelResponse), +N(OP_DuelResponse2), +N(OP_DumpName), +N(OP_Dye), +N(OP_DynamicWall), +N(OP_DzAddPlayer), +N(OP_DzChooseZone), +N(OP_DzCompass), +N(OP_DzExpeditionEndsWarning), +N(OP_DzExpeditionInfo), +N(OP_DzExpeditionList), +N(OP_DzJoinExpeditionConfirm), +N(OP_DzJoinExpeditionReply), +N(OP_DzLeaderStatus), +N(OP_DzListTimers), +N(OP_DzMakeLeader), +N(OP_DzMemberList), +N(OP_DzMemberStatus), +N(OP_DzPlayerList), +N(OP_DzQuit), +N(OP_DzRemovePlayer), +N(OP_DzSwapPlayer), +N(OP_Emote), +N(OP_EndLootRequest), +N(OP_EnduranceUpdate), +N(OP_EnterChat), +N(OP_EnterWorld), +N(OP_EnvDamage), +N(OP_ExpansionInfo), +N(OP_ExpUpdate), +N(OP_FaceChange), +N(OP_Feedback), +N(OP_FeignDeath), +N(OP_FellowshipUpdate), +N(OP_FindPersonReply), +N(OP_FindPersonRequest), +N(OP_FinishTrade), +N(OP_FinishWindow), +N(OP_FinishWindow2), +N(OP_Fishing), +N(OP_FloatListThing), +N(OP_Forage), +N(OP_ForceFindPerson), +N(OP_FormattedMessage), +N(OP_FriendsWho), +N(OP_GetGuildMOTD), +N(OP_GetGuildMOTDReply), +N(OP_GetGuildsList), +N(OP_GiveMoney), +N(OP_GMApproval), +N(OP_GMBecomeNPC), +N(OP_GMDelCorpse), +N(OP_GMEmoteZone), +N(OP_GMEndTraining), +N(OP_GMEndTrainingResponse), +N(OP_GMFind), +N(OP_GMGoto), +N(OP_GMHideMe), +N(OP_GMKick), +N(OP_GMKill), +N(OP_GMLastName), +N(OP_GMNameChange), +N(OP_GMSearchCorpse), +N(OP_GMServers), +N(OP_GMSummon), +N(OP_GMToggle), +N(OP_GMTraining), +N(OP_GMTrainSkill), +N(OP_GMTrainSkillConfirm), +N(OP_GMZoneRequest), +N(OP_GMZoneRequest2), +N(OP_GroundSpawn), +N(OP_GroupAcknowledge), +N(OP_GroupCancelInvite), +N(OP_GroupDelete), N(OP_GroupDisband), -N(OP_GroupInvite2), +N(OP_GroupDisbandOther), +N(OP_GroupDisbandYou), N(OP_GroupFollow), N(OP_GroupFollow2), -N(OP_GroupCancelInvite), -N(OP_CustomTitles), -N(OP_Split), -N(OP_Jump), -N(OP_ConsiderCorpse), -N(OP_SkillUpdate), -N(OP_GMEndTrainingResponse), -N(OP_GMEndTraining), -N(OP_GMTrainSkill), -N(OP_GMTraining), -N(OP_DeleteItem), -N(OP_CombatAbility), -N(OP_TrackUnknown), -N(OP_TrackTarget), -N(OP_Track), +N(OP_GroupInvite), +N(OP_GroupInvite2), +N(OP_GroupLeaderChange), +N(OP_GroupLeadershipAAUpdate), +N(OP_GroupMakeLeader), +N(OP_GroupMentor), +N(OP_GroupRoles), +N(OP_GroupUpdate), +N(OP_GroupUpdateB), +N(OP_GroupUpdateLeaderAA), +N(OP_GuildBank), +N(OP_GuildCreate), +N(OP_GuildDelete), +N(OP_GuildDemote), +N(OP_GuildInvite), +N(OP_GuildInviteAccept), +N(OP_GuildLeader), +N(OP_GuildManageAdd), +N(OP_GuildManageBanker), +N(OP_GuildManageRemove), +N(OP_GuildManageStatus), +N(OP_GuildMemberLevelUpdate), +N(OP_GuildMemberList), +N(OP_GuildMemberUpdate), +N(OP_GuildMOTD), +N(OP_GuildPeace), +N(OP_GuildPromote), +N(OP_GuildPublicNote), +N(OP_GuildRemove), +N(OP_GuildsList), +N(OP_GuildStatus), +N(OP_GuildTributeInfo), +N(OP_GuildUpdateURLAndChannel), +N(OP_GuildWar), +N(OP_Heartbeat), +N(OP_Hide), +N(OP_HideCorpse), +N(OP_HPUpdate), +N(OP_Illusion), +N(OP_IncreaseStats), +N(OP_InitialHPUpdate), +N(OP_InitialMobHealth), +N(OP_InspectAnswer), +N(OP_InspectBuffs), +N(OP_InspectMessageUpdate), +N(OP_InspectRequest), +N(OP_InstillDoubt), +N(OP_InterruptCast), N(OP_ItemLinkClick), N(OP_ItemLinkResponse), N(OP_ItemLinkText), -N(OP_RezzAnswer), -N(OP_RezzComplete), -N(OP_SendZonepoints), -N(OP_SetRunMode), -N(OP_InspectRequest), -N(OP_InspectAnswer), -N(OP_SenseTraps), -N(OP_DisarmTraps), -N(OP_Assist), -N(OP_AssistGroup), -N(OP_PickPocket), -N(OP_LootRequest), -N(OP_EndLootRequest), -N(OP_MoneyOnCorpse), -N(OP_LootComplete), -N(OP_LootItem), -N(OP_MoveItem), -N(OP_WhoAllRequest), -N(OP_WhoAllResponse), -N(OP_Consume), -N(OP_AutoAttack), -N(OP_AutoAttack2), -N(OP_TargetMouse), -N(OP_TargetCommand), -N(OP_TargetReject), -N(OP_TargetHoTT), -N(OP_Hide), -N(OP_Forage), -N(OP_Fishing), -N(OP_Bug), -N(OP_Emote), -N(OP_Consider), -N(OP_FaceChange), -N(OP_RandomReq), -N(OP_RandomReply), -N(OP_Camp), -N(OP_YellForHelp), -N(OP_SafePoint), -N(OP_Buff), -N(OP_BuffFadeMsg), -N(OP_SpecialMesg), -N(OP_Consent), -N(OP_ConsentResponse), -N(OP_Stun), -N(OP_BeginCast), -N(OP_CastSpell), -N(OP_InterruptCast), -N(OP_Death), -N(OP_FeignDeath), -N(OP_Illusion), -N(OP_LevelUpdate), -N(OP_LevelAppearance), -N(OP_MemorizeSpell), -N(OP_HPUpdate), -N(OP_Mend), -N(OP_Taunt), -N(OP_GMDelCorpse), -N(OP_GMFind), -N(OP_GMServers), -N(OP_GMGoto), -N(OP_GMSummon), -N(OP_GMKill), -N(OP_GMLastName), -N(OP_GMToggle), -N(OP_GMEmoteZone), -N(OP_GMBecomeNPC), -N(OP_GMHideMe), -N(OP_GMZoneRequest), -N(OP_GMZoneRequest2), -N(OP_Petition), -N(OP_PetitionRefresh), -N(OP_PDeletePetition), -N(OP_PetitionBug), -N(OP_PetitionUpdate), -N(OP_PetitionCheckout), -N(OP_PetitionCheckout2), -N(OP_PetitionDelete), -N(OP_PetitionResolve), -N(OP_PetitionCheckIn), -N(OP_PetitionUnCheckout), -N(OP_PetitionQue), -N(OP_SetServerFilter), -N(OP_NewSpawn), -N(OP_Animation), -N(OP_ZoneChange), -N(OP_DeleteSpawn), -N(OP_EnvDamage), -N(OP_Action), -N(OP_Damage), -N(OP_ManaChange), -N(OP_ClientError), -N(OP_Save), -N(OP_LocInfo), -N(OP_Surname), -N(OP_ClearSurname), -N(OP_SwapSpell), -N(OP_DeleteSpell), -N(OP_CloseContainer), -N(OP_ClickObjectAction), -N(OP_GroundSpawn), -N(OP_ClearObject), -N(OP_ZoneUnavail), +N(OP_ItemName), N(OP_ItemPacket), -N(OP_TradeRequest), -N(OP_TradeRequestAck), -N(OP_TradeAcceptClick), -N(OP_TradeMoneyUpdate), -N(OP_TradeCoins), -N(OP_CancelTrade), -N(OP_FinishTrade), -N(OP_SaveOnZoneReq), -N(OP_Logout), -N(OP_LogoutReply), -N(OP_PreLogoutReply), -N(OP_DuelResponse2), -N(OP_InstillDoubt), -N(OP_SafeFallSuccess), -N(OP_DisciplineUpdate), -N(OP_SendGuildTributes), -N(OP_SendTributes), -N(OP_TributeUpdate), -N(OP_TributeItem), -N(OP_TributePointUpdate), -N(OP_TributeInfo), -N(OP_GuildTributeInfo), -N(OP_OpenGuildTributeMaster), -N(OP_OpenTributeMaster), -N(OP_TributeTimer), -N(OP_SelectTribute), -N(OP_TributeNPC), -N(OP_TributeMoney), -N(OP_TributeToggle), -N(OP_CloseTributeMaster), -N(OP_RecipesFavorite), -N(OP_RecipesSearch), -N(OP_RecipeReply), -N(OP_RecipeDetails), -N(OP_RecipeAutoCombine), -N(OP_Shielding), -N(OP_FindPersonRequest), -N(OP_FindPersonReply), -N(OP_ZoneEntry), -N(OP_PlayerProfile), -N(OP_CharInventory), -N(OP_ZoneSpawns), -N(OP_Weather), -N(OP_ReqNewZone), -N(OP_NewZone), -N(OP_ReqClientSpawn), -N(OP_SpawnAppearance), -N(OP_ClientReady), -N(OP_ZoneComplete), -N(OP_ApproveWorld), -N(OP_LogServer), -N(OP_MOTD), -N(OP_SendLoginInfo), -N(OP_DeleteCharacter), -N(OP_SendCharInfo), -N(OP_ExpansionInfo), -N(OP_CharacterCreate), -N(OP_CharacterCreateRequest), -N(OP_RandomNameGenerator), -N(OP_GuildsList), -N(OP_ApproveName), -N(OP_EnterWorld), -N(OP_PostEnterWorld), //this is really OP_WorldAccessGranted -N(OP_SendSystemStats), -N(OP_World_Client_CRC1), -N(OP_World_Client_CRC2), -N(OP_SetChatServer), -N(OP_SetChatServer2), -N(OP_ZoneServerInfo), -N(OP_WorldClientReady), -N(OP_WorldUnknown001), -N(OP_AckPacket), -N(OP_WearChange), -N(OP_CrashDump), -N(OP_LoginComplete), -N(OP_GMNameChange), -N(OP_ReadBook), -N(OP_GMKick), -N(OP_RezzRequest), -N(OP_MultiLineMsg), -N(OP_TimeOfDay), -N(OP_CompletedTasks), -N(OP_MoneyUpdate), -N(OP_ClickObject), -N(OP_MoveDoor), -N(OP_TraderDelItem), -N(OP_AdventureMerchantPurchase), -N(OP_TestBuff), -N(OP_DuelResponse), -N(OP_RequestDuel), -N(OP_BazaarInspect), -N(OP_ClickDoor), -N(OP_GroupAcknowledge), -N(OP_GroupDelete), -N(OP_AdventureMerchantResponse), -N(OP_ShopEndConfirm), -N(OP_AdventureMerchantRequest), -N(OP_Sound), -N(OP_0x0193), -N(OP_0x0347), -N(OP_WorldComplete), -N(OP_MobRename), -N(OP_TaskDescription), -N(OP_TaskActivity), -N(OP_TaskMemberList), -N(OP_AnnoyingZoneUnknown), -N(OP_Some3ByteHPUpdate), -N(OP_FloatListThing), -N(OP_AAExpUpdate), -N(OP_ForceFindPerson), -N(OP_PlayMP3), -N(OP_RequestClientZoneChange), -N(OP_SomeItemPacketMaybe), -N(OP_QueryResponseThing), -N(OP_Some6ByteHPUpdate), -N(OP_BankerChange), -N(OP_BecomeCorpse), -N(OP_Action2), -N(OP_BazaarSearch), -N(OP_SetTitle), -N(OP_SetTitleReply), -N(OP_ConfirmDelete), -N(OP_ConsentDeny), -N(OP_CrystalCountUpdate), -N(OP_DeletePetition), -N(OP_DenyResponse), -N(OP_Disarm), -N(OP_Feedback), -N(OP_FriendsWho), -N(OP_GMApproval), -N(OP_GMSearchCorpse), -N(OP_GuildBank), -N(OP_InitialHPUpdate), -N(OP_InitialMobHealth), +N(OP_ItemPreview), +N(OP_ItemRecastDelay), +N(OP_ItemVerifyReply), +N(OP_ItemVerifyRequest), +N(OP_ItemViewUnknown), +N(OP_Jump), +N(OP_KeyRing), +N(OP_KnowledgeBase), +N(OP_LDoNButton), +N(OP_LDoNDisarmTraps), +N(OP_LDoNInspect), +N(OP_LDoNOpen), +N(OP_LDoNPickLock), +N(OP_LDoNSenseTraps), +N(OP_LeadershipExpToggle), +N(OP_LeadershipExpUpdate), +N(OP_LeaveAdventure), +N(OP_LeaveBoat), +N(OP_LevelAppearance), +N(OP_LevelUpdate), +N(OP_LFGAppearance), +N(OP_LFGCommand), N(OP_LFGGetMatchesRequest), N(OP_LFGGetMatchesResponse), N(OP_LFGResponse), +N(OP_LFGuild), N(OP_LFPCommand), N(OP_LFPGetMatchesRequest), N(OP_LFPGetMatchesResponse), -N(OP_LeadershipExpToggle), -N(OP_LeadershipExpUpdate), N(OP_LoadSpellSet), +N(OP_LocInfo), N(OP_LockoutTimerInfo), +N(OP_Login), +N(OP_LoginAccepted), +N(OP_LoginComplete), +N(OP_LoginUnknown1), +N(OP_LoginUnknown2), +N(OP_Logout), +N(OP_LogoutReply), +N(OP_LogServer), +N(OP_LootComplete), +N(OP_LootItem), +N(OP_LootRequest), +N(OP_ManaChange), +N(OP_ManaUpdate), +N(OP_MarkNPC), +N(OP_Marquee), +N(OP_MemorizeSpell), +N(OP_Mend), N(OP_MendHPUpdate), +N(OP_MercenaryAssign), +N(OP_MercenaryCommand), +N(OP_MercenaryDataRequest), +N(OP_MercenaryDataResponse), +N(OP_MercenaryDataUpdate), +N(OP_MercenaryDataUpdateRequest), +N(OP_MercenaryDismiss), +N(OP_MercenaryHire), +N(OP_MercenarySuspendRequest), +N(OP_MercenarySuspendResponse), +N(OP_MercenaryTimer), +N(OP_MercenaryTimerRequest), +N(OP_MercenaryUnknown1), +N(OP_MercenaryUnsuspendResponse), +N(OP_MobEnduranceUpdate), N(OP_MobHealth), +N(OP_MobManaUpdate), +N(OP_MobRename), +N(OP_MobUpdate), // not used anymore, here for lecacy reasons (eqextractor) +N(OP_MoneyOnCorpse), +N(OP_MoneyUpdate), +N(OP_MOTD), +N(OP_MoveCoin), +N(OP_MoveDoor), +N(OP_MoveItem), N(OP_MoveLogDisregard), N(OP_MoveLogRequest), +N(OP_MultiLineMsg), +N(OP_NewSpawn), +N(OP_NewTitlesAvailable), +N(OP_NewZone), +N(OP_OnLevelMessage), +N(OP_OpenContainer), +N(OP_OpenDiscordMerchant), +N(OP_OpenGuildTributeMaster), +N(OP_OpenInventory), +N(OP_OpenNewTasksWindow), +N(OP_OpenTributeMaster), +N(OP_PDeletePetition), +N(OP_PetBuffWindow), +N(OP_PetCommands), +N(OP_Petition), +N(OP_PetitionBug), +N(OP_PetitionCheckIn), +N(OP_PetitionCheckout), +N(OP_PetitionCheckout2), +N(OP_PetitionDelete), +N(OP_PetitionQue), +N(OP_PetitionRefresh), +N(OP_PetitionResolve), N(OP_PetitionSearch), N(OP_PetitionSearchResults), N(OP_PetitionSearchText), -N(OP_RaidInvite), -N(OP_ReclaimCrystals), -N(OP_Report), -N(OP_SenseHeading), -N(OP_LDoNOpen), -N(OP_LDoNSenseTraps), -N(OP_LDoNPickLock), -N(OP_LDoNDisarmTraps), -N(OP_LDoNInspect), -N(OP_DynamicWall), -N(OP_RequestTitles), -N(OP_PurchaseLeadershipAA), -N(OP_UpdateLeadershipAA), -N(OP_AdventurePointsUpdate), -N(OP_ZoneInUnknown), -N(OP_ZoneServerReady), //terrible name. -N(OP_ZoneGuildList), -N(OP_SendTitleList), -N(OP_NewTitlesAvailable), -N(OP_Bandolier), -N(OP_OpenDiscordMerchant), -N(OP_DiscordMerchantInventory), -N(OP_GiveMoney), -N(OP_OnLevelMessage), -N(OP_RequestKnowledgeBase), -N(OP_KnowledgeBase), -N(OP_VetRewardsAvaliable), -N(OP_VetClaimRequest), -N(OP_VetClaimReply), -N(OP_WeaponEquip1), -N(OP_WeaponEquip2), -N(OP_WeaponUnequip2), -N(OP_WorldLogout), -N(OP_SessionReady), -//Login -N(OP_Login), -N(OP_ServerListRequest), +N(OP_PetitionUnCheckout), +N(OP_PetitionUpdate), +N(OP_PickPocket), +N(OP_PlayerProfile), N(OP_PlayEverquestRequest), -N(OP_ChatMessage), -N(OP_LoginAccepted), -N(OP_ServerListResponse), -N(OP_Poll), N(OP_PlayEverquestResponse), -N(OP_EnterChat), +N(OP_PlayMP3), +N(OP_Poll), N(OP_PollResponse), -N(OP_Command), -N(OP_ZonePlayerToBind), -N(OP_AutoFire), -N(OP_Rewind), -N(OP_OpenNewTasksWindow), -N(OP_TaskActivityComplete), -N(OP_AcceptNewTask), -N(OP_CancelTask), -N(OP_TaskHistoryRequest), -N(OP_TaskHistoryReply), -N(OP_PetBuffWindow), -N(OP_RaidJoin), -N(OP_Translocate), -N(OP_Sacrifice), -N(OP_KeyRing), N(OP_PopupResponse), -N(OP_DeleteCharge), +N(OP_PostEnterWorld), //this is really OP_WorldAccessGranted N(OP_PotionBelt), -N(OP_Barter), -N(OP_VoiceMacroIn), -N(OP_VoiceMacroOut), -N(OP_WorldObjectsSent), -N(OP_BlockedBuffs), -N(OP_RemoveBlockedBuffs), -N(OP_ClearBlockedBuffs), -N(OP_GroupUpdateLeaderAA), -N(OP_MarkNPC), -N(OP_ClearNPCMarks), -N(OP_DoGroupLeadershipAbility), -N(OP_DelegateAbility), -N(OP_SetGroupTarget), -N(OP_ApplyPoison), -N(OP_FinishWindow), -N(OP_FinishWindow2), -N(OP_ItemVerifyRequest), -N(OP_ItemVerifyReply), -N(OP_GMTrainSkillConfirm), -N(OP_RestState), -N(OP_AugmentInfo), -N(OP_PVPStats), -N(OP_PVPLeaderBoardRequest), -N(OP_PVPLeaderBoardReply), -N(OP_PVPLeaderBoardDetailsRequest), +N(OP_PreLogoutReply), +N(OP_PurchaseLeadershipAA), N(OP_PVPLeaderBoardDetailsReply), -N(OP_DisciplineTimer), -N(OP_RespawnWindow), -N(OP_AdventureMerchantSell), -N(OP_AdventureStatsRequest), -N(OP_AdventureStatsReply), -N(OP_AdventureLeaderboardRequest), -N(OP_AdventureLeaderboardReply), -N(OP_SetStartCity), -N(OP_LoginUnknown1), -N(OP_LoginUnknown2), -N(OP_ItemViewUnknown), -N(OP_GetGuildMOTDReply), -N(OP_SetGuildRank), -N(OP_SpawnPositionUpdate), -N(OP_ManaUpdate), -N(OP_EnduranceUpdate), -N(OP_MobManaUpdate), -N(OP_MobEnduranceUpdate), -N(OP_GroupUpdateB), -N(OP_GroupDisbandYou), -N(OP_GroupDisbandOther), -N(OP_GroupLeaderChange), -N(OP_GroupLeadershipAAUpdate), -N(OP_GroupRoles), -N(OP_SendFindableNPCs), -N(OP_HideCorpse), -N(OP_TargetBuffs), -N(OP_TradeBusy), -N(OP_GuildUpdateURLAndChannel), -N(OP_CameraEffect), -N(OP_SpellEffect), -N(OP_DzQuit), -N(OP_DzListTimers), -N(OP_DzPlayerList), -N(OP_DzAddPlayer), -N(OP_DzRemovePlayer), -N(OP_DzSwapPlayer), -N(OP_DzMakeLeader), -N(OP_DzJoinExpeditionConfirm), -N(OP_DzJoinExpeditionReply), -N(OP_DzExpeditionInfo), -N(OP_DzMemberStatus), -N(OP_DzLeaderStatus), -N(OP_DzExpeditionEndsWarning), -N(OP_DzExpeditionList), -N(OP_DzMemberList), -N(OP_DzCompass), -N(OP_DzChooseZone), -N(OP_BuffCreate), -N(OP_GuildStatus), -N(OP_BuffRemoveRequest), -N(OP_CorpseDrag), -N(OP_CorpseDrop), -N(OP_ChangeSize), -N(OP_GroupMakeLeader), +N(OP_PVPLeaderBoardDetailsRequest), +N(OP_PVPLeaderBoardReply), +N(OP_PVPLeaderBoardRequest), +N(OP_PVPStats), +N(OP_QueryResponseThing), +N(OP_RaidInvite), +N(OP_RaidJoin), +N(OP_RaidUpdate), +N(OP_RandomNameGenerator), +N(OP_RandomReply), +N(OP_RandomReq), +N(OP_ReadBook), +N(OP_RecipeAutoCombine), +N(OP_RecipeDetails), +N(OP_RecipeReply), +N(OP_RecipesFavorite), +N(OP_RecipesSearch), +N(OP_ReclaimCrystals), +N(OP_ReloadUI), N(OP_RemoveAllDoors), +N(OP_RemoveBlockedBuffs), N(OP_RemoveNimbusEffect), -N(OP_GuildCreate), -N(OP_AltCurrency), -N(OP_FellowshipUpdate), -N(OP_AltCurrencyMerchantRequest), -N(OP_AltCurrencyMerchantReply), -N(OP_AltCurrencyPurchase), -N(OP_AltCurrencySellSelection), -N(OP_AltCurrencyReclaim), -N(OP_AltCurrencySell), -N(OP_Untargetable), -N(OP_CrystalReclaim), -N(OP_CrystalCreate), +N(OP_Report), +N(OP_ReqClientSpawn), +N(OP_ReqNewZone), +N(OP_RequestClientZoneChange), +N(OP_RequestDuel), +N(OP_RequestKnowledgeBase), +N(OP_RequestTitles), +N(OP_RespawnWindow), +N(OP_RespondAA), +N(OP_RestState), +N(OP_Rewind), +N(OP_RezzAnswer), +N(OP_RezzComplete), +N(OP_RezzRequest), +N(OP_Sacrifice), +N(OP_SafeFallSuccess), +N(OP_SafePoint), +N(OP_Save), +N(OP_SaveOnZoneReq), +N(OP_SelectTribute), +N(OP_SendAAStats), +N(OP_SendAATable), +N(OP_SendCharInfo), +N(OP_SendExpZonein), +N(OP_SendFindableNPCs), +N(OP_SendGuildTributes), +N(OP_SendLoginInfo), N(OP_SendMaxCharacters), N(OP_SendMembership), N(OP_SendMembershipDetails), -N(OP_LFGuild), +N(OP_SendSystemStats), +N(OP_SendTitleList), +N(OP_SendTributes), +N(OP_SendZonepoints), +N(OP_SenseHeading), +N(OP_SenseTraps), +N(OP_ServerListRequest), +N(OP_ServerListResponse), +N(OP_SessionReady), +N(OP_SetChatServer), +N(OP_SetChatServer2), +N(OP_SetGroupTarget), +N(OP_SetGuildMOTD), +N(OP_SetGuildRank), +N(OP_SetRunMode), +N(OP_SetServerFilter), +N(OP_SetStartCity), +N(OP_SetTitle), +N(OP_SetTitleReply), +N(OP_Shielding), +N(OP_ShopDelItem), +N(OP_ShopEnd), +N(OP_ShopEndConfirm), +N(OP_ShopItem), +N(OP_ShopPlayerBuy), +N(OP_ShopPlayerSell), +N(OP_ShopRequest), +N(OP_SimpleMessage), +N(OP_SkillUpdate), +N(OP_Sneak), +N(OP_Some3ByteHPUpdate), +N(OP_Some6ByteHPUpdate), +N(OP_SomeItemPacketMaybe), +N(OP_Sound), +N(OP_SpawnAppearance), +N(OP_SpawnDoor), +N(OP_SpawnPositionUpdate), +N(OP_SpecialMesg), +N(OP_SpellEffect), +N(OP_Split), +N(OP_Stamina), +N(OP_Stun), +N(OP_Surname), +N(OP_SwapSpell), +N(OP_TargetBuffs), +N(OP_TargetCommand), +N(OP_TargetHoTT), +N(OP_TargetMouse), +N(OP_TargetReject), +N(OP_TaskActivity), +N(OP_TaskActivityComplete), +N(OP_TaskDescription), +N(OP_TaskHistoryReply), +N(OP_TaskHistoryRequest), +N(OP_TaskMemberList), +N(OP_Taunt), +N(OP_TestBuff), +N(OP_TGB), +N(OP_TimeOfDay), +N(OP_Track), +N(OP_TrackTarget), +N(OP_TrackUnknown), +N(OP_TradeAcceptClick), +N(OP_TradeBusy), +N(OP_TradeCoins), +N(OP_TradeMoneyUpdate), +N(OP_Trader), +N(OP_TraderBuy), +N(OP_TraderDelItem), +N(OP_TradeRequest), +N(OP_TradeRequestAck), +N(OP_TraderItemUpdate), +N(OP_TraderShop), +N(OP_TradeSkillCombine), +N(OP_Translocate), +N(OP_TributeInfo), +N(OP_TributeItem), +N(OP_TributeMoney), +N(OP_TributeNPC), +N(OP_TributePointUpdate), +N(OP_TributeTimer), +N(OP_TributeToggle), +N(OP_TributeUpdate), +N(OP_Untargetable), +N(OP_UpdateAA), +N(OP_UpdateLeadershipAA), +N(OP_VetClaimReply), +N(OP_VetClaimRequest), +N(OP_VetRewardsAvaliable), +N(OP_VoiceMacroIn), +N(OP_VoiceMacroOut), +N(OP_WeaponEquip1), +N(OP_WeaponEquip2), +N(OP_WeaponUnequip2), +N(OP_WearChange), +N(OP_Weather), +N(OP_Weblink), +N(OP_WhoAllRequest), +N(OP_WhoAllResponse), +N(OP_World_Client_CRC1), +N(OP_World_Client_CRC2), +N(OP_WorldClientReady), +N(OP_WorldComplete), +N(OP_WorldLogout), +N(OP_WorldObjectsSent), +N(OP_WorldUnknown001), +N(OP_XTargetAutoAddHaters), N(OP_XTargetRequest), N(OP_XTargetResponse), -N(OP_XTargetAutoAddHaters), -N(OP_Weblink), -N(OP_InspectMessageUpdate), -N(OP_ItemPreview), -N(OP_MercenaryDataRequest), -N(OP_MercenaryDataResponse), -N(OP_MercenaryHire), -N(OP_MercenaryUnknown1), -N(OP_MercenaryTimer), -N(OP_MercenaryAssign), -N(OP_MercenaryDataUpdate), -N(OP_MercenaryCommand), -N(OP_MercenarySuspendRequest), -N(OP_MercenarySuspendResponse), -N(OP_MercenaryUnsuspendResponse), -N(OP_MercenaryDataUpdateRequest), -N(OP_MercenaryDismiss), -N(OP_MercenaryTimerRequest), -N(OP_OpenInventory), -N(OP_OpenContainer), -N(OP_Marquee), -N(OP_ClientTimeStamp), -N(OP_GuildPromote), +N(OP_YellForHelp), +N(OP_ZoneChange), +N(OP_ZoneComplete), +N(OP_ZoneEntry), +N(OP_ZoneGuildList), +N(OP_ZoneInUnknown), +N(OP_ZonePlayerToBind), +N(OP_ZoneServerInfo), +N(OP_ZoneServerReady), +N(OP_ZoneSpawns), +N(OP_ZoneUnavail), +// mail and chat opcodes located in ../mail_oplist.h diff --git a/common/eq_dictionary.cpp b/common/eq_dictionary.cpp index 595d0cc5f..f85d3110f 100644 --- a/common/eq_dictionary.cpp +++ b/common/eq_dictionary.cpp @@ -1024,6 +1024,26 @@ bool EQLimits::AllowsEmptyBagInBag(uint32 version) { //return local[ValidateMobVersion(version)]; } +bool EQLimits::AllowsClickCastFromBag(uint32 version) { + static const bool local[_EmuClientCount] = { +/*Unknown*/ false, +/*62*/ Client62::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*Titanium*/ Titanium::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*SoF*/ SoF::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*SoD*/ SoD::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*Underfoot*/ Underfoot::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*RoF*/ RoF::limits::ALLOWS_CLICK_CAST_FROM_BAG, +/*RoF2*/ false, + +/*NPC*/ false, +/*Merc*/ false, +/*Bot*/ false, +/*Pet*/ false + }; + + return local[ValidateMobVersion(version)]; +} + // items uint16 EQLimits::ItemCommonSize(uint32 version) { static const uint16 local[_EmuClientCount] = { diff --git a/common/eq_dictionary.h b/common/eq_dictionary.h index 14f9d53d3..c08809a12 100644 --- a/common/eq_dictionary.h +++ b/common/eq_dictionary.h @@ -184,6 +184,7 @@ public: static uint64 CursorBitmask(uint32 version); static bool AllowsEmptyBagInBag(uint32 version); + static bool AllowsClickCastFromBag(uint32 version); // items static uint16 ItemCommonSize(uint32 version); diff --git a/common/eq_packet.h b/common/eq_packet.h index 9e0bcec3e..04418f733 100644 --- a/common/eq_packet.h +++ b/common/eq_packet.h @@ -97,16 +97,15 @@ protected: }; class EQApplicationPacket : public EQPacket { -// friend class EQProtocolPacket; friend class EQStream; public: - EQApplicationPacket() : EQPacket(OP_Unknown,nullptr,0) + EQApplicationPacket() : EQPacket(OP_Unknown, nullptr, 0), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op) : EQPacket(op,nullptr,0) + EQApplicationPacket(const EmuOpcode op) : EQPacket(op, nullptr, 0), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op,nullptr,len) + EQApplicationPacket(const EmuOpcode op, const uint32 len) : EQPacket(op, nullptr, len), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } - EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op,buf,len) + EQApplicationPacket(const EmuOpcode op, const unsigned char *buf, const uint32 len) : EQPacket(op, buf, len), opcode_bypass(0) { app_opcode_size = GetExecutablePlatform() == ExePlatformUCS ? 1 : 2; } bool combine(const EQApplicationPacket *rhs); uint32 serialize (uint16 opcode, unsigned char *dest) const; @@ -119,12 +118,16 @@ public: virtual void DumpRawHeader(uint16 seq=0xffff, FILE *to = stdout) const; virtual void DumpRawHeaderNoTime(uint16 seq=0xffff, FILE *to = stdout) const; + uint16 GetOpcodeBypass() { return opcode_bypass; } + void SetOpcodeBypass(uint16 v) { opcode_bypass = v; } + protected: uint8 app_opcode_size; + uint16 opcode_bypass; private: - EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size) { app_opcode_size = p.app_opcode_size; } + EQApplicationPacket(const EQApplicationPacket &p) : EQPacket(p.emu_opcode, p.pBuffer, p.size), opcode_bypass(p.opcode_bypass) { app_opcode_size = p.app_opcode_size; } }; diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 1751f4b4e..a5e22e44e 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -534,7 +534,7 @@ struct SpellBuffFade_Struct { /*007*/ uint8 unknown7; /*008*/ uint32 spellid; /*012*/ uint32 duration; -/*016*/ uint32 unknown016; +/*016*/ uint32 num_hits; /*020*/ uint32 unknown020; //prolly global player ID /*024*/ uint32 slotid; /*028*/ uint32 bufffade; @@ -689,7 +689,7 @@ struct CharCreate_Struct /*0076*/ uint32 drakkin_heritage; // added for SoF /*0080*/ uint32 drakkin_tattoo; // added for SoF /*0084*/ uint32 drakkin_details; // added for SoF -/*0088*/ +/*0088*/ uint32 tutorial; }; /* @@ -759,14 +759,62 @@ struct MovePotionToBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -800,9 +848,12 @@ struct SuspendedMinion_Struct ** Length: 4308 bytes ** OpCode: 0x006a */ -static const uint32 MAX_PP_LANGUAGE = 28; -static const uint32 MAX_PP_SPELLBOOK = 480; // Increased to 480 to support SoF -static const uint32 MAX_PP_MEMSPELL = 9; +static const uint32 MAX_PP_LANGUAGE = 28; +static const uint32 MAX_PP_SPELLBOOK = 480; // Set for all functions +static const uint32 MAX_PP_MEMSPELL = 9; // Set to latest client so functions can work right +static const uint32 MAX_PP_REF_SPELLBOOK = 480; // Set for Player Profile size retain +static const uint32 MAX_PP_REF_MEMSPELL = 9; // Set for Player Profile size retain + static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 240; static const uint32 MAX_GROUP_MEMBERS = 6; @@ -880,7 +931,7 @@ struct PlayerProfile_Struct /*0245*/ uint8 guildbanker; /*0246*/ uint8 unknown0246[6]; // /*0252*/ uint32 intoxication; -/*0256*/ uint32 spellSlotRefresh[MAX_PP_MEMSPELL]; //in ms +/*0256*/ uint32 spellSlotRefresh[MAX_PP_REF_MEMSPELL]; //in ms /*0292*/ uint32 abilitySlotRefresh; /*0296*/ uint8 haircolor; // Player hair color /*0297*/ uint8 beardcolor; // Player beard color @@ -919,9 +970,9 @@ struct PlayerProfile_Struct /*2505*/ uint8 unknown2541[47]; // ? /*2552*/ uint8 languages[MAX_PP_LANGUAGE]; /*2580*/ uint8 unknown2616[4]; -/*2584*/ uint32 spell_book[MAX_PP_SPELLBOOK]; +/*2584*/ uint32 spell_book[MAX_PP_REF_SPELLBOOK]; /*4504*/ uint8 unknown4540[128]; // Was [428] all 0xff -/*4632*/ uint32 mem_spells[MAX_PP_MEMSPELL]; +/*4632*/ uint32 mem_spells[MAX_PP_REF_MEMSPELL]; /*4668*/ uint8 unknown4704[32]; // /*4700*/ float y; // Player y position /*4704*/ float x; // Player x position @@ -1446,17 +1497,18 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; struct MoveItem_Struct @@ -1464,16 +1516,18 @@ struct MoveItem_Struct /*0000*/ uint32 from_slot; /*0004*/ uint32 to_slot; /*0008*/ uint32 number_in_stack; +/*0012*/ }; // both MoveItem_Struct/DeleteItem_Struct server structures will be changing to a structure-based slot format..this will // be used for handling SoF/SoD/etc... time stamps sent using the MoveItem_Struct format. (nothing will be done with this -// info at the moment..but, it forwards it on to the server for handling/future use) +// info at the moment..but, it is forwarded on to the server for handling/future use) struct ClientTimeStamp_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; // @@ -2177,6 +2231,12 @@ struct GroupLeaderChange_Struct /*128*/ char Unknown128[20]; }; +struct GroupMentor_Struct { +/*000*/ int percent; +/*004*/ char name[64]; +/*068*/ +}; + struct FaceChange_Struct { /*000*/ uint8 haircolor; /*001*/ uint8 beardcolor; @@ -3397,7 +3457,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3908,6 +3968,11 @@ struct MarkNPC_Struct /*08**/ char Name[64]; }; +struct InspectBuffs_Struct { +/*000*/ uint32 spell_id[BUFF_COUNT]; +/*100*/ uint32 tics_remaining[BUFF_COUNT]; +}; + struct RaidGeneral_Struct { /*00*/ uint32 action; //=10 /*04*/ char player_name[64]; //should both be the player's name @@ -3923,6 +3988,19 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*136*/ char motd[0]; // max size is 1024, but reply is variable +}; + +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ char leader_name[64]; +/*132*/ GroupLeadershipAA_Struct group; //unneeded +/*196*/ RaidLeadershipAA_Struct raid; +/*260*/ char Unknown260[128]; //unverified +}; struct RaidAdd_Struct { /*000*/ uint32 action; //=0 @@ -4064,7 +4142,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; @@ -4261,6 +4339,13 @@ struct ItemVerifyReply_Struct { /*012*/ }; +struct ItemRecastDelay_Struct { +/*000*/ uint32 recast_delay; // in seconds +/*004*/ uint32 recast_type; +/*008*/ uint32 unknown008; +/*012*/ +}; + /** * Shroud yourself. For yourself shrouding, this has your spawnId, spawnStruct, * bits of your charProfileStruct (no checksum, then charProfile up till @@ -4299,9 +4384,9 @@ struct ControlBoat_Struct { struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[67]; // total packet length 72, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[67]; // total packet length 72, all the rest were always 00 /*072*/ }; @@ -4589,11 +4674,13 @@ struct BuffIconEntry_Struct uint32 buff_slot; uint32 spell_id; uint32 tics_remaining; + uint32 num_hits; }; struct BuffIcon_Struct { uint32 entity_id; + uint8 all_buffs; uint16 count; BuffIconEntry_Struct entries[0]; }; diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index 8cb67e0e0..7b69decc2 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -532,9 +532,12 @@ void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req) return; } - uint16 opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode); - - //_log(NET__APP_TRACE, "Queueing %sacked packet with opcode 0x%x (%s) and length %d", ack_req?"":"non-", opcode, OpcodeManager::EmuToName(pack->emu_opcode), pack->size); + uint16 opcode = 0; + if(pack->GetOpcodeBypass() != 0) { + opcode = pack->GetOpcodeBypass(); + } else { + opcode = (*OpMgr)->EmuToEQ(pack->emu_opcode); + } if (!ack_req) { NonSequencedPush(new EQProtocolPacket(opcode, pack->pBuffer, pack->size)); @@ -877,43 +880,6 @@ sockaddr_in address; AddBytesSent(length); } -/* -commented out since im not sure theres a lot of merit in it. -Really it was bitterness towards allocating a 2k buffer on the stack each call. -Im sure the thought was client side, but even then, they will -likely need a whole thread to call this method, in which case, they should -supply the buffer so we dont re-allocate it each time. -EQProtocolPacket *EQStream::Read(int eq_fd, sockaddr_in *from) -{ -int socklen; -int length=0; -EQProtocolPacket *p=nullptr; -char temp[15]; - - socklen=sizeof(sockaddr); -#ifdef _WINDOWS - length=recvfrom(eq_fd, (char *)_tempBuffer, 2048, 0, (struct sockaddr*)from, (int *)&socklen); -#else - length=recvfrom(eq_fd, _tempBuffer, 2048, 0, (struct sockaddr*)from, (socklen_t *)&socklen); -#endif - - if (length>=2) { - p=new EQProtocolPacket(_tempBuffer[1],&_tempBuffer[2],length-2); - - uint32 ip=from->sin_addr.s_addr; - sprintf(temp,"%d.%d.%d.%d:%d", - *(unsigned char *)&ip, - *((unsigned char *)&ip+1), - *((unsigned char *)&ip+2), - *((unsigned char *)&ip+3), - ntohs(from->sin_port)); - //std::cout << timestamp() << "Data from: " << temp << " OpCode 0x" << std::hex << std::setw(2) << std::setfill('0') << (int)p->opcode << std::dec << std::endl; - //dump_message(p->pBuffer,p->size,timestamp()); - - } - return p; -}*/ - void EQStream::SendSessionResponse() { EQProtocolPacket *out=new EQProtocolPacket(OP_SessionResponse,nullptr,sizeof(SessionResponse)); @@ -1101,14 +1067,6 @@ EQProtocolPacket *p=nullptr; SequencedQueue.clear(); } MOutboundQueue.unlock(); - -/*if(uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) { - _log(NET__ERROR, _L "Out-bound Invalid Sequenced queue: BS %d + SQ %d != NOS %d" __L, SequencedBase, SequencedQueue.size(), NextOutSeq); -} -if(NextSequencedSend > SequencedQueue.size()) { - _log(NET__ERROR, _L "Out-bound Next Send Sequence is beyond the end of the queue NSS %d > SQ %d" __L, NextSequencedSend, SequencedQueue.size()); -}*/ - //NOTE: we prolly want to reset counters if we are stupposed to do anything after this. } void EQStream::PacketQueueClear() diff --git a/common/extprofile.h b/common/extprofile.h index 2e29fbf4a..a8447d4fc 100644 --- a/common/extprofile.h +++ b/common/extprofile.h @@ -21,7 +21,6 @@ #include "eq_packet_structs.h" #include "item.h" - #pragma pack(1) /* @@ -37,24 +36,24 @@ */ struct ExtendedProfile_Struct { // Pet stuff - uint16 pet_id; - uint16 old_pet_hp; - uint16 old_pet_mana; - SpellBuff_Struct pet_buffs[BUFF_COUNT]; - uint32 pet_items[_MaterialCount]; - char merc_name[64]; + uint16 pet_id; /* Not Used */ + uint16 old_pet_hp; /* Not Used */ + uint16 old_pet_mana; /* Not Used */ + SpellBuff_Struct pet_buffs[BUFF_COUNT]; /* Not Used */ + uint32 pet_items[_MaterialCount]; /* Not Used */ + char merc_name[64]; /* Used */ - uint32 aa_effects; - uint32 perAA; //% of exp going to AAs - uint32 expended_aa; // Total of expended AA - uint32 pet_hp; - uint32 pet_mana; - uint32 mercTemplateID; - uint32 mercSuspendedTime; - bool mercIsSuspended; - uint32 mercTimerRemaining; - uint8 mercGender; - int32 mercState; + uint32 aa_effects; /* Used */ + uint32 perAA; /* Used: % of exp going to AAs */ + uint32 expended_aa; /* Used: Total of expended AA */ + uint32 pet_hp; /* Not Used */ + uint32 pet_mana; /* Not Used */ + uint32 mercTemplateID; /* Not Used */ + uint32 mercSuspendedTime; /* Not Used */ + bool mercIsSuspended; /* Not Used */ + uint32 mercTimerRemaining; /* Not Used */ + uint8 mercGender; /* Not Used */ + int32 mercState; /* Not Used */ }; #pragma pack() diff --git a/common/guild_base.cpp b/common/guild_base.cpp index 6546918d0..e6df92569 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -756,7 +756,7 @@ bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { std::string query; if(guild_id != GUILD_NONE) { - query = StringFormat("REPLACE INTO guild_members (char_id,guild_id,rank) VALUES(%d,%d,%d)", charid, guild_id, rank); + query = StringFormat("REPLACE INTO guild_members (char_id,guild_id,rank,public_note) VALUES(%d,%d,%d,'')", charid, guild_id, rank); auto results = m_db->QueryDatabase(query); if (!results.Success()) { @@ -897,10 +897,10 @@ bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg) { " FROM vwBotCharacterMobs AS c LEFT JOIN vwGuildMembers AS g ON c.id=g.char_id AND c.mobtype = g.mobtype " #else #define GuildMemberBaseQuery \ -"SELECT c.id,c.name,c.class,c.level,c.timelaston,c.zoneid," \ +"SELECT c.id,c.name,c.class,c.level,c.last_login,c.zone_id," \ " g.guild_id,g.rank,g.tribute_enable,g.total_tribute,g.last_tribute," \ " g.banker,g.public_note,g.alt " \ -" FROM character_ AS c LEFT JOIN guild_members AS g ON c.id=g.char_id " +" FROM `character_data` AS c LEFT JOIN guild_members AS g ON c.id=g.char_id " #endif static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into) { //fields from `characer_` @@ -1241,7 +1241,7 @@ BaseGuildManager::GuildInfo::GuildInfo() { uint32 BaseGuildManager::DoesAccountContainAGuildLeader(uint32 AccountID) { std::string query = StringFormat("SELECT guild_id FROM guild_members WHERE char_id IN " - "(SELECT id FROM character_ WHERE account_id = %i) AND rank = 2", + "(SELECT id FROM `character_data` WHERE account_id = %i) AND rank = 2", AccountID); auto results = m_db->QueryDatabase(query); if (!results.Success()) diff --git a/common/guilds.cpp b/common/guilds.cpp index 4f5c08bb6..ddd5d9a59 100644 --- a/common/guilds.cpp +++ b/common/guilds.cpp @@ -24,318 +24,4 @@ #ifndef WIN32 #include //for htonl -#endif - -/* -void Database::GetGuildMembers(uint32 guild_id, GuildMember_Struct* gms){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 count=0; - uint32 length=0; - if (RunQuery(query, MakeAnyLenString(&query, "Select name,profile,timelaston,guildrank,publicnote from character_ where guild=%i", guild_id), errbuf, &result)) { - safe_delete_array(query); - while( ( row = mysql_fetch_row(result) ) ){ - strcpy(gms->member[count].name,row[0]); - length+=strlen(row[0])+strlen(row[4]); - PlayerProfile_Struct* pps=(PlayerProfile_Struct*)row[1]; - gms->member[count].level=htonl(pps->level); - gms->member[count].zoneid=(pps->zone_id*256); - gms->member[count].timelaston=htonl(atol(row[2])); - gms->member[count].class_=htonl(pps->class_); - gms->member[count].rank=atoi(row[3]); - strcpy(gms->member[count].publicnote,row[4]); - count++; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetGuildMembers query '%s': %s", query, errbuf); - safe_delete_array(query); - } - gms->count=count; - gms->length=length; -} - -uint32 Database::NumberInGuild(uint32 guild_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "Select count(id) from character_ where guild=%i", guild_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in NumberInGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return 0; - } - return 0; -} -bool Database::SetGuild(char* name, uint32 guild_id, uint8 guildrank) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET guild=%i, guildrank=%i WHERE name='%s'", guild_id, guildrank, name), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in SetGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - return false; -} - -bool Database::SetGuild(uint32 charid, uint32 guild_id, uint8 guildrank) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET guild=%i, guildrank=%i WHERE id=%i", guild_id, guildrank, charid), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in SetGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::DeleteGuild(uint32 guild_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char *query2 = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "DELETE FROM guilds WHERE id=%i;", guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) { - if(!RunQuery(query2, MakeAnyLenString(&query2, "update character_ set guild=0,guildrank=0 where guild=%i", guild_id), errbuf, 0, &affected_rows)) - LogFile->write(EQEMuLog::Error, "Error in DeleteGuild cleanup query '%s': %s", query2, errbuf); - safe_delete_array(query2); - return true; - } - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in DeleteGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::RenameGuild(uint32 guild_id, const char* name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - char buf[65]; - DoEscapeString(buf, name, strlen(name)) ; - - if (RunQuery(query, MakeAnyLenString(&query, "Update guilds set name='%s' WHERE id=%i;", buf, guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in RenameGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - - - -bool Database::EditGuild(uint32 guild_id, uint8 ranknum, GuildRankLevel_Struct* grl) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int chars = 0; - uint32 affected_rows = 0; - char buf[203]; - char buf2[8]; - DoEscapeString(buf, grl->rankname, strlen(grl->rankname)) ; - buf2[GUILD_HEAR] = grl->heargu + '0'; - buf2[GUILD_SPEAK] = grl->speakgu + '0'; - buf2[GUILD_INVITE] = grl->invite + '0'; - buf2[GUILD_REMOVE] = grl->remove + '0'; - buf2[GUILD_PROMOTE] = grl->promote + '0'; - buf2[GUILD_DEMOTE] = grl->demote + '0'; - buf2[GUILD_MOTD] = grl->motd + '0'; - buf2[GUILD_WARPEACE] = grl->warpeace + '0'; - - if (ranknum == 0) - chars = MakeAnyLenString(&query, "Update guilds set rank%ititle='%s' WHERE id=%i;", ranknum, buf, guild_id); - else - chars = MakeAnyLenString(&query, "Update guilds set rank%ititle='%s', rank%i='%s' WHERE id=%i;", ranknum, buf, ranknum, buf2, guild_id); - - if (RunQuery(query, chars, errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in EditGuild query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::GetGuildNameByID(uint32 guild_id, char * name) { - if (!name || !guild_id) return false; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "select name from guilds where id='%i'", guild_id), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row[0]) - sprintf(name,"%s",row[0]); - mysql_free_result(result); - return true; - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetGuildNameByID query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -uint32 Database::GetGuildIDbyLeader(uint32 leader) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM guilds WHERE leader=%i", leader), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint32 tmp = atoi(row[0]); - mysql_free_result(result); - return tmp; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in Getguild_idbyLeader query '%s': %s", query, errbuf); - safe_delete_array(query); - } - - return 0; -} - -bool Database::SetGuildLeader(uint32 guild_id, uint32 leader) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET leader=%i WHERE id=%i", leader, guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) - return true; - else - return false; - } - else { - LogFile->write(EQEMuLog::Error, "Error in SetGuildLeader query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } - - return false; -} - -bool Database::SetGuildMOTD(uint32 guild_id, const char* motd) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char* motdbuf = 0; - uint32 affected_rows = 0; - - motdbuf = new char[(strlen(motd)*2)+3]; - - DoEscapeString(motdbuf, motd, strlen(motd)) ; - - if (RunQuery(query, MakeAnyLenString(&query, "Update guilds set motd='%s' WHERE id=%i;", motdbuf, guild_id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - delete motdbuf; - if (affected_rows == 1) - return true; - else - return false; - } - else - { - LogFile->write(EQEMuLog::Error, "Error in SetGuildMOTD query '%s': %s", query, errbuf); - safe_delete_array(query); - delete motdbuf; - return false; - } - - return false; -} - -string Database::GetGuildMOTD(uint32 guild_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - string motd_str; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT motd FROM guilds WHERE id=%i", guild_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (row[0]) - motd_str = row[0]; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetGuildMOTD query '%s': %s", query, errbuf); - safe_delete_array(query); - } - return motd_str; -} -*/ - +#endif \ No newline at end of file diff --git a/common/item.cpp b/common/item.cpp index 513ddb23f..cfa52e0c4 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -911,6 +911,30 @@ bool Inventory::CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_S return true; } +bool Inventory::SupportsClickCasting(int16 slot_id) +{ + // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range + if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource) + { + return true; + } + else if (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END) + { + if (EQLimits::AllowsClickCastFromBag(m_version)) + return true; + } + + return false; +} + +bool Inventory::SupportsPotionBeltCasting(int16 slot_id) +{ + if ((uint16)slot_id <= EmuConstants::GENERAL_END || slot_id == MainPowerSource || (slot_id >= EmuConstants::GENERAL_BAGS_BEGIN && slot_id <= EmuConstants::GENERAL_BAGS_END)) + return true; + + return false; +} + // Test whether a given slot can support a container item bool Inventory::SupportsContainers(int16 slot_id) { diff --git a/common/item.h b/common/item.h index c24c708b1..9be660117 100644 --- a/common/item.h +++ b/common/item.h @@ -121,8 +121,22 @@ public: // Public Methods /////////////////////////////// + Inventory() { m_version = EQClientUnknown; m_versionset = false; } ~Inventory(); + // Inventory v2 creep + bool SetInventoryVersion(EQClientVersion version) { + if (!m_versionset) { + m_version = version; + return (m_versionset = true); + } + else { + return false; + } + } + + EQClientVersion GetInventoryVersion() { return m_version; } + static void CleanDirty(); static void MarkDirty(ItemInst *inst); @@ -132,7 +146,7 @@ public: inline iter_queue cursor_begin() { return m_cursor.begin(); } inline iter_queue cursor_end() { return m_cursor.end(); } - inline bool CursorEmpty() { return (m_cursor.size() == 0); } + inline bool CursorEmpty() { return (m_cursor.size() == 0); } // Retrieve a read-only item from inventory inline const ItemInst* operator[](int16 slot_id) const { return GetItem(slot_id); } @@ -183,6 +197,10 @@ public: static bool CanItemFitInContainer(const Item_Struct *ItemToTry, const Item_Struct *Container); + // Test for valid inventory casting slot + bool SupportsClickCasting(int16 slot_id); + bool SupportsPotionBeltCasting(int16 slot_id); + // Test whether a given slot can support a container item static bool SupportsContainers(int16 slot_id); @@ -229,7 +247,12 @@ protected: std::map m_bank; // Items in character bank std::map m_shbank; // Items in character shared bank std::map m_trade; // Items in a trade session - ItemInstQueue m_cursor; // Items on cursor: FIFO + ItemInstQueue m_cursor; // Items on cursor: FIFO + +private: + // Active inventory version + EQClientVersion m_version; + bool m_versionset; }; class SharedDatabase; diff --git a/common/mail_oplist.h b/common/mail_oplist.h index f10e62be0..29cb859e0 100644 --- a/common/mail_oplist.h +++ b/common/mail_oplist.h @@ -1,13 +1,13 @@ //Mail and Chat Channels -N(OP_MailLogin), -N(OP_Mail), +N(OP_Buddy), N(OP_ChannelAnnounceJoin), N(OP_ChannelAnnounceLeave), -N(OP_Buddy), -N(OP_MailHeaderCount), -N(OP_MailHeader), -N(OP_MailSendBody), -N(OP_MailNew), -N(OP_MailDeliveryStatus), -N(OP_MailboxChange), N(OP_Ignore), +N(OP_Mail), +N(OP_MailboxChange), +N(OP_MailDeliveryStatus), +N(OP_MailHeader), +N(OP_MailHeaderCount), +N(OP_MailLogin), +N(OP_MailNew), +N(OP_MailSendBody), diff --git a/common/misc_functions.cpp b/common/misc_functions.cpp index e7b581733..882cc61c7 100644 --- a/common/misc_functions.cpp +++ b/common/misc_functions.cpp @@ -387,3 +387,26 @@ float EQHtoFloat(int d) { return(360.0f - float((d * 360) >> 11)); } + +// returns a swapped-bit value for use in client translator and inventory functions +uint32 SwapBits21and22(uint32 mask) +{ + static const uint32 BIT21 = 1 << 21; + static const uint32 BIT22 = 1 << 22; + static const uint32 SWAPBITS = (BIT21 | BIT22); + + if ((bool)(mask & BIT21) != (bool)(mask & BIT22)) + mask ^= SWAPBITS; + + return mask; +} + +// returns an unset bit 22 value for use in client translators +uint32 Catch22(uint32 mask) +{ + static const uint32 KEEPBITS = ~(1 << 22); + + mask &= KEEPBITS; + + return mask; +} diff --git a/common/misc_functions.h b/common/misc_functions.h index c057af8d8..abc9747b5 100644 --- a/common/misc_functions.h +++ b/common/misc_functions.h @@ -102,6 +102,8 @@ int FloatToEQ13(float d); int NewFloatToEQ13(float d); int FloatToEQ19(float d); int FloatToEQH(float d); +uint32 SwapBits21and22(uint32 mask); +uint32 Catch22(uint32 mask); // macro to catch fp errors (provided by noudness) #define FCMP(a,b) (fabs(a-b) < FLT_EPSILON) diff --git a/common/mysql_request_result.cpp b/common/mysql_request_result.cpp index ffbc3ef40..e7f147b51 100644 --- a/common/mysql_request_result.cpp +++ b/common/mysql_request_result.cpp @@ -10,7 +10,7 @@ MySQLRequestResult::MySQLRequestResult() MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer) : m_CurrentRow(result), m_OneBeyondRow() { - m_Result = result; + m_Result = result; m_RowsAffected = rowsAffected; m_RowCount = rowCount; m_ColumnCount = columnCount; @@ -22,16 +22,17 @@ MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, u m_ColumnLengths = nullptr; m_Fields = nullptr; - if (errorBuffer != nullptr) + m_Success = true; + if (errorBuffer != nullptr) m_Success = false; - m_Success = true; - m_ErrorNumber = errorNumber; - m_ErrorBuffer = errorBuffer; + m_ErrorNumber = errorNumber; + m_ErrorBuffer = errorBuffer; } void MySQLRequestResult::FreeInternals() { + safe_delete_array(m_ErrorBuffer); if (m_Result != nullptr) @@ -100,6 +101,7 @@ MySQLRequestResult::MySQLRequestResult(MySQLRequestResult&& moveItem) m_RowsAffected = moveItem.m_RowsAffected; m_LastInsertedID = moveItem.m_LastInsertedID; m_ColumnLengths = moveItem.m_ColumnLengths; + m_ColumnCount = moveItem.m_ColumnCount; m_Fields = moveItem.m_Fields; // Keeps deconstructor from double freeing @@ -127,6 +129,7 @@ MySQLRequestResult& MySQLRequestResult::operator=(MySQLRequestResult&& other) m_CurrentRow = other.m_CurrentRow; m_OneBeyondRow = other.m_OneBeyondRow; m_ColumnLengths = other.m_ColumnLengths; + m_ColumnCount = other.m_ColumnCount; m_Fields = other.m_Fields; // Keeps deconstructor from double freeing diff --git a/common/opcode_dispatch.h b/common/opcode_dispatch.h index 352fc62ef..46e8b9099 100644 --- a/common/opcode_dispatch.h +++ b/common/opcode_dispatch.h @@ -305,7 +305,8 @@ OUTz(OP_FinishWindow2); //OUTv(OP_AdventureInfo, strlen(p)+1); //OUTv(OP_AdventureMerchantResponse, strlen(msg)+2); OUTv(OP_ItemPacket, ItemPacket_Struct); -OUTv(OP_BuffFadeMsg, BuffFadeMsg_Struct); +OUTv(OP_ColoredText, ColoredText_Struct); +OUTv(OP_ItemRecastDelay, ItemRecastDelay_Struct); OUTv(OP_FormattedMessage, FormattedMessage_Struct); OUTv(OP_GuildMemberList, uint32); //variable length, but nasty OUTv(OP_InterruptCast, InterruptCast_Struct); diff --git a/common/opcode_map.cpp b/common/opcode_map.cpp index 10babaccb..edb6ba64c 100644 --- a/common/opcode_map.cpp +++ b/common/opcode_map.cpp @@ -160,7 +160,7 @@ void load_opcode_names() opcode_map[0x0192]="LiveOP_YellForHelp"; opcode_map[0x00ef]="LiveOP_SafePoint"; opcode_map[0x0157]="LiveOP_Buff"; - opcode_map[0x00c0]="LiveOP_BuffFadeMsg"; + opcode_map[0x00c0]="LiveOP_ColoredText"; opcode_map[0x0440]="LiveOP_MultiLineMsg"; opcode_map[0x021c]="LiveOP_SpecialMesg"; opcode_map[0x0013]="LiveOP_Consent"; diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp index 10cc331ec..d734a29ec 100644 --- a/common/patches/client62.cpp +++ b/common/patches/client62.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "client62.h" #include "../opcodemgr.h" @@ -13,1077 +12,1329 @@ #include "../clientversions.h" #include "client62_structs.h" -namespace Client62 { +namespace Client62 +{ + static const char *name = "6.2"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "6.2"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth); -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline int16 ServerToClient62Slot(uint32 ServerSlot); + static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 Client62ToServerSlot(int16 Client62Slot); + static inline uint32 Client62ToServerCorpseSlot(int16 Client62Corpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "client62_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "client62_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClient62; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClient62; + } #include "ss_define.h" +// ENCODE methods + EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ; -/* -// Converts Server Slot IDs to Client62 Slot IDs for use in Encodes -static inline uint32 ServerToClient62Slot(uint32 ServerSlot) { - uint32 Client62Slot; - // reserved -} -*/ -/* -// Converts Client62 Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 Client62ToServerSlot(uint32 Client62Slot) { - uint32 ServerSlot; - // reserved -} -*/ -/* -// Converts Server Corpse Slot IDs to Client62 Corpse Slot IDs for use in Encodes -static inline uint32 ServerToClient62CorpseSlot(uint32 ServerCorpse) { - uint32 Client62Corpse; - // reserved -} -*/ -/* -// Converts Client62 Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 Client62ToServerCorpseSlot(uint32 Client62Corpse) { - uint32 ServerCorpse; - // reserved -} -*/ -/* -static inline uint32 RemovePowerSourceBit(uint32 slots) { // shouldn't need to add one..just grab the actual server reference, if so... - static const uint32 BIT21 = 1 << 21; - static const uint32 BIT22 = 1 << 22; - static const uint32 KEEPBITS = ~(BIT21 | BIT22); - - bool wearammo = slots & BIT22; - - slots &= KEEPBITS; - - if (wearammo) - slots |= BIT21; - - return slots; -} -*/ - - -EAT_ENCODE(OP_ZoneServerReady) -EAT_ENCODE(OP_GuildMemberLevelUpdate) - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); - int r; - for(r = 0; r < 10; r++) { - OUT(zone[r]); - OUT(eyecolor1[r]); - OUT(eyecolor2[r]); - OUT(hairstyle[r]); - OUT(primary[r]); - OUT(race[r]); - OUT(class_[r]); - OUT_str(name[r]); - OUT(gender[r]); - OUT(level[r]); - OUT(secondary[r]); - OUT(face[r]); - OUT(beard[r]); - int k; - for(k = 0; k < 9; k++) { - OUT(equip[r][k]); - OUT(cs_colors[r][k].color); - } - OUT(haircolor[r]); - OUT(gohome[r]); - OUT(deity[r]); - OUT(beardcolor[r]); - } - FINISH_ENCODE(); -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 2 is for 6.2 - if (emu->clientver <= 2 ) + EAT_ENCODE(OP_ZoneServerReady); // added ; + + ENCODE(OP_Action) { - OUT(id); - OUT(hotkey_sid); - OUT(hotkey_sid2); - OUT(title_sid); - OUT(desc_sid); - OUT(class_type); - OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + OUT(sequence); OUT(type); - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - OUT(unknown80[0]); - OUT(unknown80[1]); - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + //OUT(damage); + OUT(spell); + OUT(buff_unknown); // if this is 4, a buff icon is made + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} - -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteSpawn) { - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - memset(eq->unknown3224, 0xff, 448); - memset(eq->unknown3704, 0xff, 32); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); - OUT(level); - eq->level2 = emu->level; - - eq->bind_zone_id = emu->binds[0].zoneId; - eq->bind_x[0] = emu->binds[0].x; - eq->bind_y[0] = emu->binds[0].y; - eq->bind_z[0] = emu->binds[0].z; - eq->bind_heading[0] = emu->binds[0].heading; - //just making this up base on organization of struct: - eq->zone_safe_x = emu->binds[4].x; - eq->zone_safe_y = emu->binds[4].y; - eq->zone_safe_z = emu->binds[4].z; - eq->zone_safe_heading = emu->binds[4].heading; - - - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); - for(r = 0; r < 9; r++) { - OUT(item_material[r]); - OUT(item_tint[r].color); - } - for(r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } - OUT(points); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(DEX); - OUT(INT); - OUT(AGI); - OUT(WIS); - OUT(face); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); -// OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } -// OUT_array(recastTimers, structs::MAX_RECAST_TYPES); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(available_slots); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(exp); - OUT_array(languages, structs::MAX_PP_LANGUAGE); - OUT(x); - OUT(y); - OUT(z); - OUT(heading); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); - OUT(expansions); - OUT(autosplit); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } -// OUT_str(groupLeader); //this is prolly right after groupMembers, but I dont feel like checking. -// OUT(leadAAActive); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); - OUT(tribute_time_remaining); - OUT(career_tribute_points); - OUT(tribute_points); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } - OUT(group_leadership_exp); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); - OUT(air_remaining); - OUT(entityid); - OUT(leadAAActive); - OUT(expAA); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(showhelm); - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_Track) -{ - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_AdventureMerchantSell) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToClient62Slot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); } - in->size = sizeof(structs::Track_Struct) * EntryCount; - in->pBuffer = new unsigned char[in->size]; - structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++eq, ++emu) + ENCODE(OP_ApplyPoison) { - OUT(entityid); - OUT(padding002); - OUT(distance); + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToClient62Slot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_BazaarSearch) + { + EQApplicationPacket *in = *p; + *p = nullptr; - dest->FastQueuePacket(&in, ack_req); -} + char *Buffer = (char *)in->pBuffer; -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); - //determine and verify length - int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); - delete in; - return; - } - - //make the EQ struct. - in->size = sizeof(structs::Spawn_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - //do the transform... - int r; - int k; - for(r = 0; r < entrycount; r++, eq++, emu++) { - eq->gm = emu->gm; - eq->aaitle = emu->aaitle; - eq->anon = emu->anon; - eq->face = emu->face; - strcpy(eq->name, emu->name); - eq->deity = emu->deity; - eq->size = emu->size; - eq->NPC = emu->NPC; - eq->invis = emu->invis; - eq->haircolor = emu->haircolor; - eq->curHp = emu->curHp; - eq->max_hp = emu->max_hp; - eq->findable = emu->findable; - eq->deltaHeading = emu->deltaHeading; - eq->x = emu->x; - eq->y = emu->y; - eq->animation = emu->animation; - eq->z = emu->z; - eq->deltaY = emu->deltaY; - eq->deltaX = emu->deltaX; - eq->heading = emu->heading; - eq->deltaZ = emu->deltaZ; - eq->eyecolor1 = emu->eyecolor1; -// eq->showhelm = emu->showhelm; - eq->is_npc = emu->is_npc; - eq->hairstyle = emu->hairstyle; - eq->beard = emu->beard; - eq->level = emu->level; - eq->beardcolor = emu->beardcolor; - strcpy(eq->suffix, emu->suffix); - eq->petOwnerId = emu->petOwnerId; - eq->guildrank = emu->guildrank; - for(k = 0; k < 9; k++) { - eq->equipment[k] = emu->equipment[k]; - eq->colors[k].color = emu->colors[k].color; - } - for(k = 0; k < 8; k++) { - eq->set_to_0xFF[k] = 0xFF; - } - eq->runspeed = emu->runspeed; - eq->afk = emu->afk; - eq->guildID = emu->guildID; - strcpy(eq->title, emu->title); - eq->helm = emu->helm; - eq->race = emu->race; - strcpy(eq->lastName, emu->lastName); - eq->walkspeed = emu->walkspeed; - eq->is_pet = emu->is_pet; - eq->light = emu->light; - eq->class_ = emu->class_; - eq->eyecolor2 = emu->eyecolor2; - eq->gender = emu->gender; - eq->bodytype = emu->bodytype; - eq->equip_chest2 = emu->equip_chest2; - eq->spawnId = emu->spawnId; - eq->lfg = emu->lfg; - eq->flymode = emu->flymode; - } - - //kill off the emu structure and send the eq packet. - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((const ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+5; // ItemPacketType + Serialization + \0 - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length+1); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int itemcount = in->size / sizeof(InternalSerializedItem_Struct); - if(itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); - delete in; - return; - } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - //do the transform... - int r; - std::string serial_string; - for(r = 0; r < itemcount; r++, eq++) { - uint32 length; - char *serialized=SerializeItem((ItemInst*)eq->inst,eq->slot_id,&length,0); - if (serialized) { - serial_string.append(serialized,length+1); - safe_delete_array(serialized); - } else { - _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); + return; } + unsigned char *__emu_buffer = in->pBuffer; + + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + + in->pBuffer = new unsigned char[in->size]; + + memset(in->pBuffer, 0, in->size); + + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(NumItems); + OUT(SerialNumber); + OUT(SellerID); + OUT(Cost); + OUT(ItemStat); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); } - in->size = serial_string.length(); - in->pBuffer = new unsigned char[in->size]; - memcpy(in->pBuffer,serial_string.c_str(),serial_string.length()); + ENCODE(OP_BecomeTrader) + { + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} + OUT(ID); + OUT(Code); -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; + FINISH_ENCODE(); + } - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + int itemcount = in->size / sizeof(InternalSerializedItem_Struct); + if (itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + delete in; + return; + } + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. + //do the transform... + int r; + std::string serial_string; + for (r = 0; r < itemcount; r++, eq++) { + uint32 length; + char *serialized = SerializeItem((ItemInst*)eq->inst, eq->slot_id, &length, 0); + if (serialized) { + serial_string.append(serialized, length + 1); + safe_delete_array(serialized); + } + else { + _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } - uint8 *buffer; - buffer = in->pBuffer; + } - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; + in->size = serial_string.length(); + in->pBuffer = new unsigned char[in->size]; + memcpy(in->pBuffer, serial_string.c_str(), serial_string.length()); - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToClient62Slot(emu->from_slot); + eq->to_slot = ServerToClient62Slot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteSpawn) + { + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count * sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data ); - const char *emu_note = (emu_name + + const char *emu_note = (emu_name + emu->name_length + //skip name contents emu->count //skip string terminators ); - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { - //the order we set things here must match the struct + //the order we set things here must match the struct - //nice helper macro - /*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ #define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); #undef SlideStructString #undef PutFieldN - e++; + e++; + } } - } - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ReadBook) { - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; - - in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); - - in->pBuffer = new unsigned char[in->size]; - - structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; - - eq_BookText_Struct->window = emu_BookText_Struct->window; - eq_BookText_Struct->type = emu_BookText_Struct->type; - strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - if(emu->race > 473){ - eq->race = 1; - } - else { - OUT(race); - } - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - - FINISH_ENCODE(); -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) - { + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - - return; } - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + ENCODE(OP_Illusion) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + + if (emu->race > 473) + eq->race = 1; + else + OUT(race); + + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + + FINISH_ENCODE(); } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - in->pBuffer = new unsigned char[in->size]; + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) + ENCODE(OP_ItemPacket) { - OUT(Beginning.Action); - OUT(NumItems); - OUT(SerialNumber); - OUT(SellerID); - OUT(Cost); - OUT(ItemStat); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((const ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 5; // ItemPacketType + Serialization + \0 + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length + 1); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); } - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_RespondAA) { - ENCODE_LENGTH_EXACT(AATable_Struct); - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - unsigned int r; - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_list[r].aa_skill); - OUT(aa_list[r].aa_value); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) { - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - OUT(sequence); - OUT(type); - //OUT(damage); - OUT(spell); - OUT(buff_unknown); // if this is 4, a buff icon is made - FINISH_ENCODE(); -} - -ENCODE(OP_BecomeTrader) -{ - ENCODE_LENGTH_EXACT(BecomeTrader_Struct); - SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - OUT(ID); - OUT(Code); - FINISH_ENCODE(); -} - -ENCODE(OP_PetBuffWindow) -{ - ENCODE_LENGTH_EXACT(PetBuff_Struct); - SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); - - OUT(petid); - OUT(buffcount); - - int EQBuffSlot = 0; - - for(uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) + ENCODE(OP_LeadershipExpUpdate) { - if(emu->spellid[EmuBuffSlot]) + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = ServerToClient62CorpseSlot(emu->slot_id); + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToClient62Slot(emu->from_slot); + eq->to_slot = ServerToClient62Slot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + OUT_str(Title); + OUT_str(Text); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + + eq->unknown4236 = 0x00000000; + eq->unknown4240 = 0xffffffff; + + FINISH_ENCODE(); + } + + ENCODE(OP_PetBuffWindow) + { + ENCODE_LENGTH_EXACT(PetBuff_Struct); + SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); + + OUT(petid); + OUT(buffcount); + + int EQBuffSlot = 0; + + for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) { - eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; - eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + if (emu->spellid[EmuBuffSlot]) + { + eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; + eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + } } + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - OUT_str(Title); - OUT_str(Text); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); + uint32 r; - eq->unknown4236 = 0x00000000; - eq->unknown4240 = 0xffffffff; + memset(eq->unknown3224, 0xff, 448); + memset(eq->unknown3704, 0xff, 32); - FINISH_ENCODE(); -} + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + OUT(level); + eq->level2 = emu->level; -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(color.color); - IN(wear_slot_id); - FINISH_DIRECT_DECODE(); -} + eq->bind_zone_id = emu->binds[0].zoneId; + eq->bind_x[0] = emu->binds[0].x; + eq->bind_y[0] = emu->binds[0].y; + eq->bind_z[0] = emu->binds[0].z; + eq->bind_heading[0] = emu->binds[0].heading; + //just making this up base on organization of struct: + eq->zone_safe_x = emu->binds[4].x; + eq->zone_safe_y = emu->binds[4].y; + eq->zone_safe_z = emu->binds[4].z; + eq->zone_safe_heading = emu->binds[4].heading; -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 25; r++) { - IN(filters[r]); - } - emu->filters[25] = 1; - emu->filters[26] = 1; - emu->filters[27] = 1; - emu->filters[28] = 1; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(haircolor); - IN(gender); - IN(race); - IN(start_zone); - IN(hairstyle); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - emu->type = 3; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(window); - IN(type); - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - - FINISH_DIRECT_DECODE(); -} - -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) { - char *serialization = nullptr; - char *instance = nullptr; - const char *protection=(const char *)"\\\\\\\\\\"; - char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; - bool stackable=inst->IsStackable(); - uint32 merchant_slot=inst->GetMerchantSlot(); - int16 charges=inst->GetCharges(); - const Item_Struct *item=inst->GetItem(); - int i; - uint32 sub_length; - - MakeAnyLenString(&instance, - "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", - stackable ? charges : 1, - 0, - (merchant_slot==0) ? slot_id : merchant_slot, - inst->GetPrice(), - (merchant_slot==0) ? 1 : inst->GetMerchantCount(), - 0, - //merchant_slot, //instance ID, bullshit for now - // The 'Merchant Slot' needs to be some unique id for bazaar to work properly - (merchant_slot==0) ? inst->GetSerialNumber() : merchant_slot, - inst->IsInstNoDrop() ? 1 : 0, //not sure where this field is - (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? charges : 0) : charges), - 0 - ); - - for(i=0;i<10;i++) { - ItemInst *sub=inst->GetItem(i); - if (sub) { - sub_items[i]=SerializeItem(sub,0,&sub_length,depth+1); + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + for (r = 0; r < 9; r++) { + OUT(item_material[r]); + OUT(item_tint[r].color); } + for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + OUT(points); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(DEX); + OUT(INT); + OUT(AGI); + OUT(WIS); + OUT(face); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + // OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + // OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(available_slots); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(exp); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + OUT(x); + OUT(y); + OUT(z); + OUT(heading); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + OUT(expansions); + OUT(autosplit); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + // OUT_str(groupLeader); //this is prolly right after groupMembers, but I dont feel like checking. + // OUT(leadAAActive); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + OUT(tribute_points); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + OUT(group_leadership_exp); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + OUT(air_remaining); + OUT(entityid); + OUT(leadAAActive); + OUT(expAA); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(showhelm); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); } + ENCODE(OP_ReadBook) + { + // no apparent slot translation needed -U + EQApplicationPacket *in = *p; + *p = nullptr; - *length=MakeAnyLenString(&serialization, - "%.*s%s" // For leading quotes (and protection) if a subitem; - "%s" // Instance data - "%.*s\"" // Quotes (and protection, if needed) around static data - "%i" // item->ItemClass so we can do |%s instead of %s| + unsigned char *__emu_buffer = in->pBuffer; + + BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; + + in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); + + in->pBuffer = new unsigned char[in->size]; + + structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; + + eq_BookText_Struct->window = emu_BookText_Struct->window; + eq_BookText_Struct->type = emu_BookText_Struct->type; + strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_RespondAA) + { + ENCODE_LENGTH_EXACT(AATable_Struct); + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + unsigned int r; + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_list[r].aa_skill); + OUT(aa_list[r].aa_value); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 2 is for 6.2 + if (emu->clientver <= 2) + { + OUT(id); + OUT(hotkey_sid); + OUT(hotkey_sid2); + OUT(title_sid); + OUT(desc_sid); + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + OUT(type); + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + OUT(unknown80[0]); + OUT(unknown80[1]); + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); + + int r; + for (r = 0; r < 10; r++) { + OUT(zone[r]); + OUT(eyecolor1[r]); + OUT(eyecolor2[r]); + OUT(hairstyle[r]); + OUT(primary[r]); + OUT(race[r]); + OUT(class_[r]); + OUT_str(name[r]); + OUT(gender[r]); + OUT(level[r]); + OUT(secondary[r]); + OUT(face[r]); + OUT(beard[r]); + int k; + for (k = 0; k < 9; k++) { + OUT(equip[r][k]); + OUT(cs_colors[r][k].color); + } + OUT(haircolor[r]); + OUT(gohome[r]); + OUT(deity[r]); + OUT(beardcolor[r]); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToClient62Slot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + in->size = sizeof(structs::Track_Struct) * EntryCount; + in->pBuffer = new unsigned char[in->size]; + structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++eq, ++emu) + { + OUT(entityid); + OUT(padding002); + OUT(distance); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToClient62Slot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(Spawn_Struct); + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::Spawn_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + //do the transform... + int r; + int k; + for (r = 0; r < entrycount; r++, eq++, emu++) { + eq->gm = emu->gm; + eq->aaitle = emu->aaitle; + eq->anon = emu->anon; + eq->face = emu->face; + strcpy(eq->name, emu->name); + eq->deity = emu->deity; + eq->size = emu->size; + eq->NPC = emu->NPC; + eq->invis = emu->invis; + eq->haircolor = emu->haircolor; + eq->curHp = emu->curHp; + eq->max_hp = emu->max_hp; + eq->findable = emu->findable; + eq->deltaHeading = emu->deltaHeading; + eq->x = emu->x; + eq->y = emu->y; + eq->animation = emu->animation; + eq->z = emu->z; + eq->deltaY = emu->deltaY; + eq->deltaX = emu->deltaX; + eq->heading = emu->heading; + eq->deltaZ = emu->deltaZ; + eq->eyecolor1 = emu->eyecolor1; + // eq->showhelm = emu->showhelm; + eq->is_npc = emu->is_npc; + eq->hairstyle = emu->hairstyle; + eq->beard = emu->beard; + eq->level = emu->level; + eq->beardcolor = emu->beardcolor; + strcpy(eq->suffix, emu->suffix); + eq->petOwnerId = emu->petOwnerId; + eq->guildrank = emu->guildrank; + for (k = 0; k < 9; k++) { + eq->equipment[k] = emu->equipment[k]; + eq->colors[k].color = emu->colors[k].color; + } + for (k = 0; k < 8; k++) { + eq->set_to_0xFF[k] = 0xFF; + } + eq->runspeed = emu->runspeed; + eq->afk = emu->afk; + eq->guildID = emu->guildID; + strcpy(eq->title, emu->title); + eq->helm = emu->helm; + eq->race = emu->race; + strcpy(eq->lastName, emu->lastName); + eq->walkspeed = emu->walkspeed; + eq->is_pet = emu->is_pet; + eq->light = emu->light; + eq->class_ = emu->class_; + eq->eyecolor2 = emu->eyecolor2; + eq->gender = emu->gender; + eq->bodytype = emu->bodytype; + eq->equip_chest2 = emu->equip_chest2; + eq->spawnId = emu->spawnId; + eq->lfg = emu->lfg; + eq->flymode = emu->flymode; + } + + //kill off the emu structure and send the eq packet. + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + +// DECODE methods + DECODE(OP_AdventureMerchantSell) + { + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + IN(npcid); + emu->slot = Client62ToServerSlot(eq->slot); + IN(charges); + IN(sell_price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = Client62ToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = Client62ToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = Client62ToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(haircolor); + IN(gender); + IN(race); + IN(start_zone); + IN(hairstyle); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(tutorial); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = Client62ToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = Client62ToServerSlot(eq->from_slot); + emu->to_slot = Client62ToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = Client62ToServerCorpseSlot(eq->slot_id); + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = Client62ToServerSlot(eq->from_slot); + emu->to_slot = Client62ToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ReadBook) + { + // no apparent slot translation needed -U + DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(window); + IN(type); + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 25; r++) { + IN(filters[r]); + } + emu->filters[25] = 1; + emu->filters[26] = 1; + emu->filters[27] = 1; + emu->filters[28] = 1; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = Client62ToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = Client62ToServerSlot(eq->container_slot); + IN(guildtribute_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = Client62ToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + IN(spawn_id); + IN(material); + IN(color.color); + IN(wear_slot_id); + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + emu->type = 3; + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + char *SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + char *serialization = nullptr; + char *instance = nullptr; + const char *protection = (const char *)"\\\\\\\\\\"; + char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + bool stackable = inst->IsStackable(); + int16 slot_id = ServerToClient62Slot(slot_id_in); + uint32 merchant_slot = inst->GetMerchantSlot(); + int16 charges = inst->GetCharges(); + const Item_Struct *item = inst->GetItem(); + int i; + uint32 sub_length; + + // not sure if 6.2 has a recast timer timestamp field..but, something seems amiss between this and Ti's ordering + MakeAnyLenString(&instance, + "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", + stackable ? charges : 1, + 0, + //(merchant_slot == 0) ? slot_id : merchant_slot, // change when translator activated + (merchant_slot == 0) ? slot_id_in : merchant_slot, + inst->GetPrice(), + (merchant_slot == 0) ? 1 : inst->GetMerchantCount(), + 0, + //merchant_slot, //instance ID, bullshit for now + // The 'Merchant Slot' needs to be some unique id for bazaar to work properly + (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot, + inst->IsInstNoDrop() ? 1 : 0, //not sure where this field is + (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? charges : 0) : charges), + 0 + ); + + for (i = 0; i<10; i++) { + ItemInst *sub = inst->GetItem(i); + if (sub) { + sub_items[i] = SerializeItem(sub, 0, &sub_length, depth + 1); + } + } + + + *length = MakeAnyLenString(&serialization, + "%.*s%s" // For leading quotes (and protection) if a subitem; + "%s" // Instance data + "%.*s\"" // Quotes (and protection, if needed) around static data + "%i" // item->ItemClass so we can do |%s instead of %s| #define I(field) "|%i" #define C(field) "|%s" #define S(field) "|%s" #define F(field) "|%f" #include "client62_itemfields.h" - "%.*s\"" // Quotes (and protection, if needed) around static data - "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items - "%.*s%s" // For trailing quotes (and protection) if a subitem; - ,depth ? depth-1 : 0,protection,(depth) ? "\"" : "" - ,instance - ,depth,protection - ,item->ItemClass + "%.*s\"" // Quotes (and protection, if needed) around static data + "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items + "%.*s%s" // For trailing quotes (and protection) if a subitem; + , depth ? depth - 1 : 0, protection, (depth) ? "\"" : "" + , instance + , depth, protection + , item->ItemClass #define I(field) ,item->field #define C(field) ,field #define S(field) ,item->field #define F(field) ,item->field #include "client62_itemfields.h" - ,depth,protection - ,sub_items[0] ? sub_items[0] : "" - ,sub_items[1] ? sub_items[1] : "" - ,sub_items[2] ? sub_items[2] : "" - ,sub_items[3] ? sub_items[3] : "" - ,sub_items[4] ? sub_items[4] : "" - ,sub_items[5] ? sub_items[5] : "" - ,sub_items[6] ? sub_items[6] : "" - ,sub_items[7] ? sub_items[7] : "" - ,sub_items[8] ? sub_items[8] : "" - ,sub_items[9] ? sub_items[9] : "" - ,(depth) ? depth-1 : 0,protection,(depth) ? "\"" : "" - ); + , depth, protection + , sub_items[0] ? sub_items[0] : "" + , sub_items[1] ? sub_items[1] : "" + , sub_items[2] ? sub_items[2] : "" + , sub_items[3] ? sub_items[3] : "" + , sub_items[4] ? sub_items[4] : "" + , sub_items[5] ? sub_items[5] : "" + , sub_items[6] ? sub_items[6] : "" + , sub_items[7] ? sub_items[7] : "" + , sub_items[8] ? sub_items[8] : "" + , sub_items[9] ? sub_items[9] : "" + , (depth) ? depth - 1 : 0, protection, (depth) ? "\"" : "" + ); - for(i=0;i<10;i++) { - if (sub_items[i]) - safe_delete_array(sub_items[i]); + for (i = 0; i < 10; i++) { + if (sub_items[i]) + safe_delete_array(sub_items[i]); + } + + safe_delete_array(instance); + + return serialization; } - safe_delete_array(instance); + static inline int16 ServerToClient62Slot(uint32 ServerSlot) + { + //int16 Client62Slot; + if (ServerSlot == INVALID_INDEX) + return INVALID_INDEX; - return serialization; + return ServerSlot; // deprecated + } + + static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse) + { + //int16 Client62Corpse; + return ServerCorpse; + } + + static inline uint32 Client62ToServerSlot(int16 Client62Slot) + { + //uint32 ServerSlot; + if (Client62Slot == INVALID_INDEX) + return INVALID_INDEX; + + return Client62Slot; // deprecated + } + + static inline uint32 Client62ToServerCorpseSlot(int16 Client62Corpse) + { + //uint32 ServerCorpse; + return Client62Corpse; + } } - - -} //end namespace Client62 - - - - - - +// end namespace Client62 diff --git a/common/patches/client62_constants.h b/common/patches/client62_constants.h index e8eb38faa..0e672a5de 100644 --- a/common/patches/client62_constants.h +++ b/common/patches/client62_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef CLIENT62_CONSTANTS_H_ #define CLIENT62_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace Client62 { namespace maps { @@ -180,6 +180,7 @@ namespace Client62 { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/client62_ops.h b/common/patches/client62_ops.h index 5f1a1b4a0..eb7ff2205 100644 --- a/common/patches/client62_ops.h +++ b/common/patches/client62_ops.h @@ -1,36 +1,54 @@ - -//list of packets we need to encode on the way out: +// out-going packets that require an ENCODE translation: +E(OP_Action) +E(OP_AdventureMerchantSell) +E(OP_ApplyPoison) +E(OP_BazaarSearch) +E(OP_BecomeTrader) +E(OP_CharInventory) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DeleteSpawn) +E(OP_GuildMemberLevelUpdate) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_LeadershipExpUpdate) +E(OP_LootItem) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_OnLevelMessage) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_ReadBook) +E(OP_RespondAA) E(OP_SendAATable) E(OP_SendCharInfo) -E(OP_LeadershipExpUpdate) -E(OP_PlayerProfile) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ZoneEntry) -E(OP_ItemPacket) -E(OP_ItemLinkResponse) -E(OP_CharInventory) -E(OP_GuildMemberList) -E(OP_ZoneServerReady) -E(OP_GuildMemberLevelUpdate) -E(OP_ReadBook) -E(OP_Illusion) +E(OP_ShopPlayerSell) E(OP_Track) -E(OP_BazaarSearch) -E(OP_RespondAA) -E(OP_DeleteSpawn) +E(OP_TributeItem) E(OP_WearChange) -E(OP_Action) -E(OP_BecomeTrader) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) +E(OP_ZoneEntry) +E(OP_ZoneServerReady) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_ApplyPoison) +D(OP_AugmentItem) +D(OP_CastSpell) D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_WhoAllRequest) -D(OP_ReadBook) +D(OP_Consume) +D(OP_DeleteItem) D(OP_FaceChange) +D(OP_ItemLinkClick) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_ReadBook) +D(OP_SetServerFilter) +D(OP_ShopPlayerSell) +D(OP_TradeSkillCombine) +D(OP_TributeItem) D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/client62_structs.h b/common/patches/client62_structs.h index 27b808620..aae4c7919 100644 --- a/common/patches/client62_structs.h +++ b/common/patches/client62_structs.h @@ -406,7 +406,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -558,7 +558,7 @@ struct CharCreate_Struct /*0128*/ uint32 face; /*0132*/ uint32 eyecolor1; //its possiable we could have these switched /*0136*/ uint32 eyecolor2; //since setting one sets the other we really can't check -/*0140*/ uint32 unknown140; +/*0140*/ uint32 tutorial; //assumptions are bad! But guessed }; /* @@ -611,14 +611,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /* @@ -1148,19 +1196,27 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; }; +struct DeleteItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ +}; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; // @@ -1362,12 +1418,6 @@ struct CombatAbility_Struct { uint32 m_skill; }; -struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; -}; - //Instill Doubt struct Instill_Doubt_Struct { uint8 i_id; @@ -1521,6 +1571,14 @@ struct Adventure_Purchase_Struct { /*008*/ uint32 variable; }; +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 +/*004*/ uint32 npcid; +/*008*/ uint32 slot; +/*012*/ uint32 charges; +/*016*/ uint32 sell_price; +}; + struct AdventurePoints_Update_Struct { /*000*/ uint32 ldon_available_points; // Total available points /*004*/ uint8 unkown_apu004[20]; @@ -2524,10 +2582,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -2563,7 +2621,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -2969,7 +3027,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; @@ -3076,6 +3134,11 @@ struct AnnoyingZoneUnknown_Struct { uint32 value; //always 4 }; +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + struct GuildMemberUpdate_Struct { /*00*/ uint32 guild_id; //not sure /*04*/ char member_name[64]; @@ -3084,11 +3147,6 @@ struct GuildMemberUpdate_Struct { /*72*/ uint32 unknown072; }; - - - - - }; //end namespace structs }; //end namespace Client62 diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index d5303ccfb..3a03b3a62 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -15,542 +15,1705 @@ #include #include -namespace RoF { +namespace RoF +{ + static const char *name = "RoF"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "RoF"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline structs::ItemSlotStruct ServerToRoFSlot(uint32 ServerSlot); + static inline structs::MainInvItemSlotStruct ServerToRoFMainInvSlot(uint32 ServerSlot); + static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 RoFToServerSlot(structs::ItemSlotStruct RoFSlot); + static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot); + static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "rof_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "rof_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientRoF; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientRoF; + } #include "ss_define.h" +// ENCODE methods + ENCODE(OP_Action) + { + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); -// Converts Server Slot IDs to RoF Slot IDs for use in Encodes -static inline structs::ItemSlotStruct ServerToRoFSlot(uint32 ServerSlot) { - structs::ItemSlotStruct RoFSlot; - RoFSlot.SlotType = INVALID_INDEX; - RoFSlot.Unknown02 = NOT_USED; - RoFSlot.MainSlot = INVALID_INDEX; - RoFSlot.SubSlot = INVALID_INDEX; - RoFSlot.AugSlot = INVALID_INDEX; - RoFSlot.Unknown01 = NOT_USED; + OUT(target); + OUT(source); + OUT(level); + eq->unknown06 = 0; + eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; + eq->bard_focus_id = emu->bard_focus_id; + eq->knockback_angle = emu->sequence; + eq->unknown22 = 0; + OUT(type); + eq->damage = 0; + eq->unknown31 = 0; + OUT(spell); + eq->level2 = eq->level; + eq->effect_flag = emu->buff_unknown; + eq->unknown39 = 14; + eq->unknown43 = 0; + eq->unknown44 = 17; + eq->unknown45 = 0; + eq->unknown46 = -1; + eq->unknown50 = 0; + eq->unknown54 = 0; - uint32 TempSlot = 0; - - if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // Main Inventory and Cursor - RoFSlot.SlotType = maps::MapPossessions; - RoFSlot.MainSlot = ServerSlot; - - if (ServerSlot == MainPowerSource) - RoFSlot.MainSlot = slots::MainPowerSource; - - else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory - RoFSlot.MainSlot += 3; - - else if (ServerSlot >= MainAmmo) // (> 20) - RoFSlot.MainSlot += 1; + FINISH_ENCODE(); } - /*else if (ServerSlot < 51) { // Cursor Buffer - RoFSlot.SlotType = maps::MapLimbo; - RoFSlot.MainSlot = ServerSlot - 31; + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToRoFMainInvSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_AltCurrency) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *emu_buffer = in->pBuffer; + uint32 opcode = *((uint32*)emu_buffer); + + if (opcode == 8) { + AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) + + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); + structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; + + out_populate->opcode = populate->opcode; + out_populate->count = populate->count; + for (uint32 i = 0; i < populate->count; ++i) { + out_populate->entries[i].currency_number = populate->entries[i].currency_number; + out_populate->entries[i].unknown00 = populate->entries[i].unknown00; + out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; + out_populate->entries[i].item_id = populate->entries[i].item_id; + out_populate->entries[i].item_icon = populate->entries[i].item_icon; + out_populate->entries[i].stack_size = populate->entries[i].stack_size; + out_populate->entries[i].display = ((populate->entries[i].stack_size > 0) ? 1 : 0); + } + + dest->FastQueuePacket(&outapp, ack_req); + } + else { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); + memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); + dest->FastQueuePacket(&outapp, ack_req); + } + + //dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_AltCurrencySell) + { + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + OUT(merchant_entity_id); + eq->slot_id = ServerToRoFSlot(emu->slot_id); + OUT(charges); + OUT(cost); + + FINISH_ENCODE(); + } + + ENCODE(OP_Animation) + { + ENCODE_LENGTH_EXACT(Animation_Struct); + SETUP_DIRECT_ENCODE(Animation_Struct, structs::Animation_Struct); + + OUT(spawnid); + OUT(value); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToRoFMainInvSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); + } + + ENCODE(OP_Barter) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + + if (SubAction != Barter_BuyerAppearance) + { + dest->FastQueuePacket(&in, ack_req); + + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = 80; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + char Name[64]; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); + uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + VARSTRUCT_DECODE_STRING(Name, Buffer); + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + OutBuffer = (char *)in->pBuffer + 72; + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BazaarSearch) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + in->pBuffer = new unsigned char[in->size]; + + memset(in->pBuffer, 0, in->size); + + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(SellerID); + memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); + OUT(NumItems); + OUT(ItemID); + OUT(SerialNumber); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(Cost); + OUT(ItemStat); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BeginCast) + { + SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); + + OUT(spell_id); + OUT(caster_id); + OUT(cast_time); + + FINISH_ENCODE(); + } + + ENCODE(OP_BlockedBuffs) + { + ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); + SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); + + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = emu->SpellID[i]; + + // -1 for the extra 10 added in RoF. We should really be encoding for the older clients, not RoF, but + // we can sort that out later. + + for (uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) + eq->SpellID[i] = -1; + + OUT(Count); + OUT(Pet); + OUT(Initialise); + OUT(Flags); + + FINISH_ENCODE(); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); + + OUT(entityid); + eq->unknown004 = 2; + //eq->level = 80; + //eq->effect = 0; + OUT(level); + OUT(effect); + eq->unknown007 = 0; + eq->unknown008 = 1.0f; + OUT(spellid); + OUT(duration); + eq->playerId = 0x7cde; + OUT(slotid); + OUT(num_hits); + if (emu->bufffade == 1) + eq->bufffade = 1; + else + eq->bufffade = 2; + + // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon + EQApplicationPacket *outapp = nullptr; + if (eq->bufffade == 1) + { + outapp = new EQApplicationPacket(OP_BuffCreate, 29); + outapp->WriteUInt32(emu->entityid); + outapp->WriteUInt32(0x0271); // Unk + outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? + outapp->WriteUInt16(1); // 1 buff in this packet + outapp->WriteUInt32(emu->slotid); + outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) + outapp->WriteUInt32(0); // Duration + outapp->WriteUInt32(0); // ? + outapp->WriteUInt8(0); // Caster name + outapp->WriteUInt8(0); // Terminating byte + } + FINISH_ENCODE(); + + if (outapp) + dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff + } + + ENCODE(OP_BuffCreate) + { + SETUP_VAR_ENCODE(BuffIcon_Struct); + + uint32 sz = 12 + (17 * emu->count); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); + + __packet->WriteUInt32(emu->entity_id); + __packet->WriteUInt32(0); // PlayerID ? + __packet->WriteUInt8(emu->all_buffs); // 1 indicates all buffs on the player (0 to add or remove a single buff) + __packet->WriteUInt16(emu->count); + + for (uint16 i = 0; i < emu->count; ++i) + { + uint16 buffslot = emu->entries[i].buff_slot; + // Not sure if this is needs amending for RoF yet. + if (emu->entries[i].buff_slot >= 25) + { + buffslot += 17; + } + + __packet->WriteUInt32(buffslot); + __packet->WriteUInt32(emu->entries[i].spell_id); + __packet->WriteUInt32(emu->entries[i].tics_remaining); + __packet->WriteUInt32(emu->entries[i].num_hits); // Unknown + __packet->WriteString(""); + } + __packet->WriteUInt8(!emu->all_buffs); // Unknown + + FINISH_ENCODE(); + } + + ENCODE(OP_CancelTrade) + { + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); + + OUT(fromid); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_CastSpell) + { + ENCODE_LENGTH_EXACT(CastSpell_Struct); + SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); + + if (emu->slot == 10) + eq->slot = 13; + else + OUT(slot); + + OUT(spell_id); + eq->inventoryslot = ServerToRoFSlot(emu->inventoryslot); + //OUT(inventoryslot); + OUT(target_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; + + *p = nullptr; + + if (in->size == 0) { + + in->size = 4; + in->pBuffer = new uchar[in->size]; + + *((uint32 *)in->pBuffer) = 0; + + dest->FastQueuePacket(&in, ack_req); + return; + } + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + + uchar *OldBuffer = in->pBuffer; + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClickObjectAction) + { + ENCODE_LENGTH_EXACT(ClickObjectAction_Struct); + SETUP_DIRECT_ENCODE(ClickObjectAction_Struct, structs::ClickObjectAction_Struct); + + OUT(drop_id); + eq->unknown04 = -1; + eq->unknown08 = -1; + OUT(type); + OUT(icon); + eq->unknown16 = 0; + OUT_str(object_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToRoFSlot(emu->from_slot); + eq->to_slot = ServerToRoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteSpawn) + { + ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + eq->unknown04 = 1; // Observed + + FINISH_ENCODE(); + } + + ENCODE(OP_DisciplineUpdate) + { + ENCODE_LENGTH_EXACT(Disciplines_Struct); + SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); + + memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->unknown004 = 785316192; + eq->unknown008 = 435601; + strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strncpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); + strncpy(eq->player_name, emu->player_name, sizeof(eq->player_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GMLastName) + { + ENCODE_LENGTH_EXACT(GMLastName_Struct); + SETUP_DIRECT_ENCODE(GMLastName_Struct, structs::GMLastName_Struct); + + OUT_str(name); + OUT_str(gmname); + OUT_str(lastname); + for (int i = 0; i<4; i++) + { + eq->unknown[i] = emu->unknown[i]; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_GMTrainSkillConfirm) + { + ENCODE_LENGTH_EXACT(GMTrainSkillConfirm_Struct); + SETUP_DIRECT_ENCODE(GMTrainSkillConfirm_Struct, structs::GMTrainSkillConfirm_Struct); + + OUT(SkillID); + OUT(Cost); + OUT(NewSkill); + OUT_str(TrainerName); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + // We are not encoding the spawn_id field here, but it doesn't appear to matter. + // + EQApplicationPacket *in = *p; + *p = nullptr; + + Object_Struct *emu = (Object_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = strlen(emu->object_name) + sizeof(Object_Struct)-1; + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); // Some unique id + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Same for all objects in the zone + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Normally 0, but seen (float)255.0 as well + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 1); // Need to add emu->size to struct + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); + VARSTRUCT_ENCODE_TYPE(int32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GroupCancelInvite) + { + ENCODE_LENGTH_EXACT(GroupCancel_Struct); + SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + OUT(toggle); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow2) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupInvite) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); + memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupUpdate) + { + //_log(NET__ERROR, "OP_GroupUpdate"); + EQApplicationPacket *in = *p; + GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; + + //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); + if ((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + { + if ((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) + { + //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); + dest->FastQueuePacket(&outapp); + + // Make an empty GLAA packet to clear out their useable GLAAs + // + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + dest->FastQueuePacket(&outapp); + delete in; + return; + } + //if(gjs->action == groupActLeave) + // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + + if (in->size == sizeof(GroupUpdate2_Struct)) + { + // Group Update2 + //_log(NET__ERROR, "Struct is GroupUpdate2"); + + unsigned char *__emu_buffer = in->pBuffer; + GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*)__emu_buffer; + + //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + + int MemberCount = 1; + int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + + for (int i = 0; i < 5; ++i) + { + //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); + if (gu2->membername[i][0] != '\0') + { + PacketLength += (22 + strlen(gu2->membername[i]) + 1); + ++MemberCount; + } + } + + //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + + char *Buffer = (char *)outapp->pBuffer; + + // Header + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + + // Leader + // + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + + int MemberNumber = 1; + + for (int i = 0; i < 5; ++i) + { + if (gu2->membername[i][0] == '\0') + continue; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = gu2->NPCMarkerID; + memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); + + dest->FastQueuePacket(&outapp); + delete in; + + return; + + } + //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + ENCODE_LENGTH_EXACT(GroupJoin_Struct); + SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); + + memcpy(eq->membername, emu->membername, sizeof(eq->membername)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = emu->NPCMarkerID; + + memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); + //_hex(NET__ERROR, __packet->pBuffer, __packet->size); + + FINISH_ENCODE(); + + dest->FastQueuePacket(&outapp); + } + + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + // Guild ID + buffer += sizeof(uint32); + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + + /* Translate older ranks to new values */ + switch (emu_e->rank) { + case 0: { e->rank = htonl(5); break; } // GUILD_MEMBER 0 + case 1: { e->rank = htonl(3); break; } // GUILD_OFFICER 1 + case 2: { e->rank = htonl(1); break; } // GUILD_LEADER 2 + default: { e->rank = htonl(emu_e->rank); break; } // GUILD_NONE + } + + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + e->unknown01 = 0; + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); + e->unknown_one2 = htonl(1); + e->unknown04 = 0; + +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GuildMemberUpdate) + { + SETUP_DIRECT_ENCODE(GuildMemberUpdate_Struct, structs::GuildMemberUpdate_Struct); + + OUT(GuildID); + memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); + OUT(ZoneID); + OUT(InstanceID); + OUT(LastSeen); + eq->Unknown76 = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildsList) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + uint32 NumberOfGuilds = in->size / 64; + uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. + + unsigned char *__emu_buffer = in->pBuffer; + + char *InBuffer = (char *)__emu_buffer; + + uint32 HighestGuildID = 0; + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + PacketSize += (5 + strlen(InBuffer)); + HighestGuildID = i - 1; + } + InBuffer += 64; + } + + PacketSize++; // Appears to be an extra 0x00 at the very end. + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + InBuffer = (char *)__emu_buffer; + + char *OutBuffer = (char *)in->pBuffer; + + // Init the first 64 bytes to zero, as per live. + // + memset(OutBuffer, 0, 64); + + OutBuffer += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); + VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); + } + InBuffer += 64; + } + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_HPUpdate) + { + SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); + + OUT(spawn_id); + OUT(cur_hp); + OUT(max_hp); + + FINISH_ENCODE(); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + eq->unknown316 = -1; // Observed + + FINISH_ENCODE(); + } + + /*ENCODE(OP_InspectAnswer) + { + ENCODE_LENGTH_EXACT(InspectResponse_Struct); + SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); + + OUT(TargetID); + OUT(playerid); + + int r; + for (r = 0; r < 21; r++) { + strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); + } + // Swap last 2 slots for Arrow and Power Source + strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); + strn0cpy(eq->unknown_zero, emu->itemnames[21], sizeof(eq->unknown_zero)); + + int k; + for (k = 0; k < 21; k++) { + OUT(itemicons[k]); + } + // Swap last 2 slots for Arrow and Power Source + eq->itemicons[21] = emu->itemicons[22]; + eq->unknown_zero2 = emu->itemicons[21]; + strn0cpy(eq->text, emu->text, sizeof(eq->text)); + + FINISH_ENCODE(); }*/ - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { // (> 250 && < 341) - RoFSlot.SlotType = maps::MapPossessions; - TempSlot = ServerSlot - 1; - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); - - if (RoFSlot.MainSlot >= slots::MainGeneral9) // (> 30) - RoFSlot.MainSlot = slots::MainCursor; - } - - else if (ServerSlot >= EmuConstants::TRIBUTE_BEGIN && ServerSlot <= EmuConstants::TRIBUTE_END) { // Tribute - RoFSlot.SlotType = maps::MapTribute; - RoFSlot.MainSlot = ServerSlot - EmuConstants::TRIBUTE_BEGIN; - } - - else if (ServerSlot >= EmuConstants::BANK_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) { - RoFSlot.SlotType = maps::MapBank; - TempSlot = ServerSlot - EmuConstants::BANK_BEGIN; - RoFSlot.MainSlot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); - } - } - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) { - RoFSlot.SlotType = maps::MapSharedBank; - TempSlot = ServerSlot - EmuConstants::SHARED_BANK_BEGIN; - RoFSlot.MainSlot = TempSlot; - - if (TempSlot > 30) { // (> 30) - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); - } - } - - else if (ServerSlot >= EmuConstants::TRADE_BEGIN && ServerSlot <= EmuConstants::TRADE_BAGS_END) { - RoFSlot.SlotType = maps::MapTrade; - TempSlot = ServerSlot - EmuConstants::TRADE_BEGIN; - RoFSlot.MainSlot = TempSlot; - - if (TempSlot > 30) { - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); - } - - /* - // OLD CODE: - if (TempSlot > 99) { - if (TempSlot > 100) - RoFSlot.MainSlot = int((TempSlot - 100) / 10); - - else - RoFSlot.MainSlot = 0; - - RoFSlot.SubSlot = TempSlot - (100 + RoFSlot.MainSlot); - } - */ - } - - else if (ServerSlot >= EmuConstants::WORLD_BEGIN && ServerSlot <= EmuConstants::WORLD_END) { - RoFSlot.SlotType = maps::MapWorld; - TempSlot = ServerSlot - EmuConstants::WORLD_BEGIN; - RoFSlot.MainSlot = TempSlot; - } - - _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); - - return RoFSlot; -} - -// Converts RoF Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 RoFToServerSlot(structs::ItemSlotStruct RoFSlot) { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; - - if (RoFSlot.SlotType == maps::MapPossessions && RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 51) - if (RoFSlot.MainSlot == slots::MainPowerSource) - TempSlot = MainPowerSource; - - else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory - TempSlot = RoFSlot.MainSlot - 3; - - /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory/corpse slots - // Need to figure out what to do when we get these - - // The slot range of 0 - client_max is cross-utilized between player inventory and corpse inventory. - // In the case of RoF, player inventory is addressed as 0 - 33 and corpse inventory is addressed as 23 - 56. - // We 'could' assign the two new inventory slots as 9997 and 9998, and then work around their bag - // slot assignments, but doing so may disrupt our ability to utilize the corpse looting range properly. - - // For now, it's probably best to leave as-is and let this work itself out in the inventory rework. - }*/ - - else if (RoFSlot.MainSlot >= slots::MainAmmo) // Ammo and Main Inventory - TempSlot = RoFSlot.MainSlot - 1; - - else // Worn Slots - TempSlot = RoFSlot.MainSlot; - - if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapBank) { - TempSlot = EmuConstants::BANK_BEGIN; - - if (RoFSlot.SubSlot >= SUB_BEGIN) - TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - else - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapSharedBank) { - TempSlot = EmuConstants::SHARED_BANK_BEGIN; - - if (RoFSlot.SubSlot >= SUB_BEGIN) - TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - else - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapTrade) { - TempSlot = EmuConstants::TRADE_BEGIN; - - if (RoFSlot.SubSlot >= SUB_BEGIN) - TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - // OLD CODE: - //TempSlot += 100 + (RoFSlot.MainSlot * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot; - - else - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - else if (RoFSlot.SlotType == maps::MapWorld) { - TempSlot = EmuConstants::WORLD_BEGIN; - - if (RoFSlot.MainSlot >= SUB_BEGIN) - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - } - - /*else if (RoFSlot.SlotType == maps::MapLimbo) { // Cursor Buffer - TempSlot = 31; - - if (RoFSlot.MainSlot >= 0) - TempSlot += RoFSlot.MainSlot; - - ServerSlot = TempSlot; - }*/ - - _log(NET__ERROR, "Convert RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); - - return ServerSlot; -} - -// Converts Server MainInv Slot IDs to RoF MainInv Slot IDs for use in Encodes -static inline structs::MainInvItemSlotStruct ServerToRoFMainInvSlot(uint32 ServerSlot) { - structs::MainInvItemSlotStruct RoFSlot; - RoFSlot.MainSlot = INVALID_INDEX; - RoFSlot.SubSlot = INVALID_INDEX; - RoFSlot.AugSlot = INVALID_INDEX; - RoFSlot.Unknown01 = NOT_USED; - - uint32 TempSlot = 0; - - if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // (< 52) - RoFSlot.MainSlot = ServerSlot; - - if (ServerSlot == MainPowerSource) - RoFSlot.MainSlot = slots::MainPowerSource; - - else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory - RoFSlot.MainSlot += 3; - - else if (ServerSlot >= MainAmmo) // Ammo and Personl Inventory - RoFSlot.MainSlot += 1; - - /*else if (ServerSlot >= MainCursor) { // Cursor - RoFSlot.MainSlot = slots::MainCursor; - - if (ServerSlot > 30) - RoFSlot.SubSlot = (ServerSlot + 3) - 33; - }*/ - } - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { - TempSlot = ServerSlot - 1; - RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; - RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); - } - - _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); - - return RoFSlot; -} - -// Converts RoF MainInv Slot IDs to Server MainInv Slot IDs for use in Decodes -static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot) { - uint32 ServerSlot = INVALID_INDEX; - uint32 TempSlot = 0; - - if (RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 33) - if (RoFSlot.MainSlot == slots::MainPowerSource) - TempSlot = MainPowerSource; - - else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory - TempSlot = RoFSlot.MainSlot - 3; - - /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory slots - // Need to figure out what to do when we get these - - // Same as above - }*/ - - else if (RoFSlot.MainSlot >= slots::MainAmmo) // Main Inventory and Ammo Slots - TempSlot = RoFSlot.MainSlot - 1; - - else - TempSlot = RoFSlot.MainSlot; - - if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots - TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; - - ServerSlot = TempSlot; - } - - _log(NET__ERROR, "Convert RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to RoF Corpse Slot IDs for use in Encodes -static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse) { - uint32 RoFCorpse; - // reserved -} -*/ -/* -// Converts RoF Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_TaskHistoryReply) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - // First we need to calculate the length of the new packet - in->SetReadPosition(4); - uint32 ActivityCount = in->ReadUInt32(); - - uint32 Text1Length = 0; - uint32 Text2Length = 0; - uint32 Text3Length = 0; - - uint32 OutboundPacketSize = 8; - - for(uint32 i = 0; i < ActivityCount; ++i) + ENCODE(OP_InspectBuffs) { - Text1Length = 0; - Text2Length = 0; - Text3Length = 0; + ENCODE_LENGTH_EXACT(InspectBuffs_Struct); + SETUP_DIRECT_ENCODE(InspectBuffs_Struct, structs::InspectBuffs_Struct); - in->ReadUInt32(); // Activity type + // we go over the internal 25 instead of the packet's since no entry is 0, which it will be already + for (int i = 0; i < BUFF_COUNT; i++) { + OUT(spell_id[i]); + OUT(tics_remaining[i]); + } - // Skip past Text1 - while(in->ReadUInt8()) - ++Text1Length; - - // Skip past Text2 - while(in->ReadUInt8()) - ++Text2Length; - - in->ReadUInt32(); - in->ReadUInt32(); - in->ReadUInt32(); - uint32 ZoneID = in->ReadUInt32(); - in->ReadUInt32(); - - // Skip past Text3 - while(in->ReadUInt8()) - ++Text3Length; - - char ZoneNumber[10]; - - sprintf(ZoneNumber, "%i", ZoneID); - - OutboundPacketSize += (24 + Text1Length + 1 + Text2Length + Text3Length + 1 + 7 + (strlen(ZoneNumber) * 2)); + FINISH_ENCODE(); } - in->SetReadPosition(0); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskHistoryReply, OutboundPacketSize); - - outapp->WriteUInt32(in->ReadUInt32()); // Task index - outapp->WriteUInt32(in->ReadUInt32()); // Activity count - - for(uint32 i = 0; i < ActivityCount; ++i) + ENCODE(OP_InspectRequest) { - Text1Length = 0; - Text2Length = 0; - Text3Length = 0; + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - outapp->WriteUInt32(in->ReadUInt32()); // ActivityType + OUT(TargetID); + OUT(PlayerID); - // Copy Text1 - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - - outapp->WriteUInt8(0); // Text1 has a null terminator - - uint32 CurrentPosition = in->GetReadPosition(); - - // Determine Length of Text2 - while(in->ReadUInt8()) - ++Text2Length; - - outapp->WriteUInt32(Text2Length); - - in->SetReadPosition(CurrentPosition); - - // Copy Text2 - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - - outapp->WriteUInt32(in->ReadUInt32()); // Goalcount - in->ReadUInt32(); - in->ReadUInt32(); - uint32 ZoneID = in->ReadUInt32(); - in->ReadUInt32(); - - char ZoneNumber[10]; - - sprintf(ZoneNumber, "%i", ZoneID); - - outapp->WriteUInt32(2); - outapp->WriteUInt8(0x2d); // "-" - outapp->WriteUInt8(0x31); // "1" - - outapp->WriteUInt32(2); - outapp->WriteUInt8(0x2d); // "-" - outapp->WriteUInt8(0x31); // "1" - outapp->WriteString(ZoneNumber); - - outapp->WriteUInt32(0); - - // Copy Tex3t - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - - outapp->WriteUInt8(0); // Text3 has a null terminator - - outapp->WriteUInt8(0x31); // "1" - outapp->WriteString(ZoneNumber); + FINISH_ENCODE(); } - delete in; + ENCODE(OP_InterruptCast) + { + ENCODE_LENGTH_EXACT(InterruptCast_Struct); + SETUP_DIRECT_ENCODE(InterruptCast_Struct, structs::InterruptCast_Struct); - dest->FastQueuePacket(&outapp, ack_req); -} + OUT(spawnid); + OUT(messageid); -ENCODE(OP_TaskDescription) -{ - EQApplicationPacket *in = *p; - *p = nullptr; + FINISH_ENCODE(); + } - EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskDescription, in->size + 1); - // Set the Write pointer as we don't know what has been done with the packet before we get it. - in->SetReadPosition(0); - // Copy the header - for(int i = 0; i < 5; ++i) - outapp->WriteUInt32(in->ReadUInt32()); + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } - // Copy Title - while(uint8 c = in->ReadUInt8()) - outapp->WriteUInt8(c); - outapp->WriteUInt8(0); + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; - outapp->WriteUInt32(in->ReadUInt32()); // Duration - outapp->WriteUInt32(in->ReadUInt32()); // Unknown - uint32 StartTime = in->ReadUInt32(); - outapp->WriteUInt32(time(nullptr) - StartTime); // RoF has elapsed time here rather than starttime + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - // Copy the rest of the packet verbatim - uint32 BytesLeftToCopy = in->size - in->GetReadPosition(); - memcpy(outapp->pBuffer + outapp->GetWritePosition(), in->pBuffer + in->GetReadPosition(), BytesLeftToCopy); + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); - delete in; + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); - dest->FastQueuePacket(&outapp, ack_req); -} + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } -/* -ENCODE(OP_OpenNewTasksWindow) { + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); + eq->slot = ServerToRoFSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strncpy(eq->worldshortname, emu->worldshortname, sizeof(eq->worldshortname)); + + //OUT(enablevoicemacros); // These two are lost, but must be one of the 1s in unknown[249] + //OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + eq->unknown016 = 1; + eq->unknown020[0] = 1; + + eq->unknown249[0] = 1; + eq->unknown249[1] = 1; + eq->unknown249[8] = 1; + eq->unknown249[9] = 1; + eq->unknown249[12] = 1; + eq->unknown249[14] = 1; + eq->unknown249[15] = 1; + eq->unknown249[16] = 1; + + eq->unknown276[0] = 1.0f; + eq->unknown276[1] = 1.0f; + eq->unknown276[6] = 1.0f; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = ServerToRoFCorpseSlot(emu->slot_id); + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MercenaryDataResponse) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; + PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); + + for (r = 0; r < emu->MercTypeCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); + } + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Status); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName + for (k = 0; k < emu->Mercs[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); + } + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MercenaryDataUpdate) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + EQApplicationPacket *outapp; + + uint32 PacketSize = 0; + + // There are 2 different sized versions of this packet depending if a merc is hired or not + if (emu->MercStatus >= 0) + { + PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; + PacketSize += strlen(emu->MercData[r].MercName); // Null Terminator size already accounted for in the struct + } + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Status); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); + //VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName + VARSTRUCT_ENCODE_STRING(Buffer, emu->MercData[r].MercName); + for (k = 0; k < emu->MercData[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); // MercUnk05 + } + } + else + { + PacketSize += sizeof(structs::NoMercenaryHired_Struct); + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToRoFSlot(emu->from_slot); + eq->to_slot = ServerToRoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + + eq->FogDensity = emu->fog_density; + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown800 = -1; + eq->unknown844 = 600; + eq->unknown880 = 50; + eq->unknown884 = 10; + eq->unknown888 = 1; + eq->unknown889 = 0; + eq->unknown890 = 1; + eq->unknown891 = 0; + eq->unknown892 = 0; + eq->unknown893 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown895 = 0; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 180; + eq->unknown908 = 2; + eq->unknown912 = 2; + eq->unknown932 = -1; // Set from PoK Example + eq->unknown936 = -1; // Set from PoK Example + eq->unknown944 = 1.0; // Set from PoK Example + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + // This packet is variable sized now, but forcing it to the old packet size for now. + eq->Title_Count = 128; + memcpy(eq->Title, emu->Title, sizeof(eq->Title)); + eq->Text_Count = 4096; + memcpy(eq->Text, emu->Text, sizeof(eq->Text)); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + OUT(NegativeID); + // These two field names are used if Buttons == 1. We should add an interface to them via Perl. + eq->ButtonName0_Count = 25; + OUT_str(ButtonName0); + eq->ButtonName1_Count = 25; + OUT_str(ButtonName1); + + FINISH_ENCODE(); + } + + /* + ENCODE(OP_OpenNewTasksWindow) + { AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; AvailableTaskData1_Struct* __emu_AvailableTaskData1; AvailableTaskData2_Struct* __emu_AvailableTaskData2; @@ -593,1260 +1756,1878 @@ ENCODE(OP_OpenNewTasksWindow) { for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in RoF packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in RoF packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - strcpy(__eq_ptr, __emu_Ptr); // Title + strcpy(__eq_ptr, __emu_Ptr); // Title - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - strcpy(__eq_ptr, __emu_Ptr); // Description + strcpy(__eq_ptr, __emu_Ptr); // Description - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - strcpy(__eq_ptr, __emu_Ptr); // Unknown string + strcpy(__eq_ptr, __emu_Ptr); // Unknown string - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - strcpy(__eq_ptr, __emu_Ptr); // Unknown string + strcpy(__eq_ptr, __emu_Ptr); // Unknown string - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - strcpy(__eq_ptr, __emu_Ptr); // Unknown string + strcpy(__eq_ptr, __emu_Ptr); // Unknown string - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; } delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} -*/ - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); - - - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; - - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } - - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - //eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->class_ = emu->class_[r]; - eq2->race = emu->race[r]; - eq2->level = emu->level[r]; - eq2->class_2 = emu->class_[r]; - eq2->race2 = emu->race[r]; - eq2->zone = emu->zone[r]; - eq2->instance = 0; - eq2->gender = emu->gender[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].equip2 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].equip3 = emu->equip[r][k]; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; - } - eq2->u15 = 0xff; - eq2->u19 = 0xFF; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; - eq2->deity = emu->deity[r]; - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->beard = emu->beard[r]; - eq2->char_enabled = 1; - eq2->tutorial = emu->tutorial[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->unknown1 = 0; - eq2->gohome = emu->gohome[r]; - eq2->LastLogin = 1212696584; - eq2->unknown2 = 0; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); - } - - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); -} - -ENCODE(OP_SendZonepoints) { - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); - - eq->count = emu->count; - for(uint32 i = 0; i < emu->count; ++i) - { - eq->zpe[i].iterator = emu->zpe[i].iterator; - eq->zpe[i].x = emu->zpe[i].x; - eq->zpe[i].y = emu->zpe[i].y; - eq->zpe[i].z = emu->zpe[i].z; - eq->zpe[i].heading = emu->zpe[i].heading; - eq->zpe[i].zoneid = emu->zpe[i].zoneid; - eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; - } - - FINISH_ENCODE(); -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 5 is for Live - if (emu->clientver <= 5 ) - { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?-1:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?-1:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); - OUT(cost); - OUT(seq); - OUT(current_level); - eq->unknown037 = 1; // Introduced during HoT - OUT(prereq_skill); - eq->unknown045 = 1; // New Mar 21 2012 - Seen 1 - OUT(prereq_minpoints); - eq->type = emu->sof_type; - OUT(spellid); - eq->unknown057 = 1; // Introduced during HoT - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } - } - - _hex(NET__ERROR, eq, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - FINISH_ENCODE(); -} - -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - - -ENCODE(OP_PlayerProfile) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - PlayerProfile_Struct *emu = (PlayerProfile_Struct *) __emu_buffer; - - uint32 PacketSize = 40000; // Calculate this later - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayerProfile, PacketSize); - - outapp->WriteUInt32(0); // Checksum, we will update this later - outapp->WriteUInt32(0); // Checksum size, we will update this later - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - - outapp->WriteUInt8(emu->gender); // Gender - outapp->WriteUInt32(emu->race); // Race - outapp->WriteUInt8(emu->class_); // Class - outapp->WriteUInt8(emu->level); // Level - outapp->WriteUInt8(emu->level); // Level1 - - - outapp->WriteUInt32(5); // Bind count - - for(int r = 0; r < 5; r++) - { - outapp->WriteUInt32(emu->binds[r].zoneId); - outapp->WriteFloat(emu->binds[r].x); - outapp->WriteFloat(emu->binds[r].y); - outapp->WriteFloat(emu->binds[r].z); - outapp->WriteFloat(emu->binds[r].heading); - } - - outapp->WriteUInt32(emu->deity); - outapp->WriteUInt32(emu->intoxication); - - outapp->WriteUInt32(10); // Unknown count - - for(int r = 0; r < 10; r++) - { - outapp->WriteUInt32(0); // Unknown - } - - outapp->WriteUInt32(22); // Equipment count - - for(int r = 0; r < 9; r++) - { - outapp->WriteUInt32(emu->item_material[r]); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - // Write zeroes for the next 13 equipment slots - - for(int r = 0; r < 13; r++) - { - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(9); // Equipment2 count - - for(int r = 0; r < 9; r++) - { - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(9); // Tint Count - - for(int r = 0; r < 7; r++) - { - outapp->WriteUInt32(emu->item_tint[r].color); - } - // Write zeroes for extra two tint values - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - - outapp->WriteUInt32(9); // Tint2 Count - - for(int r = 0; r < 7; r++) - { - outapp->WriteUInt32(emu->item_tint[r].color); - } - // Write zeroes for extra two tint values - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - - - outapp->WriteUInt8(emu->haircolor); - outapp->WriteUInt8(emu->beardcolor); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt8(emu->eyecolor1); - outapp->WriteUInt8(emu->eyecolor2); - outapp->WriteUInt8(emu->hairstyle); - outapp->WriteUInt8(emu->beard); - outapp->WriteUInt8(emu->face); - - // Think there should be an extra byte before the drakkin stuff (referred to as oldface in client) - // Then one of the five bytes following the drakkin stuff needs removing. - - outapp->WriteUInt32(emu->drakkin_heritage); - outapp->WriteUInt32(emu->drakkin_tattoo); - outapp->WriteUInt32(emu->drakkin_details); - - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - outapp->WriteFloat(5.0f); // Height ? - - outapp->WriteFloat(3.0f); // Unknown - outapp->WriteFloat(2.5f); // Unknown - outapp->WriteFloat(5.5f); // Unknown - - outapp->WriteUInt32(0); // Primary ? - outapp->WriteUInt32(0); // Secondary ? - - outapp->WriteUInt32(emu->points); // Unspent skill points - outapp->WriteUInt32(emu->mana); - outapp->WriteUInt32(emu->cur_hp); - - outapp->WriteUInt32(emu->STR); - outapp->WriteUInt32(emu->STA); - outapp->WriteUInt32(emu->CHA); - outapp->WriteUInt32(emu->DEX); - outapp->WriteUInt32(emu->INT); - outapp->WriteUInt32(emu->AGI); - outapp->WriteUInt32(emu->WIS); - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - - outapp->WriteUInt32(300); // AA Count - - for(uint32 r = 0; r < MAX_PP_AA_ARRAY; r++) - { - outapp->WriteUInt32(emu->aa_array[r].AA); - outapp->WriteUInt32(emu->aa_array[r].value); - outapp->WriteUInt32(0); - } - - // Fill the other 60 AAs with zeroes - - for(uint32 r = 0; r < structs::MAX_PP_AA_ARRAY - MAX_PP_AA_ARRAY; r++) - { - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } - - - outapp->WriteUInt32(structs::MAX_PP_SKILL); - - for(uint32 r = 0; r < structs::MAX_PP_SKILL; r++) - { - outapp->WriteUInt32(emu->skills[r]); - } - - // deprecated - // Write zeroes for the rest of the skills - /* - for(uint32 r = 0; r < structs::MAX_PP_SKILL - MAX_PP_SKILL; r++) - { - outapp->WriteUInt32(emu->skills[r]); } */ - outapp->WriteUInt32(25); // Unknown count - - for(uint32 r = 0; r < 25; r++) + ENCODE(OP_PetBuffWindow) { - outapp->WriteUInt32(0); // Unknown - } + // The format of the RoF packet is identical to the OP_BuffCreate packet. - outapp->WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count + SETUP_VAR_ENCODE(PetBuff_Struct); - for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) - { - outapp->WriteUInt32(emu->disciplines.values[r]); - } + uint32 sz = 12 + (17 * emu->buffcount); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); - // Write zeroes for the rest of the disciplines - for(uint32 r = 0; r < structs::MAX_PP_DISCIPLINES - MAX_PP_DISCIPLINES; r++) - { - outapp->WriteUInt32(0); - } + __packet->WriteUInt32(emu->petid); + __packet->WriteUInt32(0); // PlayerID ? + __packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff) + __packet->WriteUInt16(emu->buffcount); - outapp->WriteUInt32(20); // Timestamp count - - for(uint32 r = 0; r < 20; r++) - { - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(MAX_RECAST_TYPES); // Timestamp count - - for(uint32 r = 0; r < MAX_RECAST_TYPES; r++) - { - outapp->WriteUInt32(emu->recastTimers[r]); - } - - outapp->WriteUInt32(100); // Timestamp2 count - - for(uint32 r = 0; r < 100; r++) - { - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(structs::MAX_PP_SPELLBOOK); // Spellbook slots - - for(uint32 r = 0; r < MAX_PP_SPELLBOOK; r++) - { - outapp->WriteUInt32(emu->spell_book[r]); - } - // zeroes for the rest of the spellbook slots - for(uint32 r = 0; r < structs::MAX_PP_SPELLBOOK - MAX_PP_SPELLBOOK; r++) - { - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots - - for(uint32 r = 0; r < MAX_PP_MEMSPELL; r++) - { - outapp->WriteUInt32(emu->mem_spells[r]); - } - // zeroes for the rest of the slots - for(uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++) - { - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(13); // Unknown count - - for(uint32 r = 0; r < 13; r++) - { - outapp->WriteUInt32(0); // Unknown - } - - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(structs::BUFF_COUNT); - - //*000*/ uint8 slotid; // badly named... seems to be 2 for a real buff, 0 otherwise - //*001*/ float unknown004; // Seen 1 for no buff - //*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages - //*009*/ uint32 unknown016; - //*013*/ uint8 bard_modifier; - //*014*/ uint32 duration; - //*018*/ uint8 level; - //*019*/ uint32 spellid; - //*023*/ uint32 counters; - //*027*/ uint8 unknown0028[53]; - //*080*/ - - for(uint32 r = 0; r < BUFF_COUNT; r++) - { - float instrument_mod = 0.0f; - uint8 slotid = emu->buffs[r].slotid; - uint32 player_id = emu->buffs[r].player_id;; - - if(emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + for (uint16 i = 0; i < BUFF_COUNT; ++i) { - instrument_mod = 1.0f + (emu->buffs[r].bard_modifier - 10) / 10.0f; - slotid = 2; - player_id = 0x000717fd; + if (emu->spellid[i]) + { + __packet->WriteUInt32(i); + __packet->WriteUInt32(emu->spellid[i]); + __packet->WriteUInt32(emu->ticsremaining[i]); + __packet->WriteUInt32(0); // Unknown + __packet->WriteString(""); + } } - else - { - slotid = 0; - } - outapp->WriteUInt8(0); // Had this as slot, but always appears to be 0 on live. - outapp->WriteFloat(instrument_mod); - outapp->WriteUInt32(player_id); - outapp->WriteUInt8(0); - outapp->WriteUInt32(emu->buffs[r].counters); - //outapp->WriteUInt8(emu->buffs[r].bard_modifier); - outapp->WriteUInt32(emu->buffs[r].duration); - outapp->WriteUInt8(emu->buffs[r].level); - outapp->WriteUInt32(emu->buffs[r].spellid); - outapp->WriteUInt32(slotid); // Only ever seen 2 - outapp->WriteUInt32(0); - outapp->WriteUInt8(0); - outapp->WriteUInt32(emu->buffs[r].counters); // Appears twice ? + __packet->WriteUInt8(0); // Unknown - for(uint32 j = 0; j < 44; ++j) - outapp->WriteUInt8(0); // Unknown + FINISH_ENCODE(); } - for(uint32 r = 0; r < structs::BUFF_COUNT - BUFF_COUNT; r++) + ENCODE(OP_PlayerProfile) { - // 80 bytes of zeroes - for(uint32 j = 0; j < 20; ++j) + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + PlayerProfile_Struct *emu = (PlayerProfile_Struct *)__emu_buffer; + + uint32 PacketSize = 40000; // Calculate this later + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PlayerProfile, PacketSize); + + outapp->WriteUInt32(0); // Checksum, we will update this later + outapp->WriteUInt32(0); // Checksum size, we will update this later + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + + outapp->WriteUInt8(emu->gender); // Gender + outapp->WriteUInt32(emu->race); // Race + outapp->WriteUInt8(emu->class_); // Class + outapp->WriteUInt8(emu->level); // Level + outapp->WriteUInt8(emu->level); // Level1 + + + outapp->WriteUInt32(5); // Bind count + + for (int r = 0; r < 5; r++) + { + outapp->WriteUInt32(emu->binds[r].zoneId); + outapp->WriteFloat(emu->binds[r].x); + outapp->WriteFloat(emu->binds[r].y); + outapp->WriteFloat(emu->binds[r].z); + outapp->WriteFloat(emu->binds[r].heading); + } + + outapp->WriteUInt32(emu->deity); + outapp->WriteUInt32(emu->intoxication); + + outapp->WriteUInt32(10); // Unknown count + + for (int r = 0; r < 10; r++) + { + outapp->WriteUInt32(0); // Unknown + } + + outapp->WriteUInt32(22); // Equipment count + + for (int r = 0; r < 9; r++) + { + outapp->WriteUInt32(emu->item_material[r]); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); outapp->WriteUInt32(0); - - } - - outapp->WriteUInt32(emu->platinum); - outapp->WriteUInt32(emu->gold); - outapp->WriteUInt32(emu->silver); - outapp->WriteUInt32(emu->copper); - - outapp->WriteUInt32(emu->platinum_cursor); - outapp->WriteUInt32(emu->gold_cursor); - outapp->WriteUInt32(emu->silver_cursor); - outapp->WriteUInt32(emu->copper_cursor); - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt32(0); // This is the cooldown timer for the monk 'Mend' skill. Client will add 6 minutes to this value the first time the - // player logs in. After that it will honour whatever value we send here. - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt32(emu->thirst_level); - outapp->WriteUInt32(emu->hunger_level); - - outapp->WriteUInt32(emu->aapoints_spent); - - outapp->WriteUInt32(5); // AA Points count ?? - outapp->WriteUInt32(1234); // AA Points assigned - outapp->WriteUInt32(0); // AA Points in General ? - outapp->WriteUInt32(0); // AA Points in Class ? - outapp->WriteUInt32(0); // AA Points in Archetype ? - outapp->WriteUInt32(0); // AA Points in Special ? - outapp->WriteUInt32(emu->aapoints); // AA Points unspent - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - - outapp->WriteUInt32(structs::MAX_PLAYER_BANDOLIER); - - for(uint32 r = 0; r < EmuConstants::BANDOLIERS_COUNT; r++) - { - outapp->WriteString(emu->bandoliers[r].name); - - for(uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) - { - outapp->WriteString(emu->bandoliers[r].items[j].item_name); - outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id); - outapp->WriteUInt32(emu->bandoliers[r].items[j].icon); } - } - for(uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++) - { - outapp->WriteString(""); + // Write zeroes for the next 13 equipment slots - for(uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) + for (int r = 0; r < 13; r++) + { + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(9); // Equipment2 count + + for (int r = 0; r < 9; r++) + { + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(9); // Tint Count + + for (int r = 0; r < 7; r++) + { + outapp->WriteUInt32(emu->item_tint[r].color); + } + // Write zeroes for extra two tint values + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + + outapp->WriteUInt32(9); // Tint2 Count + + for (int r = 0; r < 7; r++) + { + outapp->WriteUInt32(emu->item_tint[r].color); + } + // Write zeroes for extra two tint values + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + + outapp->WriteUInt8(emu->haircolor); + outapp->WriteUInt8(emu->beardcolor); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt8(emu->eyecolor1); + outapp->WriteUInt8(emu->eyecolor2); + outapp->WriteUInt8(emu->hairstyle); + outapp->WriteUInt8(emu->beard); + outapp->WriteUInt8(emu->face); + + // Think there should be an extra byte before the drakkin stuff (referred to as oldface in client) + // Then one of the five bytes following the drakkin stuff needs removing. + + outapp->WriteUInt32(emu->drakkin_heritage); + outapp->WriteUInt32(emu->drakkin_tattoo); + outapp->WriteUInt32(emu->drakkin_details); + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + + outapp->WriteFloat(5.0f); // Height ? + + outapp->WriteFloat(3.0f); // Unknown + outapp->WriteFloat(2.5f); // Unknown + outapp->WriteFloat(5.5f); // Unknown + + outapp->WriteUInt32(0); // Primary ? + outapp->WriteUInt32(0); // Secondary ? + + outapp->WriteUInt32(emu->points); // Unspent skill points + outapp->WriteUInt32(emu->mana); + outapp->WriteUInt32(emu->cur_hp); + + outapp->WriteUInt32(emu->STR); + outapp->WriteUInt32(emu->STA); + outapp->WriteUInt32(emu->CHA); + outapp->WriteUInt32(emu->DEX); + outapp->WriteUInt32(emu->INT); + outapp->WriteUInt32(emu->AGI); + outapp->WriteUInt32(emu->WIS); + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt32(300); // AA Count + + for (uint32 r = 0; r < MAX_PP_AA_ARRAY; r++) + { + outapp->WriteUInt32(emu->aa_array[r].AA); + outapp->WriteUInt32(emu->aa_array[r].value); + outapp->WriteUInt32(0); + } + + // Fill the other 60 AAs with zeroes + + for (uint32 r = 0; r < structs::MAX_PP_AA_ARRAY - MAX_PP_AA_ARRAY; r++) + { + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(structs::MAX_PP_SKILL); + + for (uint32 r = 0; r < structs::MAX_PP_SKILL; r++) + { + outapp->WriteUInt32(emu->skills[r]); + } + + // deprecated + // Write zeroes for the rest of the skills + /* + for(uint32 r = 0; r < structs::MAX_PP_SKILL - MAX_PP_SKILL; r++) + { + outapp->WriteUInt32(emu->skills[r]); + } + */ + + outapp->WriteUInt32(25); // Unknown count + + for (uint32 r = 0; r < 25; r++) + { + outapp->WriteUInt32(0); // Unknown + } + + outapp->WriteUInt32(structs::MAX_PP_DISCIPLINES); // Discipline count + + for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) + { + outapp->WriteUInt32(emu->disciplines.values[r]); + } + + // Write zeroes for the rest of the disciplines + for (uint32 r = 0; r < structs::MAX_PP_DISCIPLINES - MAX_PP_DISCIPLINES; r++) + { + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(20); // Timestamp count + + for (uint32 r = 0; r < 20; r++) + { + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(MAX_RECAST_TYPES); // Timestamp count + + for (uint32 r = 0; r < MAX_RECAST_TYPES; r++) + { + outapp->WriteUInt32(emu->recastTimers[r]); + } + + outapp->WriteUInt32(100); // Timestamp2 count + + for (uint32 r = 0; r < 100; r++) + { + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(structs::MAX_PP_SPELLBOOK); // Spellbook slots + + for (uint32 r = 0; r < MAX_PP_SPELLBOOK; r++) + { + outapp->WriteUInt32(emu->spell_book[r]); + } + // zeroes for the rest of the spellbook slots + for (uint32 r = 0; r < structs::MAX_PP_SPELLBOOK - MAX_PP_SPELLBOOK; r++) + { + outapp->WriteUInt32(0xFFFFFFFFU); + } + + outapp->WriteUInt32(structs::MAX_PP_MEMSPELL); // Memorised spell slots + + for (uint32 r = 0; r < MAX_PP_MEMSPELL; r++) + { + outapp->WriteUInt32(emu->mem_spells[r]); + } + // zeroes for the rest of the slots + for (uint32 r = 0; r < structs::MAX_PP_MEMSPELL - MAX_PP_MEMSPELL; r++) + { + outapp->WriteUInt32(0xFFFFFFFFU); + } + + outapp->WriteUInt32(13); // Unknown count + + for (uint32 r = 0; r < 13; r++) + { + outapp->WriteUInt32(0); // Unknown + } + + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(structs::BUFF_COUNT); + + //*000*/ uint8 slotid; // badly named... seems to be 2 for a real buff, 0 otherwise + //*001*/ float unknown004; // Seen 1 for no buff + //*005*/ uint32 player_id; // 'global' ID of the caster, for wearoff messages + //*009*/ uint32 unknown016; + //*013*/ uint8 bard_modifier; + //*014*/ uint32 duration; + //*018*/ uint8 level; + //*019*/ uint32 spellid; + //*023*/ uint32 counters; + //*027*/ uint8 unknown0028[53]; + //*080*/ + + for (uint32 r = 0; r < BUFF_COUNT; r++) + { + float instrument_mod = 0.0f; + uint8 slotid = emu->buffs[r].slotid; + uint32 player_id = emu->buffs[r].player_id;; + + if (emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + { + instrument_mod = 1.0f + (emu->buffs[r].bard_modifier - 10) / 10.0f; + slotid = 2; + player_id = 0x000717fd; + } + else + { + slotid = 0; + } + outapp->WriteUInt8(0); // Had this as slot, but always appears to be 0 on live. + outapp->WriteFloat(instrument_mod); + outapp->WriteUInt32(player_id); + outapp->WriteUInt8(0); + outapp->WriteUInt32(emu->buffs[r].counters); + //outapp->WriteUInt8(emu->buffs[r].bard_modifier); + outapp->WriteUInt32(emu->buffs[r].duration); + outapp->WriteUInt8(emu->buffs[r].level); + outapp->WriteUInt32(emu->buffs[r].spellid); + outapp->WriteUInt32(slotid); // Only ever seen 2 + outapp->WriteUInt32(0); + outapp->WriteUInt8(0); + outapp->WriteUInt32(emu->buffs[r].counters); // Appears twice ? + + for (uint32 j = 0; j < 44; ++j) + outapp->WriteUInt8(0); // Unknown + } + + for (uint32 r = 0; r < structs::BUFF_COUNT - BUFF_COUNT; r++) + { + // 80 bytes of zeroes + for (uint32 j = 0; j < 20; ++j) + outapp->WriteUInt32(0); + + } + + outapp->WriteUInt32(emu->platinum); + outapp->WriteUInt32(emu->gold); + outapp->WriteUInt32(emu->silver); + outapp->WriteUInt32(emu->copper); + + outapp->WriteUInt32(emu->platinum_cursor); + outapp->WriteUInt32(emu->gold_cursor); + outapp->WriteUInt32(emu->silver_cursor); + outapp->WriteUInt32(emu->copper_cursor); + + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt32(0); // This is the cooldown timer for the monk 'Mend' skill. Client will add 6 minutes to this value the first time the + // player logs in. After that it will honour whatever value we send here. + + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt32(emu->thirst_level); + outapp->WriteUInt32(emu->hunger_level); + + outapp->WriteUInt32(emu->aapoints_spent); + + outapp->WriteUInt32(5); // AA Points count ?? + outapp->WriteUInt32(1234); // AA Points assigned + outapp->WriteUInt32(0); // AA Points in General ? + outapp->WriteUInt32(0); // AA Points in Class ? + outapp->WriteUInt32(0); // AA Points in Archetype ? + outapp->WriteUInt32(0); // AA Points in Special ? + outapp->WriteUInt32(emu->aapoints); // AA Points unspent + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(structs::MAX_PLAYER_BANDOLIER); + + for (uint32 r = 0; r < EmuConstants::BANDOLIERS_COUNT; r++) + { + outapp->WriteString(emu->bandoliers[r].name); + + for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) + { + outapp->WriteString(emu->bandoliers[r].items[j].item_name); + outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id); + outapp->WriteUInt32(emu->bandoliers[r].items[j].icon); + } + } + + for (uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++) + { + outapp->WriteString(""); + + for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j) + { + outapp->WriteString(""); + outapp->WriteUInt32(0); + outapp->WriteUInt32(0); + } + } + + outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT); + + for (uint32 r = 0; r < EmuConstants::POTION_BELT_SIZE; r++) + { + outapp->WriteString(emu->potionbelt.items[r].item_name); + outapp->WriteUInt32(emu->potionbelt.items[r].item_id); + outapp->WriteUInt32(emu->potionbelt.items[r].icon); + } + + for (uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; r++) { outapp->WriteString(""); outapp->WriteUInt32(0); outapp->WriteUInt32(0); } - } + outapp->WriteSInt32(-1); // Unknown; + outapp->WriteSInt32(123); // HP Total ? + outapp->WriteSInt32(234); // Endurance Total ? + outapp->WriteSInt32(345); // Mana Total ? - outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < EmuConstants::POTION_BELT_SIZE; r++) - { - outapp->WriteString(emu->potionbelt.items[r].item_name); - outapp->WriteUInt32(emu->potionbelt.items[r].item_id); - outapp->WriteUInt32(emu->potionbelt.items[r].icon); - } + outapp->WriteUInt32(20); // Unknown - Expansion count ? + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->endurance); + outapp->WriteUInt32(0); // Unknown - Observed 0x7cde - This is also seen in guild packets sent to this character. + outapp->WriteUInt32(0); // Unknown - Observed 0x64 - for(uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; r++) - { - outapp->WriteString(""); - outapp->WriteUInt32(0); - outapp->WriteUInt32(0); - } + outapp->WriteUInt32(64); // Name Length - outapp->WriteSInt32(-1); // Unknown; - outapp->WriteSInt32(123); // HP Total ? - outapp->WriteSInt32(234); // Endurance Total ? - outapp->WriteSInt32(345); // Mana Total ? + uint32 CurrentPosition = outapp->GetWritePosition(); + outapp->WriteString(emu->name); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->SetWritePosition(CurrentPosition + 64); - outapp->WriteUInt32(20); // Unknown - Expansion count ? + outapp->WriteUInt32(32); // Last Name Length - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->endurance); - outapp->WriteUInt32(0); // Unknown - Observed 0x7cde - This is also seen in guild packets sent to this character. - outapp->WriteUInt32(0); // Unknown - Observed 0x64 + CurrentPosition = outapp->GetWritePosition(); - outapp->WriteUInt32(64); // Name Length + outapp->WriteString(emu->last_name); - uint32 CurrentPosition = outapp->GetWritePosition(); + outapp->SetWritePosition(CurrentPosition + 32); - outapp->WriteString(emu->name); + outapp->WriteUInt32(emu->birthday); + outapp->WriteUInt32(emu->birthday); // Account start date ? + outapp->WriteUInt32(emu->lastlogin); + outapp->WriteUInt32(emu->timePlayedMin); + outapp->WriteUInt32(emu->timeentitledonaccount); + outapp->WriteUInt32(0x0007ffff); // Expansion bitmask - outapp->SetWritePosition(CurrentPosition + 64); + outapp->WriteUInt32(structs::MAX_PP_LANGUAGE); - outapp->WriteUInt32(32); // Last Name Length + for (uint32 r = 0; r < MAX_PP_LANGUAGE; r++) + { + outapp->WriteUInt8(emu->languages[r]); + } - CurrentPosition = outapp->GetWritePosition(); + for (uint32 r = 0; r < structs::MAX_PP_LANGUAGE - MAX_PP_LANGUAGE; r++) + { + outapp->WriteUInt8(0); + } - outapp->WriteString(emu->last_name); + outapp->WriteUInt16(emu->zone_id); + outapp->WriteUInt16(emu->zoneInstance); - outapp->SetWritePosition(CurrentPosition + 32); + outapp->WriteFloat(emu->y); + outapp->WriteFloat(emu->x); + outapp->WriteFloat(emu->z); + outapp->WriteFloat(emu->heading); - outapp->WriteUInt32(emu->birthday); - outapp->WriteUInt32(emu->birthday); // Account start date ? - outapp->WriteUInt32(emu->lastlogin); - outapp->WriteUInt32(emu->timePlayedMin); - outapp->WriteUInt32(emu->timeentitledonaccount); - outapp->WriteUInt32(0x0007ffff); // Expansion bitmask + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(emu->pvp); + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(emu->gm); - outapp->WriteUInt32(structs::MAX_PP_LANGUAGE); + outapp->WriteUInt32(emu->guild_id); + outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. + outapp->WriteUInt32(0); // Unknown - observed 1 in a live packet. + outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < MAX_PP_LANGUAGE; r++) - { - outapp->WriteUInt8(emu->languages[r]); - } + outapp->WriteUInt64(emu->exp); + outapp->WriteUInt8(0); // Unknown - for(uint32 r = 0; r < structs::MAX_PP_LANGUAGE - MAX_PP_LANGUAGE; r++) - { - outapp->WriteUInt8(0); - } + outapp->WriteUInt32(emu->platinum_bank); + outapp->WriteUInt32(emu->gold_bank); + outapp->WriteUInt32(emu->silver_bank); + outapp->WriteUInt32(emu->copper_bank); - outapp->WriteUInt16(emu->zone_id); - outapp->WriteUInt16(emu->zoneInstance); - - outapp->WriteFloat(emu->y); - outapp->WriteFloat(emu->x); - outapp->WriteFloat(emu->z); - outapp->WriteFloat(emu->heading); - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(emu->pvp); - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(emu->gm); - - outapp->WriteUInt32(emu->guild_id); - outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt32(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt8(0); // Unknown - observed 1 in a live packet. - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt64(emu->exp); - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(emu->platinum_bank); - outapp->WriteUInt32(emu->gold_bank); - outapp->WriteUInt32(emu->silver_bank); - outapp->WriteUInt32(emu->copper_bank); - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt32(42); // The meaning of life ? - - for(uint32 r = 0; r < 42; r++) - { outapp->WriteUInt32(0); // Unknown outapp->WriteUInt32(0); // Unknown - } + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(42); // The meaning of life ? - outapp->WriteUInt32(emu->career_tribute_points); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->tribute_points); - outapp->WriteUInt32(0); // Unknown + for (uint32 r = 0; r < 42; r++) + { + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + } - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(EmuConstants::TRIBUTE_SIZE); + outapp->WriteUInt32(emu->career_tribute_points); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->tribute_points); + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < EmuConstants::TRIBUTE_SIZE; r++) - { - outapp->WriteUInt32(emu->tributes[r].tribute); - outapp->WriteUInt32(emu->tributes[r].tier); - } - - outapp->WriteUInt32(10); // Guild Tribute Count ? - - for(uint32 r = 0; r < 10; r++) - { - outapp->WriteUInt32(0xffffffff); - outapp->WriteUInt32(0); - } - - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - // Block of 121 unknown bytes - for(uint32 r = 0; r < 121; r++) + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->currentRadCrystals); - outapp->WriteUInt32(emu->careerRadCrystals); - outapp->WriteUInt32(emu->currentEbonCrystals); - outapp->WriteUInt32(emu->careerEbonCrystals); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(EmuConstants::TRIBUTE_SIZE); - // Unknown String ? - outapp->WriteUInt32(64); // Unknown - for(uint32 r = 0; r < 64; r++) + for (uint32 r = 0; r < EmuConstants::TRIBUTE_SIZE; r++) + { + outapp->WriteUInt32(emu->tributes[r].tribute); + outapp->WriteUInt32(emu->tributes[r].tier); + } + + outapp->WriteUInt32(10); // Guild Tribute Count ? + + for (uint32 r = 0; r < 10; r++) + { + outapp->WriteUInt32(0xffffffff); + outapp->WriteUInt32(0); + } + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + // Block of 121 unknown bytes + for (uint32 r = 0; r < 121; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->currentRadCrystals); + outapp->WriteUInt32(emu->careerRadCrystals); + outapp->WriteUInt32(emu->currentEbonCrystals); + outapp->WriteUInt32(emu->careerEbonCrystals); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + // Unknown String ? + outapp->WriteUInt32(64); // Unknown + for (uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(0); // Unknown - - // Unknown String ? - outapp->WriteUInt32(64); // Unknown - for(uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - // Unknown String ? - outapp->WriteUInt32(64); // Unknown - for(uint32 r = 0; r < 64; r++) + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - // Block of 320 unknown bytes - for(uint32 r = 0; r < 320; r++) outapp->WriteUInt8(0); // Unknown - // Block of 343 unknown bytes - for(uint32 r = 0; r < 343; r++) + outapp->WriteUInt32(0); // Unknown + + // Unknown String ? + outapp->WriteUInt32(64); // Unknown + for (uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + + // Unknown String ? + outapp->WriteUInt32(64); // Unknown + for (uint32 r = 0; r < 64; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(0); // Unknown + + // Block of 320 unknown bytes + for (uint32 r = 0; r < 320; r++) + outapp->WriteUInt8(0); // Unknown + + // Block of 343 unknown bytes + for (uint32 r = 0; r < 343; r++) + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt8(emu->leadAAActive); + + outapp->WriteUInt32(6); // Count ... of LDoN stats ? + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->ldon_points_guk); + outapp->WriteUInt32(emu->ldon_points_mir); + outapp->WriteUInt32(emu->ldon_points_mmc); + outapp->WriteUInt32(emu->ldon_points_ruj); + outapp->WriteUInt32(emu->ldon_points_tak); + + outapp->WriteUInt32(emu->ldon_points_available); + + outapp->WriteDouble(emu->group_leadership_exp); + outapp->WriteDouble(emu->raid_leadership_exp); + + outapp->WriteUInt32(emu->group_leadership_points); + outapp->WriteUInt32(emu->raid_leadership_points); + + outapp->WriteUInt32(64); // Group of 64 int32s follow Group/Raid Leadership abilities ? + + for (uint32 r = 0; r < MAX_LEADERSHIP_AA_ARRAY; r++) + outapp->WriteUInt32(emu->leader_abilities.ranks[r]); + + for (uint32 r = 0; r < 64 - MAX_LEADERSHIP_AA_ARRAY; r++) + outapp->WriteUInt32(0); // Unused/unsupported Leadership abilities + + outapp->WriteUInt32(emu->air_remaining); // ? + + // PVP Stats + + outapp->WriteUInt32(emu->PVPKills); + outapp->WriteUInt32(emu->PVPDeaths); + outapp->WriteUInt32(emu->PVPCurrentPoints); + outapp->WriteUInt32(emu->PVPCareerPoints); + outapp->WriteUInt32(emu->PVPBestKillStreak); + outapp->WriteUInt32(emu->PVPWorstDeathStreak); + outapp->WriteUInt32(emu->PVPCurrentKillStreak); + + // Last PVP Kill + + outapp->WriteString(emu->PVPLastKill.Name); + outapp->WriteUInt32(emu->PVPLastKill.Level); + outapp->WriteUInt32(emu->PVPLastKill.Race); + outapp->WriteUInt32(emu->PVPLastKill.Class); + outapp->WriteUInt32(emu->PVPLastKill.Zone); + outapp->WriteUInt32(emu->PVPLastKill.Time); + outapp->WriteUInt32(emu->PVPLastKill.Points); + + // Last PVP Death + + outapp->WriteString(emu->PVPLastDeath.Name); + outapp->WriteUInt32(emu->PVPLastDeath.Level); + outapp->WriteUInt32(emu->PVPLastDeath.Race); + outapp->WriteUInt32(emu->PVPLastDeath.Class); + outapp->WriteUInt32(emu->PVPLastDeath.Zone); + outapp->WriteUInt32(emu->PVPLastDeath.Time); + outapp->WriteUInt32(emu->PVPLastDeath.Points); + + outapp->WriteUInt32(emu->PVPNumberOfKillsInLast24Hours); + + // Last 50 Kills + outapp->WriteUInt32(50); + for (uint32 r = 0; r < 50; ++r) + { + outapp->WriteString(emu->PVPRecentKills[r].Name); + outapp->WriteUInt32(emu->PVPRecentKills[r].Level); + outapp->WriteUInt32(emu->PVPRecentKills[r].Race); + outapp->WriteUInt32(emu->PVPRecentKills[r].Class); + outapp->WriteUInt32(emu->PVPRecentKills[r].Zone); + outapp->WriteUInt32(emu->PVPRecentKills[r].Time); + outapp->WriteUInt32(emu->PVPRecentKills[r].Points); + } + + outapp->WriteUInt32(emu->expAA); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt8(emu->groupAutoconsent); + outapp->WriteUInt8(emu->raidAutoconsent); + outapp->WriteUInt8(emu->guildAutoconsent); + outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->level); // Level3 ? - outapp->WriteUInt8(emu->leadAAActive); + outapp->WriteUInt8(emu->showhelm); - outapp->WriteUInt32(6); // Count ... of LDoN stats ? - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->ldon_points_guk); - outapp->WriteUInt32(emu->ldon_points_mir); - outapp->WriteUInt32(emu->ldon_points_mmc); - outapp->WriteUInt32(emu->ldon_points_ruj); - outapp->WriteUInt32(emu->ldon_points_tak); + outapp->WriteUInt32(emu->RestTimer); - outapp->WriteUInt32(emu->ldon_points_available); + outapp->WriteUInt32(1024); // Unknown Count - outapp->WriteDouble(emu->group_leadership_exp); - outapp->WriteDouble(emu->raid_leadership_exp); + // Block of 1024 unknown bytes + outapp->WriteUInt8(31); // Unknown - outapp->WriteUInt32(emu->group_leadership_points); - outapp->WriteUInt32(emu->raid_leadership_points); + for (uint32 r = 0; r < 1023; r++) + outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(64); // Group of 64 int32s follow Group/Raid Leadership abilities ? + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(0); // Unknown - for(uint32 r = 0; r < MAX_LEADERSHIP_AA_ARRAY; r++) - outapp->WriteUInt32(emu->leader_abilities.ranks[r]); + // Think we need 1 byte of padding at the end - for(uint32 r = 0; r < 64 - MAX_LEADERSHIP_AA_ARRAY; r++) - outapp->WriteUInt32(0); // Unused/unsupported Leadership abilities - - outapp->WriteUInt32(emu->air_remaining); // ? - - // PVP Stats - - outapp->WriteUInt32(emu->PVPKills); - outapp->WriteUInt32(emu->PVPDeaths); - outapp->WriteUInt32(emu->PVPCurrentPoints); - outapp->WriteUInt32(emu->PVPCareerPoints); - outapp->WriteUInt32(emu->PVPBestKillStreak); - outapp->WriteUInt32(emu->PVPWorstDeathStreak); - outapp->WriteUInt32(emu->PVPCurrentKillStreak); - - // Last PVP Kill - - outapp->WriteString(emu->PVPLastKill.Name); - outapp->WriteUInt32(emu->PVPLastKill.Level); - outapp->WriteUInt32(emu->PVPLastKill.Race); - outapp->WriteUInt32(emu->PVPLastKill.Class); - outapp->WriteUInt32(emu->PVPLastKill.Zone); - outapp->WriteUInt32(emu->PVPLastKill.Time); - outapp->WriteUInt32(emu->PVPLastKill.Points); - - // Last PVP Death - - outapp->WriteString(emu->PVPLastDeath.Name); - outapp->WriteUInt32(emu->PVPLastDeath.Level); - outapp->WriteUInt32(emu->PVPLastDeath.Race); - outapp->WriteUInt32(emu->PVPLastDeath.Class); - outapp->WriteUInt32(emu->PVPLastDeath.Zone); - outapp->WriteUInt32(emu->PVPLastDeath.Time); - outapp->WriteUInt32(emu->PVPLastDeath.Points); - - outapp->WriteUInt32(emu->PVPNumberOfKillsInLast24Hours); - - // Last 50 Kills - outapp->WriteUInt32(50); - for(uint32 r = 0; r < 50; ++r) - { - outapp->WriteString(emu->PVPRecentKills[r].Name); - outapp->WriteUInt32(emu->PVPRecentKills[r].Level); - outapp->WriteUInt32(emu->PVPRecentKills[r].Race); - outapp->WriteUInt32(emu->PVPRecentKills[r].Class); - outapp->WriteUInt32(emu->PVPRecentKills[r].Zone); - outapp->WriteUInt32(emu->PVPRecentKills[r].Time); - outapp->WriteUInt32(emu->PVPRecentKills[r].Points); - } - - - outapp->WriteUInt32(emu->expAA); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(emu->groupAutoconsent); - outapp->WriteUInt8(emu->raidAutoconsent); - outapp->WriteUInt8(emu->guildAutoconsent); - - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(emu->level); // Level3 ? - - outapp->WriteUInt8(emu->showhelm); - - outapp->WriteUInt32(emu->RestTimer); - - outapp->WriteUInt32(1024); // Unknown Count - - // Block of 1024 unknown bytes - outapp->WriteUInt8(31); // Unknown - - for(uint32 r = 0; r < 1023; r++) outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(0); // Unknown + _log(NET__STRUCTS, "Player Profile Packet is %i bytes", outapp->GetWritePosition()); - // Think we need 1 byte of padding at the end + unsigned char *NewBuffer = new unsigned char[outapp->GetWritePosition()]; + memcpy(NewBuffer, outapp->pBuffer, outapp->GetWritePosition()); + safe_delete_array(outapp->pBuffer); + outapp->pBuffer = NewBuffer; + outapp->size = outapp->GetWritePosition(); + outapp->SetWritePosition(4); + outapp->WriteUInt32(outapp->size - 9); - outapp->WriteUInt8(0); // Unknown + CRC32::SetEQChecksum(outapp->pBuffer, outapp->size - 1, 8); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - - _log(NET__STRUCTS, "Player Profile Packet is %i bytes", outapp->GetWritePosition()); - - unsigned char *NewBuffer = new unsigned char[outapp->GetWritePosition()]; - memcpy(NewBuffer, outapp->pBuffer, outapp->GetWritePosition()); - safe_delete_array(outapp->pBuffer); - outapp->pBuffer = NewBuffer; - outapp->size = outapp->GetWritePosition(); - outapp->SetWritePosition(4); - outapp->WriteUInt32(outapp->size - 9); - - - CRC32::SetEQChecksum(outapp->pBuffer, outapp->size - 1, 8); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp, ack_req); - - delete in; - - return; - -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - - eq->FogDensity = emu->fog_density; - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown800 = -1; - eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; - eq->unknown888 = 1; - eq->unknown889 = 0; - eq->unknown890 = 1; - eq->unknown891 = 0; - eq->unknown892 = 0; - eq->unknown893 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown895 = 0; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 180; - eq->unknown908 = 2; - eq->unknown912 = 2; - eq->unknown932 = -1; // Set from PoK Example - eq->unknown936 = -1; // Set from PoK Example - eq->unknown944 = 1.0; // Set from PoK Example - - FINISH_ENCODE(); -} - - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + dest->FastQueuePacket(&outapp, ack_req); delete in; return; } - int PacketSize = 2; - - for(int i = 0; i < EntryCount; ++i, ++emu) - PacketSize += (12 + strlen(emu->name)); - - emu = (Track_Struct *) __emu_buffer; - - in->size = PacketSize; - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); - - for(int i = 0; i < EntryCount; ++i, ++emu) + ENCODE(OP_RaidJoin) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - VARSTRUCT_ENCODE_STRING(Buffer, emu->name); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; } - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_PetBuffWindow) -{ - // The format of the RoF packet is identical to the OP_BuffCreate packet. - - SETUP_VAR_ENCODE(PetBuff_Struct); - - uint32 sz = 12 + (17 * emu->buffcount); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - __packet->WriteUInt32(emu->petid); - __packet->WriteUInt32(0); // PlayerID ? - __packet->WriteUInt8(1); // 1 indicates all buffs on the pet (0 to add or remove a single buff) - __packet->WriteUInt16(emu->buffcount); - - for(uint16 i = 0; i < BUFF_COUNT; ++i) + ENCODE(OP_RaidUpdate) { - if(emu->spellid[i]) + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates { - __packet->WriteUInt32(i); - __packet->WriteUInt32(emu->spellid[i]); - __packet->WriteUInt32(emu->ticsremaining[i]); - __packet->WriteUInt32(0); // Unknown - __packet->WriteString(""); + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 14 || raid_gen->action == 30) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); + } + else + { + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); + } + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + OUT(invslot); + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_RecipeAutoCombine) + { + ENCODE_LENGTH_EXACT(RecipeAutoCombine_Struct); + SETUP_DIRECT_ENCODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); + + OUT(object_type); + OUT(some_id); + eq->container_slot = ServerToRoFSlot(emu->unknown1); + structs::ItemSlotStruct RoFSlot; + RoFSlot.SlotType = 8; // Observed + RoFSlot.Unknown02 = 0; + RoFSlot.MainSlot = 0xffff; + RoFSlot.SubSlot = 0xffff; + RoFSlot.AugSlot = 0xffff; + RoFSlot.Unknown01 = 0; + eq->unknown_slot = RoFSlot; + OUT(recipe_id); + OUT(reply_code); + + FINISH_ENCODE(); + } + + ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } + + ENCODE(OP_RequestClientZoneChange) + { + ENCODE_LENGTH_EXACT(RequestClientZoneChange_Struct); + SETUP_DIRECT_ENCODE(RequestClientZoneChange_Struct, structs::RequestClientZoneChange_Struct); + + OUT(zone_id); + OUT(instance_id); + OUT(y); + OUT(x); + OUT(z); + OUT(heading); + eq->type = 0x0b; + eq->unknown004 = 0xffffffff; + eq->unknown172 = 0x0168b500; + + FINISH_ENCODE(); + } + + ENCODE(OP_RespondAA) + { + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + eq->aa_spent = emu->aa_spent; + // These fields may need to be correctly populated at some point + eq->aapoints_assigned = emu->aa_spent + 1; + eq->aa_spent_general = 0; + eq->aa_spent_archetype = 0; + eq->aa_spent_class = 0; + eq->aa_spent_special = 0; + + for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + { + eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; + eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; + eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_RezzRequest) + { + SETUP_DIRECT_ENCODE(Resurrect_Struct, structs::Resurrect_Struct); + + OUT(zone_id); + OUT(instance_id); + OUT(y); + OUT(x); + OUT(z); + OUT_str(your_name); + OUT_str(rezzer_name); + OUT(spellid); + OUT_str(corpse_name); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 5 is for Live + if (emu->clientver <= 5) + { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? -1 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? -1 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + eq->unknown037 = 1; // Introduced during HoT + OUT(prereq_skill); + eq->unknown045 = 1; // New Mar 21 2012 - Seen 1 + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + eq->unknown057 = 1; // Introduced during HoT + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + _hex(NET__ERROR, eq, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + //eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->class_ = emu->class_[r]; + eq2->race = emu->race[r]; + eq2->level = emu->level[r]; + eq2->class_2 = emu->class_[r]; + eq2->race2 = emu->race[r]; + eq2->zone = emu->zone[r]; + eq2->instance = 0; + eq2->gender = emu->gender[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].equip2 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].equip3 = emu->equip[r][k]; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->u15 = 0xff; + eq2->u19 = 0xFF; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + eq2->deity = emu->deity[r]; + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->beard = emu->beard[r]; + eq2->char_enabled = 1; + eq2->tutorial = emu->tutorial[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->unknown1 = 0; + eq2->gohome = emu->gohome[r]; + eq2->LastLogin = 1212696584; + eq2->unknown2 = 0; + } + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendMembership) + { + ENCODE_LENGTH_EXACT(Membership_Struct); + SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct); + + eq->membership = emu->membership; + eq->races = emu->races; + eq->classes = emu->classes; + eq->entrysize = 22; + for (int i = 0; i<21; i++) + { + eq->entries[i] = emu->entries[i]; + } + eq->entries[21] = 0; + + FINISH_ENCODE(); + } + + ENCODE(OP_SendZonepoints) + { + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); + + eq->count = emu->count; + for (uint32 i = 0; i < emu->count; ++i) + { + eq->zpe[i].iterator = emu->zpe[i].iterator; + eq->zpe[i].x = emu->zpe[i].x; + eq->zpe[i].y = emu->zpe[i].y; + eq->zpe[i].z = emu->zpe[i].z; + eq->zpe[i].heading = emu->zpe[i].heading; + eq->zpe[i].zoneid = emu->zpe[i].zoneid; + eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SetGuildRank) + { + ENCODE_LENGTH_EXACT(GuildSetRank_Struct); + SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); + + eq->GuildID = emu->Unknown00; + + /* Translate older ranks to new values */ + switch (emu->Rank) { + case 0: { eq->Rank = 5; break; } // GUILD_MEMBER 0 + case 1: { eq->Rank = 3; break; } // GUILD_OFFICER 1 + case 2: { eq->Rank = 1; break; } // GUILD_LEADER 2 + default: { eq->Rank = emu->Rank; break; } + } + + memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); + OUT(Banker); + eq->Unknown76 = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerBuy) + { + ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); + SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + OUT(npcid); + OUT(playerid); + OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToRoFMainInvSlot(emu->itemslot); + //OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopRequest) + { + ENCODE_LENGTH_EXACT(Merchant_Click_Struct); + SETUP_DIRECT_ENCODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); + + OUT(npcid); + OUT(playerid); + OUT(command); + OUT(rate); + eq->unknown01 = 3; // Not sure what these values do yet, but list won't display without them + eq->unknown02 = 2592000; + + FINISH_ENCODE(); + } + + ENCODE(OP_SkillUpdate) + { + ENCODE_LENGTH_EXACT(SkillUpdate_Struct); + SETUP_DIRECT_ENCODE(SkillUpdate_Struct, structs::SkillUpdate_Struct); + + OUT(skillId); + OUT(value); + eq->unknown08 = 1; // Observed + eq->unknown09 = 80; // Observed + eq->unknown10 = 136; // Observed + eq->unknown11 = 54; // Observed + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strncpy(eq->model_name, emu->model_name, sizeof(eq->model_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *emu_buffer = in->pBuffer; + + SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; + + if (sas->type != AT_Size) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + + ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; + + css->EntityID = sas->spawn_id; + css->Size = (float)sas->parameter; + css->Unknown08 = 0; + css->Unknown12 = 1.0f; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + strncpy(eq[r].name, emu[r].name, sizeof(eq[r].name)); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0082 = 0; + eq[r].unknown0083 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0084 = 0; + eq[r].unknown0085 = 0; + eq[r].unknown0086 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } + + ENCODE(OP_TaskDescription) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskDescription, in->size + 1); + // Set the Write pointer as we don't know what has been done with the packet before we get it. + in->SetReadPosition(0); + // Copy the header + for (int i = 0; i < 5; ++i) + outapp->WriteUInt32(in->ReadUInt32()); + + // Copy Title + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + outapp->WriteUInt8(0); + + outapp->WriteUInt32(in->ReadUInt32()); // Duration + outapp->WriteUInt32(in->ReadUInt32()); // Unknown + uint32 StartTime = in->ReadUInt32(); + outapp->WriteUInt32(time(nullptr) - StartTime); // RoF has elapsed time here rather than starttime + + // Copy the rest of the packet verbatim + uint32 BytesLeftToCopy = in->size - in->GetReadPosition(); + memcpy(outapp->pBuffer + outapp->GetWritePosition(), in->pBuffer + in->GetReadPosition(), BytesLeftToCopy); + + delete in; + dest->FastQueuePacket(&outapp, ack_req); + } + + ENCODE(OP_TaskHistoryReply) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + // First we need to calculate the length of the new packet + in->SetReadPosition(4); + uint32 ActivityCount = in->ReadUInt32(); + + uint32 Text1Length = 0; + uint32 Text2Length = 0; + uint32 Text3Length = 0; + + uint32 OutboundPacketSize = 8; + + for (uint32 i = 0; i < ActivityCount; ++i) + { + Text1Length = 0; + Text2Length = 0; + Text3Length = 0; + + in->ReadUInt32(); // Activity type + + // Skip past Text1 + while (in->ReadUInt8()) + ++Text1Length; + + // Skip past Text2 + while (in->ReadUInt8()) + ++Text2Length; + + in->ReadUInt32(); + in->ReadUInt32(); + in->ReadUInt32(); + uint32 ZoneID = in->ReadUInt32(); + in->ReadUInt32(); + + // Skip past Text3 + while (in->ReadUInt8()) + ++Text3Length; + + char ZoneNumber[10]; + + sprintf(ZoneNumber, "%i", ZoneID); + + OutboundPacketSize += (24 + Text1Length + 1 + Text2Length + Text3Length + 1 + 7 + (strlen(ZoneNumber) * 2)); + } + + in->SetReadPosition(0); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_TaskHistoryReply, OutboundPacketSize); + + outapp->WriteUInt32(in->ReadUInt32()); // Task index + outapp->WriteUInt32(in->ReadUInt32()); // Activity count + + for (uint32 i = 0; i < ActivityCount; ++i) + { + Text1Length = 0; + Text2Length = 0; + Text3Length = 0; + + outapp->WriteUInt32(in->ReadUInt32()); // ActivityType + + // Copy Text1 + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + + outapp->WriteUInt8(0); // Text1 has a null terminator + + uint32 CurrentPosition = in->GetReadPosition(); + + // Determine Length of Text2 + while (in->ReadUInt8()) + ++Text2Length; + + outapp->WriteUInt32(Text2Length); + + in->SetReadPosition(CurrentPosition); + + // Copy Text2 + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + + outapp->WriteUInt32(in->ReadUInt32()); // Goalcount + in->ReadUInt32(); + in->ReadUInt32(); + uint32 ZoneID = in->ReadUInt32(); + in->ReadUInt32(); + + char ZoneNumber[10]; + + sprintf(ZoneNumber, "%i", ZoneID); + + outapp->WriteUInt32(2); + outapp->WriteUInt8(0x2d); // "-" + outapp->WriteUInt8(0x31); // "1" + + outapp->WriteUInt32(2); + outapp->WriteUInt8(0x2d); // "-" + outapp->WriteUInt8(0x31); // "1" + outapp->WriteString(ZoneNumber); + + outapp->WriteUInt32(0); + + // Copy Tex3t + while (uint8 c = in->ReadUInt8()) + outapp->WriteUInt8(c); + + outapp->WriteUInt8(0); // Text3 has a null terminator + + outapp->WriteUInt8(0x31); // "1" + outapp->WriteString(ZoneNumber); + } + + delete in; + dest->FastQueuePacket(&outapp, ack_req); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + int PacketSize = 2; + + for (int i = 0; i < EntryCount; ++i, ++emu) + PacketSize += (12 + strlen(emu->name)); + + emu = (Track_Struct *)__emu_buffer; + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); + + for (int i = 0; i < EntryCount; ++i, ++emu) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + VARSTRUCT_ENCODE_STRING(Buffer, emu->name); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size == sizeof(ClickTrader_Struct)) + { + ENCODE_LENGTH_EXACT(ClickTrader_Struct); + SETUP_DIRECT_ENCODE(ClickTrader_Struct, structs::ClickTrader_Struct); + + eq->Code = emu->Code; + // Live actually has 200 items now, but 80 is the most our internal struct supports + for (uint32 i = 0; i < 200; i++) + { + strncpy(eq->items[i].SerialNumber, "0000000000000000", sizeof(eq->items[i].SerialNumber)); + eq->items[i].Unknown18 = 0; + if (i < 80) { + eq->ItemCost[i] = emu->ItemCost[i]; + } + else { + eq->ItemCost[i] = 0; + } + } + + FINISH_ENCODE(); + } + else if ((*p)->size == sizeof(Trader_ShowItems_Struct)) + { + ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct); + SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); + + eq->Code = emu->Code; + strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber)); + eq->TraderID = emu->TraderID; + eq->Stacksize = 0; + eq->Price = 0; + + FINISH_ENCODE(); + } + else if ((*p)->size == sizeof(TraderStatus_Struct)) + { + ENCODE_LENGTH_EXACT(TraderStatus_Struct); + SETUP_DIRECT_ENCODE(TraderStatus_Struct, structs::TraderStatus_Struct); + + eq->Code = emu->Code; + + FINISH_ENCODE(); + } + else if ((*p)->size == sizeof(TraderBuy_Struct)) + { + ENCODE_FORWARD(OP_TraderBuy); } } - __packet->WriteUInt8(0); // Unknown - FINISH_ENCODE(); -} - -ENCODE(OP_Barter) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - - if(SubAction != Barter_BuyerAppearance) + ENCODE(OP_TraderBuy) { - dest->FastQueuePacket(&in, ack_req); + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = 80; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - char Name[64]; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); - uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); - uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - VARSTRUCT_DECODE_STRING(Name, Buffer); - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - OutBuffer = (char *)in->pBuffer + 72; - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) - { - dest->FastQueuePacket(&in, ack_req); - - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; - } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - - in->pBuffer = new unsigned char[in->size]; - - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) - { - OUT(Beginning.Action); - OUT(SellerID); - memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); - OUT(NumItems); - OUT(ItemID); - OUT(SerialNumber); + OUT(Action); + OUT(Price); + OUT(TraderID); memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(Cost); - OUT(ItemStat); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_TributeInfo) + { + ENCODE_LENGTH_ATLEAST(TributeAbility_Struct); + SETUP_VAR_ENCODE(TributeAbility_Struct); + ALLOC_VAR_ENCODE(structs::TributeAbility_Struct, sizeof(structs::TributeAbility_Struct) + strlen(emu->name) + 1); - dest->FastQueuePacket(&in, ack_req); -} + OUT(tribute_id); + OUT(tier_count); -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) -{ + for (uint32 i = 0; i < MAX_TRIBUTE_TIERS; ++i) + { + eq->tiers[i].level = emu->tiers[i].level; + eq->tiers[i].tribute_item_id = emu->tiers[i].tribute_item_id; + eq->tiers[i].cost = emu->tiers[i].cost; + } + + eq->unknown128 = 0; + + strcpy(eq->name, emu->name); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToRoFSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (unsigned int i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(hero_forge_model); + OUT(unknown18); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_WhoAllResponse) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *InBuffer = (char *)in->pBuffer; + + WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; + + int Count = wars->playercount; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); + + char *OutBuffer = (char *)outapp->pBuffer; + + // The struct fields were moved around a bit, so adjust values before copying + wars->unknown44[0] = Count; + wars->unknown52 = 0; + + memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); + + OutBuffer += sizeof(WhoAllReturnStruct); + InBuffer += sizeof(WhoAllReturnStruct); + + for (int i = 0; i < Count; ++i) + { + uint32 x; + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + InBuffer += 4; + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); + + char Name[64]; + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + for (int j = 0; j < 7; ++j) + { + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + delete in; + } + + ENCODE(OP_ZoneChange) + { + ENCODE_LENGTH_EXACT(ZoneChange_Struct); + SETUP_DIRECT_ENCODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(eq->char_name, emu->char_name, sizeof(emu->char_name)); + OUT(zoneID); + OUT(instanceID); + OUT(y); + OUT(x); + OUT(z) + OUT(zone_reason); + OUT(success); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = 0; + zph->bind_instance_id = zps->bind_instance_id; + strncpy(zph->zone_name, zps->zone_name, sizeof(zph->zone_name)); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneSpawns) + { //consume the packet EQApplicationPacket *in = *p; *p = nullptr; //store away the emu struct unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - emu = (Spawn_Struct *) __emu_buffer; + emu = (Spawn_Struct *)__emu_buffer; //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - char *Buffer = (char *) in->pBuffer, *BufferStart; - + char *Buffer = (char *)in->pBuffer, *BufferStart; int r; int k; - for(r = 0; r < entrycount; r++, emu++) { + for (r = 0; r < entrycount; r++, emu++) { int PacketSize = 206; @@ -1856,14 +3637,14 @@ ENCODE(OP_ZoneSpawns) emu->title[0] = 0; emu->suffix[0] = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) PacketSize += strlen(emu->title) + 1; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) PacketSize += strlen(emu->suffix) + 1; bool ShowName = 1; - if(emu->bodytype >= 66) + if (emu->bodytype >= 66) { emu->race = 127; emu->bodytype = 11; @@ -1872,11 +3653,11 @@ ENCODE(OP_ZoneSpawns) } float SpawnSize = emu->size; - if(!((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) + if (!((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) { PacketSize += 60; - if(emu->size == 0) + if (emu->size == 0) { emu->size = 6; SpawnSize = 6; @@ -1885,13 +3666,13 @@ ENCODE(OP_ZoneSpawns) else PacketSize += 216; - if(SpawnSize == 0) + if (SpawnSize == 0) { SpawnSize = 3; } EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - Buffer = (char *) outapp->pBuffer; + Buffer = (char *)outapp->pBuffer; BufferStart = Buffer; VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); @@ -1924,10 +3705,10 @@ ENCODE(OP_ZoneSpawns) uint8 OtherData = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) OtherData = OtherData | 16; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) OtherData = OtherData | 32; VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); @@ -1962,10 +3743,9 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // ShowEQ calls this 'Holding' VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); - if(emu->NPC) + if (emu->NPC) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); @@ -1976,10 +3756,10 @@ ENCODE(OP_ZoneSpawns) /* Translate older ranks to new values */ switch (emu->guildrank) { - case 0: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); break; } // GUILD_MEMBER 0 - case 1: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); break; } // GUILD_OFFICER 1 - case 2: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); break; } // GUILD_LEADER 2 - default: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); break; } // + case 0: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 5); break; } // GUILD_MEMBER 0 + case 1: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 3); break; } // GUILD_OFFICER 1 + case 2: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); break; } // GUILD_LEADER 2 + default: { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); break; } // } } @@ -2005,9 +3785,9 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown18 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown19 - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { - for(k = 0; k < 9; ++k) + for (k = 0; k < 9; ++k) { { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); @@ -2016,7 +3796,7 @@ ENCODE(OP_ZoneSpawns) structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - for(k = 0; k < 9; k++) { + for (k = 0; k < 9; k++) { Equipment[k].equip0 = emu->equipment[k]; Equipment[k].equip1 = 0; Equipment[k].equip2 = 0; @@ -2047,7 +3827,6 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); } - structs::Spawn_Struct_Position *Position = (structs::Spawn_Struct_Position*)Buffer; Position->deltaX = emu->deltaX; @@ -2062,12 +3841,12 @@ ENCODE(OP_ZoneSpawns) Buffer += sizeof(structs::Spawn_Struct_Position); - if(strlen(emu->title)) + if (strlen(emu->title)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->title); } - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); } @@ -2079,1989 +3858,615 @@ ENCODE(OP_ZoneSpawns) VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // 29 zero bytes follow Buffer += 29; - if(Buffer != (BufferStart + PacketSize)) + if (Buffer != (BufferStart + PacketSize)) { _log(NET__ERROR, "SPAWN ENCODE LOGIC PROBLEM: Buffer pointer is now %i from end", Buffer - (BufferStart + PacketSize)); } //_log(NET__ERROR, "Sending zone spawn for %s packet is %i bytes", emu->name, outapp->size); //_hex(NET__ERROR, outapp->pBuffer, outapp->size); dest->FastQueuePacket(&outapp, ack_req); - } - - delete in; -} - -ENCODE(OP_MercenaryDataResponse) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; - PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); - - for(r = 0; r < emu->MercTypeCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); - } - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Status); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName - for(k = 0; k < emu->Mercs[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); } - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_MercenaryDataUpdate) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - EQApplicationPacket *outapp; - - uint32 PacketSize = 0; - - // There are 2 different sized versions of this packet depending if a merc is hired or not - if (emu->MercStatus >= 0) - { - PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; - PacketSize += strlen(emu->MercData[r].MercName); // Null Terminator size already accounted for in the struct - } - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Status); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); - //VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // MercName - VARSTRUCT_ENCODE_STRING(Buffer,emu->MercData[r].MercName); - for(k = 0; k < emu->MercData[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); // MercUnk05 - } - } - else - { - PacketSize += sizeof(structs::NoMercenaryHired_Struct); - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; - - dest->FastQueuePacket(&in, ack_req); - - return; - } - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); delete in; - - return; } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - // Guild ID - buffer += sizeof(uint32); - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - - /* Translate older ranks to new values */ - switch (emu_e->rank) { - case 0: { e->rank = htonl(5); break; } // GUILD_MEMBER 0 - case 1: { e->rank = htonl(3); break; } // GUILD_OFFICER 1 - case 2: { e->rank = htonl(1); break; } // GUILD_LEADER 2 - default: { e->rank = htonl(emu_e->rank); break; } // GUILD_NONE - } - - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - e->unknown01 = 0; - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - e->unknown_one2 = htonl(1); - e->unknown04 = 0; - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strncpy(eq[r].name, emu[r].name, sizeof(eq[r].name)); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0082 = 0; - eq[r].unknown0083 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0084 = 0; - eq[r].unknown0085 = 0; - eq[r].unknown0086 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) -{ - - // We are not encoding the spawn_id field here, but it doesn't appear to matter. - // - EQApplicationPacket *in = *p; - *p = nullptr; - - Object_Struct *emu = (Object_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->object_name) + sizeof(Object_Struct) - 1; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); // Some unique id - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Same for all objects in the zone - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Normally 0, but seen (float)255.0 as well - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, 1); // Need to add emu->size to struct - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); - VARSTRUCT_ENCODE_TYPE(int32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ClickObjectAction) { - ENCODE_LENGTH_EXACT(ClickObjectAction_Struct); - SETUP_DIRECT_ENCODE(ClickObjectAction_Struct, structs::ClickObjectAction_Struct); - OUT(drop_id); - eq->unknown04 = -1; - eq->unknown08 = -1; - OUT(type); - OUT(icon); - eq->unknown16 = 0; - OUT_str(object_name); - FINISH_ENCODE(); -} - -ENCODE(OP_SendMembership) { - ENCODE_LENGTH_EXACT(Membership_Struct); - SETUP_DIRECT_ENCODE(Membership_Struct, structs::Membership_Struct); - - eq->membership = emu->membership; - eq->races = emu->races; - eq->classes = emu->classes; - eq->entrysize = 22; - for (int i=0; i<21; i++) +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - eq->entries[i] = emu->entries[i]; - } - eq->entries[21] = 0; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - FINISH_ENCODE(); -} + IN(npcid); + emu->slot = RoFToServerMainInvSlot(eq->slot); + IN(charges); + IN(sell_price); -ENCODE(OP_GMTrainSkillConfirm) { - ENCODE_LENGTH_EXACT(GMTrainSkillConfirm_Struct); - SETUP_DIRECT_ENCODE(GMTrainSkillConfirm_Struct, structs::GMTrainSkillConfirm_Struct); - OUT(SkillID); - OUT(Cost); - OUT(NewSkill); - OUT_str(TrainerName); - FINISH_ENCODE(); -} - -ENCODE(OP_GMLastName) { - ENCODE_LENGTH_EXACT(GMLastName_Struct); - SETUP_DIRECT_ENCODE(GMLastName_Struct, structs::GMLastName_Struct); - OUT_str(name); - OUT_str(gmname); - OUT_str(lastname); - for (int i=0; i<4; i++) - { - eq->unknown[i] = emu->unknown[i]; - } - FINISH_ENCODE(); -} - -ENCODE(OP_SkillUpdate) { - ENCODE_LENGTH_EXACT(SkillUpdate_Struct); - SETUP_DIRECT_ENCODE(SkillUpdate_Struct, structs::SkillUpdate_Struct); - OUT(skillId); - OUT(value); - eq->unknown08 = 1; // Observed - eq->unknown09 = 80; // Observed - eq->unknown10 = 136; // Observed - eq->unknown11 = 54; // Observed - FINISH_ENCODE(); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_RequestClientZoneChange) { - ENCODE_LENGTH_EXACT(RequestClientZoneChange_Struct); - SETUP_DIRECT_ENCODE(RequestClientZoneChange_Struct, structs::RequestClientZoneChange_Struct); - OUT(zone_id); - OUT(instance_id); - OUT(y); - OUT(x); - OUT(z); - OUT(heading); - eq->type = 0x0b; - eq->unknown004 = 0xffffffff; - eq->unknown172 = 0x0168b500; - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - - // This packet is variable sized now, but forcing it to the old packet size for now. - eq->Title_Count = 128; - memcpy(eq->Title, emu->Title, sizeof(eq->Title)); - eq->Text_Count = 4096; - memcpy(eq->Text, emu->Text, sizeof(eq->Text)); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - OUT(NegativeID); - // These two field names are used if Buttons == 1. We should add an interface to them via Perl. - eq->ButtonName0_Count = 25; - OUT_str(ButtonName0); - eq->ButtonName1_Count = 25; - OUT_str(ButtonName1); - - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - eq->unknown316 = -1; // Observed - - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerBuy) -{ - ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); - SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - OUT(npcid); - OUT(playerid); - OUT(itemslot); - OUT(quantity); - OUT(price); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteSpawn) -{ - ENCODE_LENGTH_EXACT(DeleteSpawn_Struct); - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - eq->unknown04 = 1; // Observed - - FINISH_ENCODE(); -} - - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strncpy(eq->worldshortname, emu->worldshortname, sizeof(eq->worldshortname)); - - //OUT(enablevoicemacros); // These two are lost, but must be one of the 1s in unknown[249] - //OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - eq->unknown016 = 1; - eq->unknown020[0] = 1; - - eq->unknown249[0] = 1; - eq->unknown249[1] = 1; - eq->unknown249[8] = 1; - eq->unknown249[9] = 1; - eq->unknown249[12] = 1; - eq->unknown249[14] = 1; - eq->unknown249[15] = 1; - eq->unknown249[16] = 1; - - eq->unknown276[0] = 1.0f; - eq->unknown276[1] = 1.0f; - eq->unknown276[6] = 1.0f; - - FINISH_ENCODE(); -} - -ENCODE(OP_Animation) { - ENCODE_LENGTH_EXACT(Animation_Struct); - SETUP_DIRECT_ENCODE(Animation_Struct, structs::Animation_Struct); - OUT(spawnid); - OUT(value); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); - OUT(target); - OUT(source); - OUT(level); - eq->unknown06 = 0; - eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->bard_focus_id = emu->bard_focus_id; - eq->knockback_angle = emu->sequence; - eq->unknown22 = 0; - OUT(type); - eq->damage = 0; - eq->unknown31 = 0; - OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown39 = 14; - eq->unknown43 = 0; - eq->unknown44 = 17; - eq->unknown45 = 0; - eq->unknown46 = -1; - eq->unknown50 = 0; - eq->unknown54 = 0; - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); - OUT(entityid); - eq->unknown004 = 2; - //eq->level = 80; - //eq->effect = 0; - OUT(level); - OUT(effect); - eq->unknown007 = 0; - eq->unknown008 = 1.0f; - OUT(spellid); - OUT(duration); - eq->playerId = 0x7cde; - OUT(slotid); - if(emu->bufffade == 1) - eq->bufffade = 1; - else - eq->bufffade = 2; - - // Bit of a hack. OP_Buff appears to add/remove the buff while OP_BuffCreate adds/removes the actual buff icon - EQApplicationPacket *outapp = nullptr; - if(eq->bufffade == 1) - { - outapp = new EQApplicationPacket(OP_BuffCreate, 29); - outapp->WriteUInt32(emu->entityid); - outapp->WriteUInt32(0x0271); // Unk - outapp->WriteUInt8(0); // Type of OP_BuffCreate packet ? - outapp->WriteUInt16(1); // 1 buff in this packet - outapp->WriteUInt32(emu->slotid); - outapp->WriteUInt32(0xffffffff); // SpellID (0xffff to remove) - outapp->WriteUInt32(0); // Duration - outapp->WriteUInt32(0); // ? - outapp->WriteUInt8(0); // Caster name - outapp->WriteUInt8(0); // Terminating byte - } - FINISH_ENCODE(); - - if(outapp) - dest->FastQueuePacket(&outapp); // Send the OP_BuffCreate to remove the buff -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_InterruptCast) { - ENCODE_LENGTH_EXACT(InterruptCast_Struct); - SETUP_DIRECT_ENCODE(InterruptCast_Struct, structs::InterruptCast_Struct); - OUT(spawnid); - OUT(messageid); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToRoFMainInvSlot(emu->itemslot); - //OUT(itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToRoFMainInvSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_RecipeAutoCombine) { - ENCODE_LENGTH_EXACT(RecipeAutoCombine_Struct); - SETUP_DIRECT_ENCODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); - OUT(object_type); - OUT(some_id); - eq->container_slot = ServerToRoFSlot(emu->unknown1); - structs::ItemSlotStruct RoFSlot; - RoFSlot.SlotType = 8; // Observed - RoFSlot.Unknown02 = 0; - RoFSlot.MainSlot = 0xffff; - RoFSlot.SubSlot = 0xffff; - RoFSlot.AugSlot = 0xffff; - RoFSlot.Unknown01 = 0; - eq->unknown_slot = RoFSlot; - OUT(recipe_id); - OUT(reply_code); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToRoFSlot(emu->from_slot); - eq->to_slot = ServerToRoFSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToRoFSlot(emu->from_slot); - eq->to_slot = ServerToRoFSlot(emu->to_slot); - OUT(number_in_stack); - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToRoFSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_Trader) { - - if((*p)->size == sizeof(ClickTrader_Struct)) - { - ENCODE_LENGTH_EXACT(ClickTrader_Struct); - SETUP_DIRECT_ENCODE(ClickTrader_Struct, structs::ClickTrader_Struct); - - eq->Code = emu->Code; - // Live actually has 200 items now, but 80 is the most our internal struct supports - for (uint32 i = 0; i < 200; i++) - { - strncpy(eq->items[i].SerialNumber, "0000000000000000", sizeof(eq->items[i].SerialNumber)); - eq->items[i].Unknown18 = 0; - if (i < 80) { - eq->ItemCost[i] = emu->ItemCost[i]; - } else { - eq->ItemCost[i] = 0; - } - } - FINISH_ENCODE(); - } - else if((*p)->size == sizeof(Trader_ShowItems_Struct)) - { - ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct); - SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); - eq->Code = emu->Code; - strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber)); - eq->TraderID = emu->TraderID; - eq->Stacksize = 0; - eq->Price = 0; - FINISH_ENCODE(); - } - else if((*p)->size == sizeof(TraderStatus_Struct)) - { - ENCODE_LENGTH_EXACT(TraderStatus_Struct); - SETUP_DIRECT_ENCODE(TraderStatus_Struct, structs::TraderStatus_Struct); - eq->Code = emu->Code; - FINISH_ENCODE(); - } - else if((*p)->size == sizeof(TraderBuy_Struct)) - { - ENCODE_FORWARD(OP_TraderBuy); - } -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToRoFSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strncpy(eq->model_name, emu->model_name, sizeof(eq->model_name)); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = 0; // Set to hard 0 since it's not required for the structure to work - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = 0; - zph->bind_instance_id = zps->bind_instance_id; - strncpy(zph->zone_name, zps->zone_name, sizeof(zph->zone_name)); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToRoFMainInvSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates - { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; - - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); - } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); - } - delete[] __emu_buffer; -} - -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name)); - vr->items[x].charges = ivr->items[x].charges; - } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + FINISH_DIRECT_DECODE(); } - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_WhoAllResponse) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *InBuffer = (char *)in->pBuffer; - - WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; - - int Count = wars->playercount; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); - - char *OutBuffer = (char *)outapp->pBuffer; - - // The struct fields were moved around a bit, so adjust values before copying - wars->unknown44[0] = Count; - wars->unknown52 = 0; - - memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); - - OutBuffer += sizeof(WhoAllReturnStruct); - InBuffer += sizeof(WhoAllReturnStruct); - - for(int i = 0; i < Count; ++i) + DECODE(OP_AltCurrencySell) { - uint32 x; + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + IN(merchant_entity_id); + emu->slot_id = RoFToServerSlot(eq->slot_id); + IN(charges); + IN(cost); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AltCurrencySellSelection) + { + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = RoFToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = RoFToServerMainInvSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentInfo) + { + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = RoFToServerSlot(eq->container_slot); + emu->augment_slot = RoFToServerSlot(eq->augment_slot); + emu->container_index = eq->container_index; + emu->augment_index = eq->augment_index; + emu->dest_inst_id = eq->dest_inst_id; + emu->augment_action = eq->augment_action; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BazaarSearch) + { + char *Buffer = (char *)__packet->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) + return; + + SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); + MEMSET_IN(structs::NewBazaarInspect_Struct); + + IN(Beginning.Action); + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + IN(SerialNumber); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BlockedBuffs) + { + DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); + SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); + + for (uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) + emu->SpellID[i] = eq->SpellID[i]; + + IN(Count); + IN(Pet); + IN(Initialise); + IN(Flags); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Buff) + { + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Live); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); + + IN(entityid); + //IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BuffRemoveRequest) + { + // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. + // + DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); + SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); + + emu->SlotID = (eq->SlotID < 42) ? eq->SlotID : (eq->SlotID - 17); + + IN(EntityID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + if (eq->slot == 13) + emu->slot = 10; + else + IN(slot); + + IN(spell_id); + emu->inventoryslot = RoFToServerSlot(eq->inventoryslot); + //IN(inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + char *InBuffer = (char *)__eq_buffer; + + char Sender[64]; + char Target[64]; + + VARSTRUCT_DECODE_STRING(Sender, InBuffer); + VARSTRUCT_DECODE_STRING(Target, InBuffer); InBuffer += 4; - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); - char Name[64]; + uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + InBuffer += 5; - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; - for(int j = 0; j < 7; ++j) - { - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - } + strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); + strn0cpy(emu->sender, Target, sizeof(emu->sender)); + emu->language = Language; + emu->chan_num = Channel; + emu->skill_in_language = Skill; + strcpy(emu->message, InBuffer); - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + delete[] __eq_buffer; } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - delete in; -} + IN(gender); + IN(race); + IN(class_); + IN(deity); + IN(start_zone); + IN(haircolor); + IN(beard); + IN(beardcolor); + IN(hairstyle); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(tutorial); -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} + FINISH_DIRECT_DECODE(); + } -/*ENCODE(OP_InspectAnswer) { - ENCODE_LENGTH_EXACT(InspectResponse_Struct); - SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - OUT(TargetID); - OUT(playerid); + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = RoFToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Damage) + { + DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); + SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + IN(target); + IN(source); + IN(type); + IN(spellid); + IN(damage); + emu->sequence = eq->sequence; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = RoFToServerSlot(eq->from_slot); + emu->to_slot = RoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_EnvDamage) + { + DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); + SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); + + IN(id); + IN(damage); + IN(dmgtype); + emu->constant = 0xFFFF; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GMLastName) + { + DECODE_LENGTH_EXACT(structs::GMLastName_Struct); + SETUP_DIRECT_DECODE(GMLastName_Struct, structs::GMLastName_Struct); + + memcpy(emu->name, eq->name, sizeof(emu->name)); + memcpy(emu->gmname, eq->gmname, sizeof(emu->gmname)); + memcpy(emu->lastname, eq->lastname, sizeof(emu->lastname)); + for (int i = 0; i<4; i++) + { + emu->unknown[i] = eq->unknown[i]; + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupCancelInvite) + { + DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); + SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + IN(toggle); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupDisband) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_Disband"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupInvite"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); + memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite2) + { + //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); + DECODE_FORWARD(OP_GroupInvite); + } + + DECODE(OP_GuildDemote) + { + DECODE_LENGTH_EXACT(structs::GuildDemoteStruct); + SETUP_DIRECT_DECODE(GuildDemoteStruct, structs::GuildDemoteStruct); + + strn0cpy(emu->target, eq->target, sizeof(emu->target)); + strn0cpy(emu->name, eq->name, sizeof(emu->name)); + // IN(rank); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GuildRemove) + { + DECODE_LENGTH_EXACT(structs::GuildCommand_Struct); + SETUP_DIRECT_DECODE(GuildCommand_Struct, structs::GuildCommand_Struct); + + strn0cpy(emu->othername, eq->othername, sizeof(emu->othername)); + strn0cpy(emu->myname, eq->myname, sizeof(emu->myname)); + IN(guildeqid); + IN(officer); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GuildStatus) + { + DECODE_LENGTH_EXACT(structs::GuildStatus_Struct); + SETUP_DIRECT_DECODE(GuildStatus_Struct, structs::GuildStatus_Struct); + + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + + FINISH_DIRECT_DECODE(); + } + + /*DECODE(OP_InspectAnswer) + { + DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); + SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); + + IN(TargetID); + IN(playerid); int r; for (r = 0; r < 21; r++) { - strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); + strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); } // Swap last 2 slots for Arrow and Power Source - strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); - strn0cpy(eq->unknown_zero, emu->itemnames[21], sizeof(eq->unknown_zero)); + strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); + strn0cpy(emu->itemnames[21], eq->unknown_zero, sizeof(emu->itemnames[21])); + strn0cpy(emu->unknown_zero, eq->unknown_zero, sizeof(emu->unknown_zero)); int k; for (k = 0; k < 21; k++) { - OUT(itemicons[k]); + IN(itemicons[k]); } // Swap last 2 slots for Arrow and Power Source - eq->itemicons[21] = emu->itemicons[22]; - eq->unknown_zero2 = emu->itemicons[21]; - strn0cpy(eq->text, emu->text, sizeof(eq->text)); - - FINISH_ENCODE(); -}*/ - -ENCODE(OP_GroupInvite) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); - memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow2) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupCancelInvite) -{ - ENCODE_LENGTH_EXACT(GroupCancel_Struct); - SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - OUT(toggle); - FINISH_ENCODE(); -} - -ENCODE(OP_SetGuildRank) -{ - ENCODE_LENGTH_EXACT(GuildSetRank_Struct); - SETUP_DIRECT_ENCODE(GuildSetRank_Struct, structs::GuildSetRank_Struct); - eq->GuildID = emu->Unknown00; - - /* Translate older ranks to new values */ - switch (emu->Rank) { - case 0: { eq->Rank = 5; break; } // GUILD_MEMBER 0 - case 1: { eq->Rank = 3; break; } // GUILD_OFFICER 1 - case 2: { eq->Rank = 1; break; } // GUILD_LEADER 2 - default: { eq->Rank = emu->Rank; break; } - } - - memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); - OUT(Banker); - eq->Unknown76 = 1; - FINISH_ENCODE(); -} - - -ENCODE(OP_GroupUpdate) -{ - //_log(NET__ERROR, "OP_GroupUpdate"); - EQApplicationPacket *in = *p; - - GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; - - //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); - if((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) - { - if((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) - { - //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); - dest->FastQueuePacket(&outapp); - - // Make an empty GLAA packet to clear out their useable GLAAs - // - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - dest->FastQueuePacket(&outapp); - - delete in; - - return; - } - //if(gjs->action == groupActLeave) - // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; - return; - - - } - - if(in->size == sizeof(GroupUpdate2_Struct)) - { - // Group Update2 - //_log(NET__ERROR, "Struct is GroupUpdate2"); - - unsigned char *__emu_buffer = in->pBuffer; - GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*) __emu_buffer; - - //_log(NET__ERROR, "Yourname is %s", gu2->yourname); - - int MemberCount = 1; - - int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; - - for(int i = 0; i < 5; ++i) - { - //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); - if(gu2->membername[i][0] != '\0') - { - PacketLength += (22 + strlen(gu2->membername[i]) + 1); - ++MemberCount; - } - } - - //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); - - char *Buffer = (char *)outapp->pBuffer; - - // Header - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); - - // Leader - // - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - - int MemberNumber = 1; - - for(int i = 0; i < 5; ++i) - { - if(gu2->membername[i][0] == '\0') - continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - } - - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = gu2->NPCMarkerID; - memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); - - dest->FastQueuePacket(&outapp); - delete in; - - return; - - } - //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - ENCODE_LENGTH_EXACT(GroupJoin_Struct); - SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); - - memcpy(eq->membername, emu->membername, sizeof(eq->membername)); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = emu->NPCMarkerID; - - memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); - //_hex(NET__ERROR, __packet->pBuffer, __packet->size); - FINISH_ENCODE(); - - dest->FastQueuePacket(&outapp); -} - -ENCODE(OP_ChannelMessage) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildsList) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - uint32 NumberOfGuilds = in->size / 64; - - uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. - - unsigned char *__emu_buffer = in->pBuffer; - - char *InBuffer = (char *)__emu_buffer; - - uint32 HighestGuildID = 0; - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - PacketSize += (5 + strlen(InBuffer)); - HighestGuildID = i - 1; - } - InBuffer += 64; - } - - PacketSize++; // Appears to be an extra 0x00 at the very end. - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - InBuffer = (char *)__emu_buffer; - - char *OutBuffer = (char *)in->pBuffer; - - // Init the first 64 bytes to zero, as per live. - // - memset(OutBuffer, 0, 64); - - OutBuffer += 64; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); - VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); - } - InBuffer += 64; - } - - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->unknown004 = 785316192; - eq->unknown008 = 435601; - strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); - strncpy(eq->leader_name, emu->leader_name, sizeof(eq->leader_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strncpy(eq->expedition_name, emu->expedition_name, sizeof(eq->expedition_name)); - strncpy(eq->player_name, emu->player_name, sizeof(eq->player_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } -ENCODE(OP_BuffCreate) -{ - SETUP_VAR_ENCODE(BuffIcon_Struct); - - uint32 sz = 12 + (17 * emu->count); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - __packet->WriteUInt32(emu->entity_id); - __packet->WriteUInt32(0); // PlayerID ? - __packet->WriteUInt8(1); // 1 indicates all buffs on the player (0 to add or remove a single buff) - __packet->WriteUInt16(emu->count); - - for(uint16 i = 0; i < emu->count; ++i) - { - uint16 buffslot = emu->entries[i].buff_slot; - // Not sure if this is needs amending for RoF yet. - if(emu->entries[i].buff_slot >= 25) - { - buffslot += 17; - } - - __packet->WriteUInt32(buffslot); - __packet->WriteUInt32(emu->entries[i].spell_id); - __packet->WriteUInt32(emu->entries[i].tics_remaining); - __packet->WriteUInt32(0); // Unknown - __packet->WriteString(""); - } - __packet->WriteUInt8(0); // Unknown - - FINISH_ENCODE(); -} - -ENCODE(OP_ZoneChange) -{ - ENCODE_LENGTH_EXACT(ZoneChange_Struct); - SETUP_DIRECT_ENCODE(ZoneChange_Struct, structs::ZoneChange_Struct); - - memcpy(eq->char_name, emu->char_name, sizeof(emu->char_name)); - OUT(zoneID); - OUT(instanceID); - OUT(y); - OUT(x); - OUT(z) - OUT(zone_reason); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(hero_forge_model); - OUT(unknown18); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_SpawnAppearance) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - - SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; - - if(sas->type != AT_Size) - { - dest->FastQueuePacket(&in, ack_req); - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - - ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; - - css->EntityID = sas->spawn_id; - css->Size = (float)sas->parameter; - css->Unknown08 = 0; - css->Unknown12 = 1.0f; - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_CastSpell) -{ - ENCODE_LENGTH_EXACT(CastSpell_Struct); - SETUP_DIRECT_ENCODE(CastSpell_Struct, structs::CastSpell_Struct); - if(emu->slot == 10) - { - eq->slot = 13; - } - else - { - OUT(slot); - } - OUT(spell_id); - eq->inventoryslot = ServerToRoFSlot(emu->inventoryslot); - //OUT(inventoryslot); - OUT(target_id); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopRequest) -{ - ENCODE_LENGTH_EXACT(Merchant_Click_Struct); - SETUP_DIRECT_ENCODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); - OUT(npcid); - OUT(playerid); - OUT(command); - OUT(rate); - eq->unknown01 = 3; // Not sure what these values do yet, but list won't display without them - eq->unknown02 = 2592000; - FINISH_ENCODE(); -} - -ENCODE(OP_DisciplineUpdate) -{ - ENCODE_LENGTH_EXACT(Disciplines_Struct); - SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); - - memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); - - FINISH_ENCODE(); -} - -ENCODE(OP_RespondAA) { - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - eq->aa_spent = emu->aa_spent; - // These fields may need to be correctly populated at some point - eq->aapoints_assigned = emu->aa_spent + 1; - eq->aa_spent_general = 0; - eq->aa_spent_archetype = 0; - eq->aa_spent_class = 0; - eq->aa_spent_special = 0; - - for(uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) - { - eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; - eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; - eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; - } - - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToRoFSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrency) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - uint32 opcode = *((uint32*)emu_buffer); - - if(opcode == 8) { - AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) - + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); - structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; - - out_populate->opcode = populate->opcode; - out_populate->count = populate->count; - for(uint32 i = 0; i < populate->count; ++i) { - out_populate->entries[i].currency_number = populate->entries[i].currency_number; - out_populate->entries[i].unknown00 = populate->entries[i].unknown00; - out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; - out_populate->entries[i].item_id = populate->entries[i].item_id; - out_populate->entries[i].item_icon = populate->entries[i].item_icon; - out_populate->entries[i].stack_size = populate->entries[i].stack_size; - out_populate->entries[i].display = ((populate->entries[i].stack_size > 0) ? 1 : 0); - } - - dest->FastQueuePacket(&outapp, ack_req); - } else { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); - memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); - dest->FastQueuePacket(&outapp, ack_req); - } - - //dest->FastQueuePacket(&outapp, ack_req); - delete in; -} - -ENCODE(OP_HPUpdate) -{ - SETUP_DIRECT_ENCODE(SpawnHPUpdate_Struct, structs::SpawnHPUpdate_Struct); - OUT(spawn_id); - OUT(cur_hp); - OUT(max_hp); - FINISH_ENCODE(); -} - -ENCODE(OP_RemoveBlockedBuffs) { ENCODE_FORWARD(OP_BlockedBuffs); } - -ENCODE(OP_BlockedBuffs) -{ - ENCODE_LENGTH_EXACT(BlockedBuffs_Struct); - SETUP_DIRECT_ENCODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); - - for(uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) - eq->SpellID[i] = emu->SpellID[i]; - - // -1 for the extra 10 added in RoF. We should really be encoding for the older clients, not RoF, but - // we can sort that out later. - - for(uint32 i = BLOCKED_BUFF_COUNT; i < structs::BLOCKED_BUFF_COUNT; ++i) - eq->SpellID[i] = -1; - - OUT(Count); - OUT(Pet); - OUT(Initialise); - OUT(Flags); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeInfo) -{ - ENCODE_LENGTH_ATLEAST(TributeAbility_Struct); - SETUP_VAR_ENCODE(TributeAbility_Struct); - - ALLOC_VAR_ENCODE(structs::TributeAbility_Struct, sizeof(structs::TributeAbility_Struct) + strlen(emu->name) + 1); - - OUT(tribute_id); - OUT(tier_count); - - for(uint32 i = 0; i < MAX_TRIBUTE_TIERS; ++i) - { - eq->tiers[i].level = emu->tiers[i].level; - eq->tiers[i].tribute_item_id = emu->tiers[i].tribute_item_id; - eq->tiers[i].cost = emu->tiers[i].cost; - } - - eq->unknown128 = 0; - - strcpy(eq->name, emu->name); - - FINISH_ENCODE(); -} - -ENCODE(OP_GuildMemberUpdate) -{ - SETUP_DIRECT_ENCODE(GuildMemberUpdate_Struct, structs::GuildMemberUpdate_Struct); - - OUT(GuildID); - memcpy(eq->MemberName, emu->MemberName, sizeof(eq->MemberName)); - OUT(ZoneID); - OUT(InstanceID); - OUT(LastSeen); - eq->Unknown76 = 0; - FINISH_ENCODE(); -} - -ENCODE(OP_BeginCast) -{ - SETUP_DIRECT_ENCODE(BeginCast_Struct, structs::BeginCast_Struct); - - OUT(spell_id); - OUT(caster_id); - OUT(cast_time); - - FINISH_ENCODE(); -} - -ENCODE(OP_RezzRequest) -{ - SETUP_DIRECT_ENCODE(Resurrect_Struct, structs::Resurrect_Struct); - - OUT(zone_id); - OUT(instance_id); - OUT(y); - OUT(x); - OUT(z); - OUT_str(your_name); - OUT_str(rezzer_name); - OUT(spellid); - OUT_str(corpse_name); - OUT(action); - - FINISH_ENCODE(); -} - -DECODE(OP_BuffRemoveRequest) -{ - // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. - // - DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); - SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); - - emu->SlotID = (eq->SlotID < 42 ) ? eq->SlotID : (eq->SlotID - 17); - - IN(EntityID); + emu->itemicons[22] = eq->itemicons[21]; + emu->itemicons[21] = eq->unknown_zero2; + emu->unknown_zero2 = eq->unknown_zero2; + strn0cpy(emu->text, eq->text, sizeof(emu->text)); + //emu->unknown1772 = 0; FINISH_DIRECT_DECODE(); -} + }*/ -DECODE(OP_PetCommands) -{ - DECODE_LENGTH_EXACT(structs::PetCommand_Struct); - SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); - - switch(eq->command) + DECODE(OP_InspectRequest) { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + // Max Augs is now 6, but no code to support that many yet + IN(link_hash); + IN(icon); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemVerifyRequest) + { + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = RoFToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LoadSpellSet) + { + DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); + SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); + + for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + { + if (eq->spell[i] == 0) + emu->spell[i] = 0xFFFFFFFF; + else + emu->spell[i] = eq->spell[i]; + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = RoFToServerCorpseSlot(eq->slot_id); + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + //_log(NET__ERROR, "Moved item from %u to %u", eq->from_slot.MainSlot, eq->to_slot.MainSlot); + _log(NET__ERROR, "MoveItem SlotType from %i to %i, MainSlot from %i to %i, SubSlot from %i to %i, AugSlot from %i to %i, Unknown01 from %i to %i, Number %u", eq->from_slot.SlotType, eq->to_slot.SlotType, eq->from_slot.MainSlot, eq->to_slot.MainSlot, eq->from_slot.SubSlot, eq->to_slot.SubSlot, eq->from_slot.AugSlot, eq->to_slot.AugSlot, eq->from_slot.Unknown01, eq->to_slot.Unknown01, eq->number_in_stack); + emu->from_slot = RoFToServerSlot(eq->from_slot); + emu->to_slot = RoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + _hex(NET__ERROR, eq, sizeof(structs::MoveItem_Struct)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_PetCommands) + { + DECODE_LENGTH_EXACT(structs::PetCommand_Struct); + SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); + + switch (eq->command) + { case 0x00: emu->command = 0x04; // Health break; @@ -4100,1312 +4505,1107 @@ DECODE(OP_PetCommands) break; default: emu->command = eq->command; - } - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = RoFToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = RoFToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopRequest) { - DECODE_LENGTH_EXACT(structs::Merchant_Click_Struct); - SETUP_DIRECT_DECODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); - IN(npcid); - IN(playerid); - IN(command); - IN(rate); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GuildRemove) -{ - DECODE_LENGTH_EXACT(structs::GuildCommand_Struct); - SETUP_DIRECT_DECODE(GuildCommand_Struct, structs::GuildCommand_Struct); - strn0cpy(emu->othername, eq->othername, sizeof(emu->othername)); - strn0cpy(emu->myname, eq->myname, sizeof(emu->myname)); - IN(guildeqid); - IN(officer); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GuildDemote) -{ - DECODE_LENGTH_EXACT(structs::GuildDemoteStruct); - SETUP_DIRECT_DECODE(GuildDemoteStruct, structs::GuildDemoteStruct); - strn0cpy(emu->target, eq->target, sizeof(emu->target)); - strn0cpy(emu->name, eq->name, sizeof(emu->name)); - // IN(rank); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_BazaarSearch) -{ - char *Buffer = (char *)__packet->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) - return; - - SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); - MEMSET_IN(structs::NewBazaarInspect_Struct); - IN(Beginning.Action); - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - IN(SerialNumber); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -/*DECODE(OP_InspectAnswer) { - DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); - SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); - - IN(TargetID); - IN(playerid); - - int r; - for (r = 0; r < 21; r++) { - strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); - } - // Swap last 2 slots for Arrow and Power Source - strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); - strn0cpy(emu->itemnames[21], eq->unknown_zero, sizeof(emu->itemnames[21])); - strn0cpy(emu->unknown_zero, eq->unknown_zero, sizeof(emu->unknown_zero)); - - int k; - for (k = 0; k < 21; k++) { - IN(itemicons[k]); - } - // Swap last 2 slots for Arrow and Power Source - emu->itemicons[22] = eq->itemicons[21]; - emu->itemicons[21] = eq->unknown_zero2; - emu->unknown_zero2 = eq->unknown_zero2; - strn0cpy(emu->text, eq->text, sizeof(emu->text)); - //emu->unknown1772 = 0; - - FINISH_DIRECT_DECODE(); -}*/ - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = RoFToServerMainInvSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = RoFToServerMainInvSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = RoFToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = RoFToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - if(eq->slot == 13) - { - emu->slot = 10; - } - else - { - IN(slot); - } - IN(spell_id); - emu->inventoryslot = RoFToServerSlot(eq->inventoryslot); - //IN(inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = RoFToServerSlot(eq->from_slot); - emu->to_slot = RoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - //_log(NET__ERROR, "Moved item from %u to %u", eq->from_slot.MainSlot, eq->to_slot.MainSlot); - _log(NET__ERROR, "MoveItem SlotType from %i to %i, MainSlot from %i to %i, SubSlot from %i to %i, AugSlot from %i to %i, Unknown01 from %i to %i, Number %u", eq->from_slot.SlotType, eq->to_slot.SlotType, eq->from_slot.MainSlot, eq->to_slot.MainSlot, eq->from_slot.SubSlot, eq->to_slot.SubSlot, eq->from_slot.AugSlot, eq->to_slot.AugSlot, eq->from_slot.Unknown01, eq->to_slot.Unknown01, eq->number_in_stack); - emu->from_slot = RoFToServerSlot(eq->from_slot); - emu->to_slot = RoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - _hex(NET__ERROR, eq, sizeof(structs::MoveItem_Struct)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - // Max Augs is now 6, but no code to support that many yet - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - // Size 40 in RoF - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerBuy) -{ - DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); - SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - - IN(npcid); - IN(playerid); - IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - - IN(gender); - IN(race); - IN(class_); - IN(deity); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(beard); - IN(beardcolor); - IN(hairstyle); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - //IN(tutorial); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupInvite2) -{ - //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); - DECODE_FORWARD(OP_GroupInvite); -} - -DECODE(OP_GroupInvite) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupInvite"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); - memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupDisband) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_Disband"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupCancelInvite) -{ - DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); - SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - IN(toggle); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GMLastName) -{ - DECODE_LENGTH_EXACT(structs::GMLastName_Struct); - SETUP_DIRECT_DECODE(GMLastName_Struct, structs::GMLastName_Struct); - memcpy(emu->name, eq->name, sizeof(emu->name)); - memcpy(emu->gmname, eq->gmname, sizeof(emu->gmname)); - memcpy(emu->lastname, eq->lastname, sizeof(emu->lastname)); - for (int i=0; i<4; i++) - { - emu->unknown[i] = eq->unknown[i]; - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Live); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Live); - IN(entityid); - //IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = RoFToServerMainInvSlot(eq->itemslot); - //IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_Trader) { - uint32 psize = __packet->size; - if(psize == sizeof(structs::ClickTrader_Struct)) - { - DECODE_LENGTH_EXACT(structs::ClickTrader_Struct); - SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct); - MEMSET_IN(ClickTrader_Struct); - - emu->Code = eq->Code; - // Live actually has 200 items now, but 80 is the most our internal struct supports - for (uint32 i = 0; i < 80; i++) - { - emu->SerialNumber[i] = 0; // eq->SerialNumber[i]; - emu->ItemCost[i] = eq->ItemCost[i]; } - FINISH_DIRECT_DECODE(); - } - else if(psize == sizeof(structs::Trader_ShowItems_Struct)) - { - DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct); - SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); - MEMSET_IN(Trader_ShowItems_Struct); - - emu->Code = eq->Code; - emu->TraderID = eq->TraderID; FINISH_DIRECT_DECODE(); } - else if(psize == sizeof(structs::TraderStatus_Struct)) - { - DECODE_LENGTH_EXACT(structs::TraderStatus_Struct); - SETUP_DIRECT_DECODE(TraderStatus_Struct, structs::TraderStatus_Struct); - MEMSET_IN(TraderStatus_Struct); - emu->Code = eq->Code; + DECODE(OP_RaidInvite) + { + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); + + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } + } + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + IN(invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); FINISH_DIRECT_DECODE(); } -} -DECODE(OP_TraderBuy) -{ - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = RoFToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = 0; // Set to hard 0 since it's not required for the structure to work - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - int16 slot_id = RoFToServerSlot(eq->container_slot); - if (slot_id == 4000) { - slot_id = legacy::SLOT_TRADESKILL; // 1000 - } - emu->container_slot = slot_id; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RecipeAutoCombine) { - DECODE_LENGTH_EXACT(structs::RecipeAutoCombine_Struct); - SETUP_DIRECT_DECODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); - - IN(object_type); - IN(some_id); - emu->unknown1 = RoFToServerSlot(eq->container_slot); - IN(recipe_id); - IN(reply_code); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = RoFToServerSlot(eq->container_slot); - emu->augment_slot = RoFToServerSlot(eq->augment_slot); - emu->container_index = eq->container_index; - emu->augment_index = eq->augment_index; - emu->dest_inst_id = eq->dest_inst_id; - emu->augment_action = eq->augment_action; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LoadSpellSet) -{ - DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); - SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); - - for(unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + DECODE(OP_RecipeAutoCombine) { - if (eq->spell[i] == 0) - emu->spell[i] = 0xFFFFFFFF; + DECODE_LENGTH_EXACT(structs::RecipeAutoCombine_Struct); + SETUP_DIRECT_DECODE(RecipeAutoCombine_Struct, structs::RecipeAutoCombine_Struct); + + IN(object_type); + IN(some_id); + emu->unknown1 = RoFToServerSlot(eq->container_slot); + IN(recipe_id); + IN(reply_code); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } + + DECODE(OP_RezzAnswer) + { + DECODE_LENGTH_EXACT(structs::Resurrect_Struct); + SETUP_DIRECT_DECODE(Resurrect_Struct, structs::Resurrect_Struct); + + IN(zone_id); + IN(instance_id); + IN(y); + IN(x); + IN(z); + memcpy(emu->your_name, eq->your_name, sizeof(emu->your_name)); + memcpy(emu->rezzer_name, eq->rezzer_name, sizeof(emu->rezzer_name)); + IN(spellid); + memcpy(emu->corpse_name, eq->corpse_name, sizeof(emu->corpse_name)); + IN(action); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Save) + { + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + // Size 40 in RoF + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerBuy) + { + DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); + SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + IN(npcid); + IN(playerid); + IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = RoFToServerMainInvSlot(eq->itemslot); + //IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopRequest) + { + DECODE_LENGTH_EXACT(structs::Merchant_Click_Struct); + SETUP_DIRECT_DECODE(Merchant_Click_Struct, structs::Merchant_Click_Struct); + + IN(npcid); + IN(playerid); + IN(command); + IN(rate); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Trader) + { + uint32 psize = __packet->size; + if (psize == sizeof(structs::ClickTrader_Struct)) + { + DECODE_LENGTH_EXACT(structs::ClickTrader_Struct); + SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct); + MEMSET_IN(ClickTrader_Struct); + + emu->Code = eq->Code; + // Live actually has 200 items now, but 80 is the most our internal struct supports + for (uint32 i = 0; i < 80; i++) + { + emu->SerialNumber[i] = 0; // eq->SerialNumber[i]; + emu->ItemCost[i] = eq->ItemCost[i]; + } + + FINISH_DIRECT_DECODE(); + } + else if (psize == sizeof(structs::Trader_ShowItems_Struct)) + { + DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct); + SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct); + MEMSET_IN(Trader_ShowItems_Struct); + + emu->Code = eq->Code; + emu->TraderID = eq->TraderID; + + FINISH_DIRECT_DECODE(); + } + else if (psize == sizeof(structs::TraderStatus_Struct)) + { + DECODE_LENGTH_EXACT(structs::TraderStatus_Struct); + SETUP_DIRECT_DECODE(TraderStatus_Struct, structs::TraderStatus_Struct); + MEMSET_IN(TraderStatus_Struct); + + emu->Code = eq->Code; + + FINISH_DIRECT_DECODE(); + } + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + int16 slot_id = RoFToServerSlot(eq->container_slot); + if (slot_id == 4000) { + slot_id = legacy::SLOT_TRADESKILL; // 1000 + } + emu->container_slot = slot_id; + emu->guildtribute_slot = RoFToServerSlot(eq->guildtribute_slot); // this should only return INVALID_INDEX until implemented -U + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = RoFToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ZoneChange) + { + DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); + SETUP_DIRECT_DECODE(ZoneChange_Struct, structs::ZoneChange_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + IN(zoneID); + IN(instanceID); + IN(y); + IN(x); + IN(z) + IN(zone_reason); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ZoneEntry) + { + DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); + SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct); + + memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; + + static inline int32 GetNextItemInstSerialNumber() + { + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; else - emu->spell[i] = eq->spell[i]; + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; } - FINISH_DIRECT_DECODE(); -} -DECODE(OP_Damage) { - DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); - SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); - IN(target); - IN(source); - IN(type); - IN(spellid); - IN(damage); - emu->sequence = eq->sequence; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_EnvDamage) { - DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); - SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); - IN(id); - IN(damage); - IN(dmgtype); - emu->constant = 0xFFFF; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ZoneChange) -{ - DECODE_LENGTH_EXACT(structs::ZoneChange_Struct); - SETUP_DIRECT_DECODE(ZoneChange_Struct, structs::ZoneChange_Struct); - memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); - IN(zoneID); - IN(instanceID); - IN(y); - IN(x); - IN(z) - IN(zone_reason); - IN(success); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ChannelMessage) -{ - unsigned char *__eq_buffer = __packet->pBuffer; - - char *InBuffer = (char *)__eq_buffer; - - char Sender[64]; - char Target[64]; - - VARSTRUCT_DECODE_STRING(Sender, InBuffer); - VARSTRUCT_DECODE_STRING(Target, InBuffer); - - InBuffer += 4; - - uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - InBuffer += 5; - - uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - __packet->size = sizeof(ChannelMessage_Struct) + strlen(InBuffer) + 1; - __packet->pBuffer = new unsigned char[__packet->size]; - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) __packet->pBuffer; - - strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); - strn0cpy(emu->sender, Target, sizeof(emu->sender)); - emu->language = Language; - emu->chan_num = Channel; - emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); - - delete [] __eq_buffer; -} - -DECODE(OP_ZoneEntry) -{ - DECODE_LENGTH_EXACT(structs::ClientZoneEntry_Struct); - SETUP_DIRECT_DECODE(ClientZoneEntry_Struct, structs::ClientZoneEntry_Struct); - memcpy(emu->char_name, eq->char_name, sizeof(emu->char_name)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RemoveBlockedBuffs) { DECODE_FORWARD(OP_BlockedBuffs); } - -DECODE(OP_BlockedBuffs) -{ - DECODE_LENGTH_EXACT(structs::BlockedBuffs_Struct); - SETUP_DIRECT_DECODE(BlockedBuffs_Struct, structs::BlockedBuffs_Struct); - - for(uint32 i = 0; i < BLOCKED_BUFF_COUNT; ++i) - emu->SpellID[i] = eq->SpellID[i]; - - IN(Count); - IN(Pet); - IN(Initialise); - IN(Flags); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GuildStatus) -{ - DECODE_LENGTH_EXACT(structs::GuildStatus_Struct); - SETUP_DIRECT_DECODE(GuildStatus_Struct, structs::GuildStatus_Struct); - - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RezzAnswer) -{ - DECODE_LENGTH_EXACT(structs::Resurrect_Struct); - SETUP_DIRECT_DECODE(Resurrect_Struct, structs::Resurrect_Struct); - - IN(zone_id); - IN(instance_id); - IN(y); - IN(x); - IN(z); - memcpy(emu->your_name, eq->your_name, sizeof(emu->your_name)); - memcpy(emu->rezzer_name, eq->rezzer_name, sizeof(emu->rezzer_name)); - IN(spellid); - memcpy(emu->corpse_name, eq->corpse_name, sizeof(emu->corpse_name)); - IN(action); - - FINISH_DIRECT_DECODE(); -} - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - - RoF::structs::ItemSerializationHeader hdr; - - //sprintf(hdr.unknown000, "06e0002Y1W00"); - - snprintf( hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID ); - - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - structs::ItemSlotStruct slot_id = ServerToRoFSlot(slot_id_in); - - hdr.slot_type = (merchant_slot == 0) ? slot_id.SlotType : 9; // 9 is merchant 20 is reclaim items? - hdr.main_slot = (merchant_slot == 0) ? slot_id.MainSlot : merchant_slot; - hdr.sub_slot = (merchant_slot == 0) ? slot_id.SubSlot : 0xffff; - hdr.unknown013 = (merchant_slot == 0) ? slot_id.AugSlot : 0xffff; - //hdr.unknown013 = 0xffff; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - //hdr.merchant_slot = (merchant_slot == 0) ? 1 : 0xffffffff; - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.unknown062 = 0; - hdr.unknowna1 = 0xffffffff; - hdr.unknowna2 = 0; - hdr.unknown063 = 0; - hdr.unknowna3 = 0; - hdr.unknowna4 = 0xffffffff; - hdr.unknowna5 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(RoF::structs::ItemSerializationHeader)); - - - if(strlen(item->Name) > 0) + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; - if(strlen(item->Lore) > 0) - { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - if(strlen(item->IDFile) > 0) - { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); - ss.write((const char*)&null_term, sizeof(uint8)); - //_log(NET__ERROR, "ItemBody struct is %i bytes", sizeof(RoF::structs::ItemBodyStruct)); - RoF::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(RoF::structs::ItemBodyStruct)); + RoF::structs::ItemSerializationHeader hdr; - uint32 adjusted_slots = item->Slots; + //sprintf(hdr.unknown000, "06e0002Y1W00"); - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) - { - // Do nothing - } - else - { - if(item->Slots & (1 << 21)) // Ammo Slot from Database + snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID); + + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + structs::ItemSlotStruct slot_id = ServerToRoFSlot(slot_id_in); + + hdr.slot_type = (merchant_slot == 0) ? slot_id.SlotType : 9; // 9 is merchant 20 is reclaim items? + hdr.main_slot = (merchant_slot == 0) ? slot_id.MainSlot : merchant_slot; + hdr.sub_slot = (merchant_slot == 0) ? slot_id.SubSlot : 0xffff; + hdr.unknown013 = (merchant_slot == 0) ? slot_id.AugSlot : 0xffff; + //hdr.unknown013 = 0xffff; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + //hdr.merchant_slot = (merchant_slot == 0) ? 1 : 0xffffffff; + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.unknown062 = 0; + hdr.unknowna1 = 0xffffffff; + hdr.unknowna2 = 0; + hdr.unknown063 = 0; + hdr.unknowna3 = 0; + hdr.unknowna4 = 0xffffffff; + hdr.unknowna5 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(RoF::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); } - if(item->Slots & (1 << 22)) // Power Source Slot from Database + if (strlen(item->Lore) > 0) { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); } - } - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.SkillModMax = 0xffffffff; - ibs.SkillModType = (int8)(item->SkillModType); - ibs.SkillModExtra = 0; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - if(item->ReqLevel > 100) - ibs.ReqLevel = 100; - ibs.RecLevel = item->RecLevel; - if(item->RecLevel > 100) - ibs.RecLevel = 100; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.Prestige = 0; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.unknown_RoF3 = 0; - ibs.unknown_RoF4 = 0; - ibs.SellRate = item->SellRate; - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; - - ss.write((const char*)&ibs, sizeof(RoF::structs::ItemBodyStruct)); - - //charm text - if(strlen(item->CharmFile) > 0) - { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + //_log(NET__ERROR, "ItemBody struct is %i bytes", sizeof(RoF::structs::ItemBodyStruct)); + RoF::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(RoF::structs::ItemBodyStruct)); - //_log(NET__ERROR, "ItemBody secondary struct is %i bytes", sizeof(RoF::structs::ItemSecondaryBodyStruct)); - RoF::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; - isbs.augtype = item->AugType; - isbs.augdistiller = 65535; - isbs.augrestrict = item->AugRestrict; - + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.SkillModMax = 0xffffffff; + ibs.SkillModType = (int8)(item->SkillModType); + ibs.SkillModExtra = 0; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + if (item->ReqLevel > 100) + ibs.ReqLevel = 100; + ibs.RecLevel = item->RecLevel; + if (item->RecLevel > 100) + ibs.RecLevel = 100; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.Prestige = 0; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.unknown_RoF3 = 0; + ibs.unknown_RoF4 = 0; + ibs.SellRate = item->SellRate; + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; - for(int x = AUG_BEGIN; x < EmuConstants::ITEM_COMMON_SIZE; ++x) - { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; - } + ss.write((const char*)&ibs, sizeof(RoF::structs::ItemBodyStruct)); - // Increased to 6 max aug slots - isbs.augslots[5].type = 0; - isbs.augslots[5].visible = 1; - isbs.augslots[5].unknown = 0; + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; + //_log(NET__ERROR, "ItemBody secondary struct is %i bytes", sizeof(RoF::structs::ItemSecondaryBodyStruct)); + RoF::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; + isbs.augtype = item->AugType; + isbs.augdistiller = 65535; + isbs.augrestrict = item->AugRestrict; - isbs.book = item->Book; - isbs.booktype = item->BookType; + for (int x = AUG_BEGIN; x < EmuConstants::ITEM_COMMON_SIZE; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } - ss.write((const char*)&isbs, sizeof(RoF::structs::ItemSecondaryBodyStruct)); + // Increased to 6 max aug slots + isbs.augslots[5].type = 0; + isbs.augslots[5].visible = 1; + isbs.augslots[5].unknown = 0; - if(strlen(item->Filename) > 0) - { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; - //_log(NET__ERROR, "ItemBody tertiary struct is %i bytes", sizeof(RoF::structs::ItemTertiaryBodyStruct)); - RoF::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(RoF::structs::ItemTertiaryBodyStruct)); + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.unknown3 = 0xffffffff; - itbs.unknown4 = 0; - itbs.no_pet = item->NoPet; - itbs.unknown5 = 0; + isbs.book = item->Book; + isbs.booktype = item->BookType; - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; + ss.write((const char*)&isbs, sizeof(RoF::structs::ItemSecondaryBodyStruct)); - itbs.unknown8 = 0; - itbs.unknown9 = 0; - itbs.unknown10 = 0; - itbs.unknown11 = 0; - itbs.unknown12 = 0; - itbs.unknown13 = 0; - itbs.unknown14 = 0; + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - ss.write((const char*)&itbs, sizeof(RoF::structs::ItemTertiaryBodyStruct)); + //_log(NET__ERROR, "ItemBody tertiary struct is %i bytes", sizeof(RoF::structs::ItemTertiaryBodyStruct)); + RoF::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(RoF::structs::ItemTertiaryBodyStruct)); - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.unknown3 = 0xffffffff; + itbs.unknown4 = 0; + itbs.no_pet = item->NoPet; + itbs.unknown5 = 0; - //_log(NET__ERROR, "ItemBody Click effect struct is %i bytes", sizeof(RoF::structs::ClickEffectStruct)); - RoF::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(RoF::structs::ClickEffectStruct)); + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; + itbs.unknown8 = 0; + itbs.unknown9 = 0; + itbs.unknown10 = 0; + itbs.unknown11 = 0; + itbs.unknown12 = 0; + itbs.unknown13 = 0; + itbs.unknown14 = 0; - ss.write((const char*)&ices, sizeof(RoF::structs::ClickEffectStruct)); + ss.write((const char*)&itbs, sizeof(RoF::structs::ItemTertiaryBodyStruct)); - if(strlen(item->ClickName) > 0) - { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + //_log(NET__ERROR, "ItemBody Click effect struct is %i bytes", sizeof(RoF::structs::ClickEffectStruct)); + RoF::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(RoF::structs::ClickEffectStruct)); - //_log(NET__ERROR, "ItemBody proc effect struct is %i bytes", sizeof(RoF::structs::ProcEffectStruct)); - RoF::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(RoF::structs::ProcEffectStruct)); + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; + ss.write((const char*)&ices, sizeof(RoF::structs::ClickEffectStruct)); - ss.write((const char*)&ipes, sizeof(RoF::structs::ProcEffectStruct)); + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->ProcName) > 0) - { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + //_log(NET__ERROR, "ItemBody proc effect struct is %i bytes", sizeof(RoF::structs::ProcEffectStruct)); + RoF::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(RoF::structs::ProcEffectStruct)); - //_log(NET__ERROR, "ItemBody worn effect struct is %i bytes", sizeof(RoF::structs::WornEffectStruct)); - RoF::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(RoF::structs::WornEffectStruct)); + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; + ss.write((const char*)&ipes, sizeof(RoF::structs::ProcEffectStruct)); - ss.write((const char*)&iwes, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->WornName) > 0) - { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + //_log(NET__ERROR, "ItemBody worn effect struct is %i bytes", sizeof(RoF::structs::WornEffectStruct)); + RoF::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(RoF::structs::WornEffectStruct)); - RoF::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(RoF::structs::WornEffectStruct)); + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; + ss.write((const char*)&iwes, sizeof(RoF::structs::WornEffectStruct)); - ss.write((const char*)&ifes, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->FocusName) > 0) - { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + RoF::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(RoF::structs::WornEffectStruct)); - RoF::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(RoF::structs::WornEffectStruct)); + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; + ss.write((const char*)&ifes, sizeof(RoF::structs::WornEffectStruct)); - ss.write((const char*)&ises, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - if(strlen(item->ScrollName) > 0) - { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + RoF::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(RoF::structs::WornEffectStruct)); - // Bard Effect? - RoF::structs::WornEffectStruct ibes; - memset(&ibes, 0, sizeof(RoF::structs::WornEffectStruct)); + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; - ibes.effect = 0xffffffff; - ibes.level2 = 0; - ibes.type = 0; - ibes.level = 0; - //ibes.unknown6 = 0xffffffff; + ss.write((const char*)&ises, sizeof(RoF::structs::WornEffectStruct)); - ss.write((const char*)&ibes, sizeof(RoF::structs::WornEffectStruct)); + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } - /* - if(strlen(item->BardName) > 0) - { + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + // Bard Effect? + RoF::structs::WornEffectStruct ibes; + memset(&ibes, 0, sizeof(RoF::structs::WornEffectStruct)); + + ibes.effect = 0xffffffff; + ibes.level2 = 0; + ibes.type = 0; + ibes.level = 0; + //ibes.unknown6 = 0xffffffff; + + ss.write((const char*)&ibes, sizeof(RoF::structs::WornEffectStruct)); + + /* + if(strlen(item->BardName) > 0) + { ss.write((const char*)item->BardName, strlen(item->BardName)); ss.write((const char*)&null_term, sizeof(uint8)); - } - else */ + } + else */ ss.write((const char*)&null_term, sizeof(uint8)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects - //_log(NET__ERROR, "ItemBody Quaternary effect struct is %i bytes", sizeof(RoF::structs::ItemQuaternaryBodyStruct)); - RoF::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + //_log(NET__ERROR, "ItemBody Quaternary effect struct is %i bytes", sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + RoF::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.Power = 0; - iqbs.Purity = item->Purity; - iqbs.unknown16 = 0; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - iqbs.clairvoyance = item->Clairvoyance; - iqbs.unknown28 = 0; - iqbs.unknown30 = 0; - iqbs.unknown39 = 1; + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.Power = 0; + iqbs.Purity = item->Purity; + iqbs.unknown16 = 0; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + iqbs.clairvoyance = item->Clairvoyance; + iqbs.unknown28 = 0; + iqbs.unknown30 = 0; + iqbs.unknown39 = 1; - iqbs.subitem_count = 0; + iqbs.subitem_count = 0; - char *SubSerializations[10]; // + char *SubSerializations[10]; // - uint32 SubLengths[10]; + uint32 SubLengths[10]; - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - SubSerializations[x] = nullptr; + SubSerializations[x] = nullptr; - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - if(subitem) { + if (subitem) { - int SubSlotNumber; + int SubSlotNumber; - iqbs.subitem_count++; + iqbs.subitem_count++; - if(slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if(slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if(slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? + + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ + + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + } + } + + ss.write((const char*)&iqbs, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + + if (SubSerializations[x]) { + + ss.write((const char*)&x, sizeof(uint32)); + + ss.write(SubSerializations[x], SubLengths[x]); + + safe_delete_array(SubSerializations[x]); + } + } + + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + + *length = ss.tellp(); + return item_serial; + } + + static inline structs::ItemSlotStruct ServerToRoFSlot(uint32 ServerSlot) + { + structs::ItemSlotStruct RoFSlot; + RoFSlot.SlotType = INVALID_INDEX; + RoFSlot.Unknown02 = NOT_USED; + RoFSlot.MainSlot = INVALID_INDEX; + RoFSlot.SubSlot = INVALID_INDEX; + RoFSlot.AugSlot = INVALID_INDEX; + RoFSlot.Unknown01 = NOT_USED; + + uint32 TempSlot = 0; + + if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // Main Inventory and Cursor + RoFSlot.SlotType = maps::MapPossessions; + RoFSlot.MainSlot = ServerSlot; + + if (ServerSlot == MainPowerSource) + RoFSlot.MainSlot = slots::MainPowerSource; + + else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory + RoFSlot.MainSlot += 3; + + else if (ServerSlot >= MainAmmo) // (> 20) + RoFSlot.MainSlot += 1; + } + + /*else if (ServerSlot < 51) { // Cursor Buffer + RoFSlot.SlotType = maps::MapLimbo; + RoFSlot.MainSlot = ServerSlot - 31; + }*/ + + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { // (> 250 && < 341) + RoFSlot.SlotType = maps::MapPossessions; + TempSlot = ServerSlot - 1; + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); + + if (RoFSlot.MainSlot >= slots::MainGeneral9) // (> 30) + RoFSlot.MainSlot = slots::MainCursor; + } + + else if (ServerSlot >= EmuConstants::TRIBUTE_BEGIN && ServerSlot <= EmuConstants::TRIBUTE_END) { // Tribute + RoFSlot.SlotType = maps::MapTribute; + RoFSlot.MainSlot = ServerSlot - EmuConstants::TRIBUTE_BEGIN; + } + + else if (ServerSlot >= EmuConstants::BANK_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) { + RoFSlot.SlotType = maps::MapBank; + TempSlot = ServerSlot - EmuConstants::BANK_BEGIN; + RoFSlot.MainSlot = TempSlot; + + if (TempSlot > 30) { // (> 30) + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); + } + } + + else if (ServerSlot >= EmuConstants::SHARED_BANK_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) { + RoFSlot.SlotType = maps::MapSharedBank; + TempSlot = ServerSlot - EmuConstants::SHARED_BANK_BEGIN; + RoFSlot.MainSlot = TempSlot; + + if (TempSlot > 30) { // (> 30) + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); + } + } + + else if (ServerSlot >= EmuConstants::TRADE_BEGIN && ServerSlot <= EmuConstants::TRADE_BAGS_END) { + RoFSlot.SlotType = maps::MapTrade; + TempSlot = ServerSlot - EmuConstants::TRADE_BEGIN; + RoFSlot.MainSlot = TempSlot; + + if (TempSlot > 30) { + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 3; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE); + } /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + // OLD CODE: + if (TempSlot > 99) { + if (TempSlot > 100) + RoFSlot.MainSlot = int((TempSlot - 100) / 10); + + else + RoFSlot.MainSlot = 0; + + RoFSlot.SubSlot = TempSlot - (100 + RoFSlot.MainSlot); + } */ - - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); } + + else if (ServerSlot >= EmuConstants::WORLD_BEGIN && ServerSlot <= EmuConstants::WORLD_END) { + RoFSlot.SlotType = maps::MapWorld; + TempSlot = ServerSlot - EmuConstants::WORLD_BEGIN; + RoFSlot.MainSlot = TempSlot; + } + + _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); + + return RoFSlot; } - ss.write((const char*)&iqbs, sizeof(RoF::structs::ItemQuaternaryBodyStruct)); + static inline structs::MainInvItemSlotStruct ServerToRoFMainInvSlot(uint32 ServerSlot) + { + structs::MainInvItemSlotStruct RoFSlot; + RoFSlot.MainSlot = INVALID_INDEX; + RoFSlot.SubSlot = INVALID_INDEX; + RoFSlot.AugSlot = INVALID_INDEX; + RoFSlot.Unknown01 = NOT_USED; - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + uint32 TempSlot = 0; - if(SubSerializations[x]) { + if (ServerSlot < 56 || ServerSlot == MainPowerSource) { // (< 52) + RoFSlot.MainSlot = ServerSlot; - ss.write((const char*)&x, sizeof(uint32)); + if (ServerSlot == MainPowerSource) + RoFSlot.MainSlot = slots::MainPowerSource; - ss.write(SubSerializations[x], SubLengths[x]); + else if (ServerSlot >= MainCursor) // Cursor and Extended Corpse Inventory + RoFSlot.MainSlot += 3; - safe_delete_array(SubSerializations[x]); + else if (ServerSlot >= MainAmmo) // Ammo and Personl Inventory + RoFSlot.MainSlot += 1; + + /*else if (ServerSlot >= MainCursor) { // Cursor + RoFSlot.MainSlot = slots::MainCursor; + + if (ServerSlot > 30) + RoFSlot.SubSlot = (ServerSlot + 3) - 33; + }*/ } + + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) { + TempSlot = ServerSlot - 1; + RoFSlot.MainSlot = int(TempSlot / EmuConstants::ITEM_CONTAINER_SIZE) - 2; + RoFSlot.SubSlot = TempSlot - ((RoFSlot.MainSlot + 2) * EmuConstants::ITEM_CONTAINER_SIZE); + } + + _log(NET__ERROR, "Convert Server Slot %i to RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i", ServerSlot, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01); + + return RoFSlot; } - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); + static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse) + { + //uint32 RoFCorpse; + return (ServerCorpse + 1); + } - *length = ss.tellp(); - return item_serial; + static inline uint32 RoFToServerSlot(structs::ItemSlotStruct RoFSlot) + { + uint32 ServerSlot = INVALID_INDEX; + uint32 TempSlot = 0; + + if (RoFSlot.SlotType == maps::MapPossessions && RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 51) + if (RoFSlot.MainSlot == slots::MainPowerSource) + TempSlot = MainPowerSource; + + else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory + TempSlot = RoFSlot.MainSlot - 3; + + /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory/corpse slots + // Need to figure out what to do when we get these + + // The slot range of 0 - client_max is cross-utilized between player inventory and corpse inventory. + // In the case of RoF, player inventory is addressed as 0 - 33 and corpse inventory is addressed as 23 - 56. + // We 'could' assign the two new inventory slots as 9997 and 9998, and then work around their bag + // slot assignments, but doing so may disrupt our ability to utilize the corpse looting range properly. + + // For now, it's probably best to leave as-is and let this work itself out in the inventory rework. + }*/ + + else if (RoFSlot.MainSlot >= slots::MainAmmo) // Ammo and Main Inventory + TempSlot = RoFSlot.MainSlot - 1; + + else // Worn Slots + TempSlot = RoFSlot.MainSlot; + + if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots + TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapBank) { + TempSlot = EmuConstants::BANK_BEGIN; + + if (RoFSlot.SubSlot >= SUB_BEGIN) + TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + else + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapSharedBank) { + TempSlot = EmuConstants::SHARED_BANK_BEGIN; + + if (RoFSlot.SubSlot >= SUB_BEGIN) + TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + else + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapTrade) { + TempSlot = EmuConstants::TRADE_BEGIN; + + if (RoFSlot.SubSlot >= SUB_BEGIN) + TempSlot += ((RoFSlot.MainSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + // OLD CODE: + //TempSlot += 100 + (RoFSlot.MainSlot * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot; + + else + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + else if (RoFSlot.SlotType == maps::MapWorld) { + TempSlot = EmuConstants::WORLD_BEGIN; + + if (RoFSlot.MainSlot >= SUB_BEGIN) + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + } + + /*else if (RoFSlot.SlotType == maps::MapLimbo) { // Cursor Buffer + TempSlot = 31; + + if (RoFSlot.MainSlot >= 0) + TempSlot += RoFSlot.MainSlot; + + ServerSlot = TempSlot; + }*/ + + else if (RoFSlot.SlotType == maps::MapGuildTribute) { + ServerSlot = INVALID_INDEX; + } + + _log(NET__ERROR, "Convert RoF Slots: Type %i, Unk2 %i, Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.SlotType, RoFSlot.Unknown02, RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); + + return ServerSlot; + } + + static inline uint32 RoFToServerMainInvSlot(structs::MainInvItemSlotStruct RoFSlot) + { + uint32 ServerSlot = INVALID_INDEX; + uint32 TempSlot = 0; + + if (RoFSlot.MainSlot < 57) { // Worn/Personal Inventory and Cursor (< 33) + if (RoFSlot.MainSlot == slots::MainPowerSource) + TempSlot = MainPowerSource; + + else if (RoFSlot.MainSlot >= slots::MainCursor) // Cursor and Extended Corpse Inventory + TempSlot = RoFSlot.MainSlot - 3; + + /*else if (RoFSlot.MainSlot == slots::MainGeneral9 || RoFSlot.MainSlot == slots::MainGeneral10) { // 9th and 10th RoF inventory slots + // Need to figure out what to do when we get these + + // Same as above + }*/ + + else if (RoFSlot.MainSlot >= slots::MainAmmo) // Main Inventory and Ammo Slots + TempSlot = RoFSlot.MainSlot - 1; + + else + TempSlot = RoFSlot.MainSlot; + + if (RoFSlot.SubSlot >= SUB_BEGIN) // Bag Slots + TempSlot = ((TempSlot + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + RoFSlot.SubSlot + 1; + + ServerSlot = TempSlot; + } + + _log(NET__ERROR, "Convert RoF Slots: Main %i, Sub %i, Aug %i, Unk1 %i to Server Slot %i", RoFSlot.MainSlot, RoFSlot.SubSlot, RoFSlot.AugSlot, RoFSlot.Unknown01, ServerSlot); + + return ServerSlot; + } + + static inline uint32 RoFToServerCorpseSlot(uint32 RoFCorpse) + { + //uint32 ServerCorpse; + return (RoFCorpse - 1); + } } - -} //end namespace RoF +// end namespace RoF diff --git a/common/patches/rof_constants.h b/common/patches/rof_constants.h index 7be8baf72..97cb25aa5 100644 --- a/common/patches/rof_constants.h +++ b/common/patches/rof_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef RoF_CONSTANTS_H_ #define RoF_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace RoF { namespace maps { @@ -184,6 +184,7 @@ namespace RoF { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = true; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = true; static const bool COIN_HAS_WEIGHT = false; } diff --git a/common/patches/rof_ops.h b/common/patches/rof_ops.h index 9e3a91c7e..57ff1f91d 100644 --- a/common/patches/rof_ops.h +++ b/common/patches/rof_ops.h @@ -1,162 +1,161 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -//E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_GroupInvite) -E(OP_GroupFollow) -E(OP_GroupFollow2) -E(OP_GroupUpdate) -E(OP_GroupCancelInvite) -E(OP_WhoAllResponse) -E(OP_Track) -E(OP_ShopPlayerBuy) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -E(OP_Barter) +E(OP_AltCurrency) +E(OP_AltCurrencySell) +E(OP_Animation) E(OP_ApplyPoison) +E(OP_Barter) +E(OP_BazaarSearch) +E(OP_BeginCast) +E(OP_BlockedBuffs) +E(OP_Buff) +E(OP_BuffCreate) +E(OP_CancelTrade) +E(OP_CastSpell) E(OP_ChannelMessage) -E(OP_GuildsList) +E(OP_CharInventory) +E(OP_ClickObjectAction) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DeleteSpawn) +E(OP_DisciplineUpdate) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_TargetBuffs) -E(OP_BuffCreate) -E(OP_SpawnAppearance) -E(OP_RespondAA) -E(OP_DisciplineUpdate) -E(OP_AltCurrencySell) -E(OP_AltCurrency) -E(OP_RequestClientZoneChange) -E(OP_ZoneChange) -E(OP_WearChange) -E(OP_ShopRequest) -E(OP_CastSpell) -E(OP_InterruptCast) -E(OP_SendMembership) -E(OP_Animation) -E(OP_HPUpdate) -E(OP_BlockedBuffs) -E(OP_RemoveBlockedBuffs) -E(OP_DeleteSpawn) -E(OP_ClickObjectAction) -E(OP_RecipeAutoCombine) -E(OP_GMTrainSkillConfirm) -E(OP_SkillUpdate) -E(OP_TributeInfo) -E(OP_TaskHistoryReply) -E(OP_TaskDescription) -E(OP_SetGuildRank) -E(OP_MercenaryDataUpdate) -E(OP_MercenaryDataResponse) -E(OP_GuildMemberUpdate) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) E(OP_GMLastName) -E(OP_BeginCast) +E(OP_GMTrainSkillConfirm) +E(OP_GroundSpawn) +E(OP_GroupCancelInvite) +E(OP_GroupFollow) +E(OP_GroupFollow2) +E(OP_GroupInvite) +E(OP_GroupUpdate) +E(OP_GuildMemberList) +E(OP_GuildMemberUpdate) +E(OP_GuildsList) +E(OP_HPUpdate) +E(OP_Illusion) +E(OP_InspectBuffs) +E(OP_InspectRequest) +E(OP_InterruptCast) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) +E(OP_MercenaryDataResponse) +E(OP_MercenaryDataUpdate) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) +E(OP_OnLevelMessage) +//E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_RecipeAutoCombine) +E(OP_RemoveBlockedBuffs) +E(OP_RequestClientZoneChange) +E(OP_RespondAA) E(OP_RezzRequest) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendMembership) +E(OP_SendZonepoints) +E(OP_SetGuildRank) +E(OP_ShopPlayerBuy) +E(OP_ShopPlayerSell) +E(OP_ShopRequest) +E(OP_SkillUpdate) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnAppearance) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_TargetBuffs) +E(OP_TaskDescription) +E(OP_TaskHistoryReply) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeInfo) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_WhoAllResponse) +E(OP_ZoneChange) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) +D(OP_BazaarSearch) +D(OP_BlockedBuffs) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) +D(OP_BuffRemoveRequest) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) -D(OP_GroupInvite) -D(OP_GroupInvite2) +D(OP_ChannelMessage) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_Damage) +D(OP_DeleteItem) +D(OP_EnvDamage) +D(OP_FaceChange) +D(OP_FindPersonRequest) +D(OP_GMLastName) +D(OP_GroupCancelInvite) +D(OP_GroupDisband) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_GroupDisband) -D(OP_GroupCancelInvite) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) -D(OP_InspectRequest) -D(OP_ShopPlayerBuy) -D(OP_BazaarSearch) -D(OP_LoadSpellSet) -D(OP_ApplyPoison) -D(OP_Damage) -D(OP_EnvDamage) -D(OP_ChannelMessage) -D(OP_DeleteItem) -D(OP_AugmentItem) -D(OP_PetCommands) -D(OP_BuffRemoveRequest) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) -D(OP_ZoneChange) -D(OP_ZoneEntry) -D(OP_ShopRequest) -D(OP_BlockedBuffs) -D(OP_RemoveBlockedBuffs) -D(OP_RecipeAutoCombine) +D(OP_GroupInvite) +D(OP_GroupInvite2) D(OP_GuildDemote) D(OP_GuildRemove) D(OP_GuildStatus) -D(OP_Trader) -D(OP_GMLastName) +D(OP_InspectRequest) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) +D(OP_LoadSpellSet) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_PetCommands) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_RecipeAutoCombine) +D(OP_RemoveBlockedBuffs) D(OP_RezzAnswer) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerBuy) +D(OP_ShopPlayerSell) +D(OP_ShopRequest) +D(OP_Trader) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) +D(OP_WhoAllRequest) +D(OP_ZoneChange) +D(OP_ZoneEntry) #undef E #undef D diff --git a/common/patches/rof_structs.h b/common/patches/rof_structs.h index 2d1b5ef18..1393b026e 100644 --- a/common/patches/rof_structs.h +++ b/common/patches/rof_structs.h @@ -712,7 +712,8 @@ struct SpellBuffFade_Struct_Live { /*012*/ uint32 spellid; /*016*/ uint32 duration; /*020*/ uint32 playerId; // Global player ID? -/*024*/ uint8 unknown0028[68]; +/*024*/ uint32 num_hits; +/*028*/ uint8 unknown0028[64]; /*092*/ uint32 slotid; /*096*/ uint32 bufffade; /*100*/ @@ -726,7 +727,7 @@ struct SpellBuffFade_Struct { /*007*/ uint8 unknown7; /*008*/ uint32 spellid; /*012*/ uint32 duration; -/*016*/ uint32 unknown016; +/*016*/ uint32 num_hits; /*020*/ uint32 unknown020; // Global player ID? /*024*/ uint32 playerId; // Player id who cast the buff /*028*/ uint32 slotid; @@ -741,6 +742,27 @@ struct BuffRemoveRequest_Struct /*08*/ }; +#if 0 +// not in use +struct BuffIconEntry_Struct { +/*000*/ uint32 buff_slot; +/*004*/ uint32 spell_id; +/*008*/ uint32 tics_remaining; +/*012*/ uint32 num_hits; +// char name[0]; caster name is also here sometimes +// uint8 unknownend; 1 when single, 0 when all opposite of all_buffs? +}; + +// not in use +struct BuffIcon_Struct { +/*000*/ uint32 entity_id; +/*004*/ uint32 unknown004; +/*008*/ uint8 all_buffs; // 1 when updating all buffs, 0 when doing one +/*009*/ uint16 count; +/*011*/ BuffIconEntry_Struct entires[0]; +}; +#endif + struct GMTrainee_Struct { /*000*/ uint32 npcid; @@ -893,14 +915,62 @@ struct PotionBelt_Struct { BandolierItem_Struct items[MAX_POTIONS_IN_BELT]; }; -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -1678,7 +1748,7 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*000*/ ItemSlotStruct slot; +/*000*/ ItemSlotStruct slot; /*012*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click /*016*/ uint32 type; // 0x01=Food 0x02=Water /*020*/ uint32 c_unknown1; // Seen 2 @@ -1711,17 +1781,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ ItemSlotStruct from_slot; -/*0004*/ ItemSlotStruct to_slot; -/*0008*/ uint32 number_in_stack; -/*0012*/ +/*0000*/ ItemSlotStruct from_slot; +/*0012*/ ItemSlotStruct to_slot; +/*0024*/ uint32 number_in_stack; +/*0028*/ }; struct MoveItem_Struct { -/*0000*/ ItemSlotStruct from_slot; -/*0004*/ ItemSlotStruct to_slot; -/*0008*/ uint32 number_in_stack; -/*0012*/ +/*0000*/ ItemSlotStruct from_slot; +/*0012*/ ItemSlotStruct to_slot; +/*0024*/ uint32 number_in_stack; +/*0028*/ }; // @@ -2045,7 +2115,7 @@ struct Merchant_Sell_Struct { struct Merchant_Purchase_Struct { /*000*/ uint32 npcid; // Merchant NPC's entity id -/*004*/ MainInvItemSlotStruct itemslot; +/*004*/ MainInvItemSlotStruct itemslot; /*012*/ uint32 quantity; /*016*/ uint32 price; /*020*/ @@ -2403,6 +2473,11 @@ struct GroupFollow_Struct { // Live Follow Struct /*0152*/ }; +struct InspectBuffs_Struct { +/*000*/ uint32 spell_id[BUFF_COUNT]; +/*168*/ uint32 tics_remaining[BUFF_COUNT]; +}; + struct LFG_Struct { /*000*/ uint32 unknown000; /*004*/ uint32 value; // 0x00 = off 0x01 = on @@ -3488,10 +3563,10 @@ struct TributeInfo_Struct { struct TributeItem_Struct { -/*00*/ ItemSlotStruct slot; -/*12*/ uint32 quantity; -/*16*/ uint32 tribute_master_id; -/*20*/ int32 tribute_points; +/*00*/ ItemSlotStruct slot; +/*12*/ uint32 quantity; +/*16*/ uint32 tribute_master_id; +/*20*/ int32 tribute_points; /*24*/ }; @@ -3527,7 +3602,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ ItemSlotStruct container_slot; -/*12*/ ItemSlotStruct unknown_slot; // Slot type is 8? +/*12*/ ItemSlotStruct guildtribute_slot; // Slot type is 8? (MapGuildTribute = 8 -U) /*24*/ }; @@ -3972,6 +4047,21 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name @@ -4106,7 +4196,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's @@ -4585,9 +4675,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index db2f1f989..8a023ce42 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "sod.h" #include "../opcodemgr.h" @@ -16,991 +15,2297 @@ #include #include -namespace SoD { +namespace SoD +{ + static const char *name = "SoD"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "SoD"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToSoDSlot(uint32 ServerSlot); + static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 SoDToServerSlot(uint32 SoDSlot); + static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "sod_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "sod_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientSoD; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientSoD; + } #include "ss_define.h" - -// Converts Server Slot IDs to SoD Slot IDs for use in Encodes -static inline uint32 ServerToSoDSlot(uint32 ServerSlot) { - uint32 SoDSlot = 0; - - if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - SoDSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) - SoDSlot = ServerSlot + 11; - - else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) - SoDSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) - SoDSlot = ServerSlot + 1; - - else if (ServerSlot == MainPowerSource) - SoDSlot = slots::MainPowerSource; - - else - SoDSlot = ServerSlot; - - return SoDSlot; -} - -// Converts SoD Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 SoDToServerSlot(uint32 SoDSlot) { - uint32 ServerSlot = 0; - - if(SoDSlot >= slots::MainAmmo && SoDSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = SoDSlot - 1; - - else if(SoDSlot >= consts::GENERAL_BAGS_BEGIN && SoDSlot <= consts::CURSOR_BAG_END) - ServerSlot = SoDSlot - 11; - - else if(SoDSlot >= consts::BANK_BAGS_BEGIN && SoDSlot <= consts::BANK_BAGS_END) - ServerSlot = SoDSlot - 1; - - else if(SoDSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoDSlot <= consts::SHARED_BANK_BAGS_END) - ServerSlot = SoDSlot - 1; - - else if(SoDSlot == slots::MainPowerSource) - ServerSlot = MainPowerSource; - - else - ServerSlot = SoDSlot; - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to SoD Corpse Slot IDs for use in Encodes -static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse) { - uint32 SoDCorpse; - // reserved -} -*/ -/* -// Converts SoD Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_OpenNewTasksWindow) { - - AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; - AvailableTaskData1_Struct* __emu_AvailableTaskData1; - AvailableTaskData2_Struct* __emu_AvailableTaskData2; - AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; - - structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; - structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; - structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; - structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; - - // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. - // - in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); - - in->pBuffer = new unsigned char[in->size]; - - unsigned char *__eq_buffer = in->pBuffer; - - __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; - - char *__eq_ptr, *__emu_Ptr; - - // Copy Header - // - // - - __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; - __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; - __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; - - __emu_Ptr = (char *) __emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); - __eq_ptr = (char *) __eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); - - for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in Live packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Title - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Description - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); - - - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; - - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } - - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->level = emu->level[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->gender = emu->gender[r]; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->beard = emu->beard[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; - } - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->tutorial = emu->tutorial[r]; // was u15 - eq2->u15 = 0xff; - eq2->deity = emu->deity[r]; - eq2->zone = emu->zone[r]; - eq2->u19 = 0xFF; - eq2->race = emu->race[r]; - eq2->gohome = emu->gohome[r]; - eq2->class_ = emu->class_[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); - } - - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); - - //this is SUCH bullshit to be doing from down here. but the - // new client requires us to close immediately following this - // packet, so do it. - //dest->Close(); -} - -//hack hack hack -ENCODE(OP_SendZonepoints) { - ENCODE_LENGTH_ATLEAST(ZonePoints); - - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); - - memcpy(eq, emu, __packet->size); - - FINISH_ENCODE(); -// unknown0xxx[24]; - //this is utter crap... the client is waiting for this - //certain 0 length opcode to come after the reqclientspawn - //stuff... so this is a dirty way to put it in there. - // this needs to be done better - - //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); - //dest->QueuePacket(&hack_test); - -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 5 is for SoD - if (emu->clientver <= 5 ) +// ENCODE methods + ENCODE(OP_Action) { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + eq->sequence = emu->sequence; + OUT(type); + //OUT(damage); + OUT(spell); + eq->level2 = emu->level; + OUT(buff_unknown); // if this is 4, a buff icon is made + //eq->unknown0036 = -1; + //eq->unknown0040 = -1; + //eq->unknown0044 = -1; + + FINISH_ENCODE(); + } + + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToSoDSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_AltCurrencySell) + { + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + OUT(merchant_entity_id); + eq->slot_id = ServerToSoDSlot(emu->slot_id); + OUT(charges); OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); - eq->type = emu->sof_type; - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); - memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(points); // Relocation Test -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - eq->equipment[r].equip0 = emu->item_material[r]; - eq->equipment[r].equip1 = 0; - eq->equipment[r].itemId = 0; - //eq->colors[r].color = emu->colors[r].color; - } - for(r = 0; r < 7; r++) { - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - //NOTE: new client supports 300 AAs, our internal rep/PP - //only supports 240.. - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(AGI); - OUT(INT); - OUT(DEX); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); -// OUT(unknown4184[128]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } - OUT_array(recastTimers, structs::MAX_RECAST_TYPES); -// OUT(unknown08124[360]); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - //NOTE: new client supports 20 bandoliers, our internal rep - //only supports 4.. - for(r = 0; r < 4; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[12]); - OUT(exp); -// OUT(unknown13072[8]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(y); //reversed x and y - OUT(x); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - //OUT(expansions); - eq->expansions = 16383; -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); -// OUT(unknown14628); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown17892[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; - OUT(RestTimer); -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0xa3,0x02,0x00,0x00,0x95,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F, -0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - - memcpy(eq->unknown12864, bytes, sizeof(bytes)); - - - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown800 = -1; - eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; - eq->unknown888 = 1; - eq->unknown889 = 0; - eq->unknown890 = 1; - eq->unknown891 = 0; - eq->unknown892 = 0; - eq->unknown893 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown895 = 0; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 180; - eq->unknown908 = 2; - eq->unknown912 = 2; - eq->FogDensity = emu->fog_density; - - FINISH_ENCODE(); -} - - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_ApplyPoison) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToSoDSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); } - int PacketSize = 2; - - for(int i = 0; i < EntryCount; ++i, ++emu) - PacketSize += (12 + strlen(emu->name)); - - emu = (Track_Struct *) __emu_buffer; - - in->size = PacketSize; - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); - - for(int i = 0; i < EntryCount; ++i, ++emu) + ENCODE(OP_Barter) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - VARSTRUCT_ENCODE_STRING(Buffer, emu->name); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); - } + EQApplicationPacket *in = *p; + *p = nullptr; - delete[] __emu_buffer; + char *Buffer = (char *)in->pBuffer; + uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_PetBuffWindow) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - PetBuff_Struct *emu = (PetBuff_Struct *) __emu_buffer; - - int PacketSize = 7 + (emu->buffcount * 13); - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); - - for(unsigned int i = 0; i < BUFF_COUNT; ++i) - { - if(emu->spellid[i]) + if (SubAction != Barter_BuyerAppearance) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + dest->FastQueuePacket(&in, ack_req); + + return; } - } - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); - delete[] __emu_buffer; + unsigned char *__emu_buffer = in->pBuffer; + in->size = 80; + in->pBuffer = new unsigned char[in->size]; + char *OutBuffer = (char *)in->pBuffer; + char Name[64]; - dest->FastQueuePacket(&in, ack_req); -} + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); + uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + VARSTRUCT_DECODE_STRING(Name, Buffer); + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + OutBuffer = (char *)in->pBuffer + 72; + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); -ENCODE(OP_Barter) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - - if(SubAction != Barter_BuyerAppearance) - { + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - - return; } - unsigned char *__emu_buffer = in->pBuffer; - - in->size = 80; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - char Name[64]; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); - uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); - uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - VARSTRUCT_DECODE_STRING(Name, Buffer); - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - OutBuffer = (char *)in->pBuffer + 72; - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) + ENCODE(OP_BazaarSearch) { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + in->pBuffer = new unsigned char[in->size]; + memset(in->pBuffer, 0, in->size); + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(SellerID); + memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); + OUT(NumItems); + OUT(ItemID); + OUT(SerialNumber); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(Cost); + OUT(ItemStat); + } + + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - - return; } - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + ENCODE(OP_Buff) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); + + OUT(entityid); + OUT(slot); + OUT(level); + OUT(effect); + //eq->unknown7 = 10; + OUT(spellid); + OUT(duration); + OUT(slotid); + OUT(bufffade); + + FINISH_ENCODE(); } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - in->pBuffer = new unsigned char[in->size]; - - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) + ENCODE(OP_CancelTrade) { - OUT(Beginning.Action); - OUT(SellerID); - memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); - OUT(NumItems); - OUT(ItemID); - OUT(SerialNumber); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(Cost); - OUT(ItemStat); + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); + + OUT(fromid); + OUT(action); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; - dest->FastQueuePacket(&in, ack_req); -} + *p = nullptr; -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { + if (in->size == 0) { + + in->size = 4; + + in->pBuffer = new uchar[in->size]; + + *((uint32 *)in->pBuffer) = 0; + + dest->FastQueuePacket(&in, ack_req); + + return; + } + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + + uchar *OldBuffer = in->pBuffer; + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToSoDSlot(emu->from_slot); + eq->to_slot = ServerToSoDSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->unknown004 = 785316192; + eq->unknown008 = 435601; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + ENCODE_LENGTH_EXACT(Object_Struct); + SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); + + OUT(drop_id); + OUT(zone_id); + OUT(zone_instance); + OUT(heading); + OUT(x); + OUT(y); + OUT(z); + OUT_str(object_name); + OUT(object_type); + OUT(spawn_id); + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown020 = 0; + eq->unknown024 = 0; + eq->size = 1; //This forces all objects to standard size for now + eq->unknown088 = 0; + memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupCancelInvite) + { + ENCODE_LENGTH_EXACT(GroupCancel_Struct); + SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + OUT(toggle); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow2) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupInvite) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); + memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupUpdate) + { + //_log(NET__ERROR, "OP_GroupUpdate"); + EQApplicationPacket *in = *p; + GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; + + //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); + if ((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + { + if ((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) + { + //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); + dest->FastQueuePacket(&outapp); + + // Make an empty GLAA packet to clear out their useable GLAAs + // + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + dest->FastQueuePacket(&outapp); + delete in; + return; + } + //if(gjs->action == groupActLeave) + // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + + if (in->size == sizeof(GroupUpdate2_Struct)) + { + // Group Update2 + //_log(NET__ERROR, "Struct is GroupUpdate2"); + + unsigned char *__emu_buffer = in->pBuffer; + GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*)__emu_buffer; + + //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + + int MemberCount = 1; + int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + + for (int i = 0; i < 5; ++i) + { + //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); + if (gu2->membername[i][0] != '\0') + { + PacketLength += (22 + strlen(gu2->membername[i]) + 1); + ++MemberCount; + } + } + + //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + char *Buffer = (char *)outapp->pBuffer; + + // Header + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + + // Leader + // + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + + int MemberNumber = 1; + + for (int i = 0; i < 5; ++i) + { + if (gu2->membername[i][0] == '\0') + continue; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = gu2->NPCMarkerID; + memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); + + dest->FastQueuePacket(&outapp); + delete in; + return; + } + + //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + ENCODE_LENGTH_EXACT(GroupJoin_Struct); + SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); + + memcpy(eq->membername, emu->membername, sizeof(eq->membername)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = emu->NPCMarkerID; + + memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); + //_hex(NET__ERROR, __packet->pBuffer, __packet->size); + + FINISH_ENCODE(); + + dest->FastQueuePacket(&outapp); + } + + ENCODE(OP_GuildMemberList) + { //consume the packet EQApplicationPacket *in = *p; *p = nullptr; //store away the emu struct unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); + + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); + + eq->slot = ServerToSoDSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strcpy(eq->worldshortname, emu->worldshortname); + + OUT(enablevoicemacros); + OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + // These next two need to be set like this for the Tutorial Button to work. + eq->unknown263[0] = 0; + eq->unknown263[2] = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = ServerToSoDCorpseSlot(emu->slot_id); + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MercenaryDataResponse) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; + PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - 4) * emu->MercCount; + + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); + for (r = 0; r < emu->MercTypeCount; r++) + { + if (emu->MercTypeCount > 0) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); + } + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + if (emu->MercCount) + { + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); + if (emu->Mercs[r].StanceCount > 0) + { + for (k = 0; k < emu->Mercs[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); + } + } + } + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + // This packet does not appear to exist in SoD, but leaving it here just in case + ENCODE(OP_MercenaryDataUpdate) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + EQApplicationPacket *outapp; + uint32 PacketSize = 0; + + // There are 2 different sized versions of this packet depending if a merc is hired or not + if (emu->MercStatus >= 0) + { + PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; + } + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); + for (k = 0; k < emu->MercData[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); + } + } + else + { + PacketSize += sizeof(structs::NoMercenaryHired_Struct); + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToSoDSlot(emu->from_slot); + eq->to_slot = ServerToSoDSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown800 = -1; + eq->unknown844 = 600; + eq->unknown880 = 50; + eq->unknown884 = 10; + eq->unknown888 = 1; + eq->unknown889 = 0; + eq->unknown890 = 1; + eq->unknown891 = 0; + eq->unknown892 = 0; + eq->unknown893 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown895 = 0; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 180; + eq->unknown908 = 2; + eq->unknown912 = 2; + eq->FogDensity = emu->fog_density; + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + memcpy(eq->Title, emu->Title, sizeof(eq->Title)); + memcpy(eq->Text, emu->Text, sizeof(eq->Text)); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + OUT(NegativeID); + // These two field names are used if Buttons == 1. + OUT_str(ButtonName0); + OUT_str(ButtonName1); + + FINISH_ENCODE(); + } + + ENCODE(OP_OpenNewTasksWindow) + { + AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; + AvailableTaskData1_Struct* __emu_AvailableTaskData1; + AvailableTaskData2_Struct* __emu_AvailableTaskData2; + AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; + + structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; + structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; + structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; + structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; + + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; + + // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. + // + in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); + in->pBuffer = new unsigned char[in->size]; + unsigned char *__eq_buffer = in->pBuffer; + __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; + char *__eq_ptr, *__emu_Ptr; + + // Copy Header + // + // + + __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; + __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; + __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; + + __emu_Ptr = (char *)__emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); + __eq_ptr = (char *)__eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); + + for (uint32 i = 0; i<__emu_AvailableTaskHeader->TaskCount; i++) { + + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in Live packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Title + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Description + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PetBuffWindow) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + PetBuff_Struct *emu = (PetBuff_Struct *)__emu_buffer; + int PacketSize = 7 + (emu->buffcount * 13); + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); + + for (unsigned int i = 0; i < BUFF_COUNT; ++i) + { + if (emu->spellid[i]) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + } + } + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); + memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(points); // Relocation Test + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + eq->equipment[r].equip0 = emu->item_material[r]; + eq->equipment[r].equip1 = 0; + eq->equipment[r].itemId = 0; + //eq->colors[r].color = emu->colors[r].color; + } + for (r = 0; r < 7; r++) { + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + //NOTE: new client supports 300 AAs, our internal rep/PP + //only supports 240.. + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(AGI); + OUT(INT); + OUT(DEX); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + // OUT(unknown4184[128]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + // OUT(unknown08124[360]); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + //NOTE: new client supports 20 bandoliers, our internal rep + //only supports 4.. + for (r = 0; r < 4; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[12]); + OUT(exp); + // OUT(unknown13072[8]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(y); //reversed x and y + OUT(x); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + //OUT(expansions); + eq->expansions = 16383; + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + // OUT(unknown14628); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown17892[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + OUT(RestTimer); + // OUT(unknown19584[4]); + // OUT(unknown19588); + + const uint8 bytes[] = { + 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(eq->unknown12864, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_RaidJoin) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; + } + + ENCODE(OP_RaidUpdate) + { + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates + { + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 14 || raid_gen->action == 30) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); + } + else + { + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); + } + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + eq->invslot = ServerToSoDSlot(emu->invslot); + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 5 is for SoD + if (emu->clientver <= 5) + { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->level = emu->level[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->gender = emu->gender[r]; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->beard = emu->beard[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->tutorial = emu->tutorial[r]; // was u15 + eq2->u15 = 0xff; + eq2->deity = emu->deity[r]; + eq2->zone = emu->zone[r]; + eq2->u19 = 0xFF; + eq2->race = emu->race[r]; + eq2->gohome = emu->gohome[r]; + eq2->class_ = emu->class_[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + } + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + //hack hack hack + ENCODE(OP_SendZonepoints) + { + ENCODE_LENGTH_ATLEAST(ZonePoints); + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); + + memcpy(eq, emu, __packet->size); + + FINISH_ENCODE(); + // unknown0xxx[24]; + //this is utter crap... the client is waiting for this + //certain 0 length opcode to come after the reqclientspawn + //stuff... so this is a dirty way to put it in there. + // this needs to be done better + + //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); + //dest->QueuePacket(&hack_test); + } + + ENCODE(OP_ShopPlayerBuy) + { + ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); + SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + OUT(npcid); + OUT(playerid); + OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToSoDSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strcpy(eq->model_name, emu->model_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + strcpy(eq[r].name, emu[r].name); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0076 = 0; + eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0078 = 0; + eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 0; + eq[r].unknown0082 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_TargetBuffs) + { + SETUP_VAR_ENCODE(BuffIcon_Struct); + + uint32 sz = 7 + (13 * emu->count); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); + + uchar *ptr = __packet->pBuffer; + *((uint32*)ptr) = emu->entity_id; + ptr += sizeof(uint32); + + *((uint16*)ptr) = emu->count; + ptr += sizeof(uint16); + + for (uint16 i = 0; i < emu->count; ++i) + { + *((uint32*)ptr) = emu->entries[i].buff_slot; + ptr += sizeof(uint32); + *((uint32*)ptr) = emu->entries[i].spell_id; + ptr += sizeof(uint32); + *((uint32*)ptr) = emu->entries[i].tics_remaining; + ptr += sizeof(uint32); + ptr += 1; + } + /*std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint8 write_var8 = 1; + ss.write((const char*)&emu->entity_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint16)); + write_var8 = 0; + for(uint16 i = 0; i < emu->count; ++i) + { + ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); + ss.write((const char*)&write_var8, sizeof(uint8)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + */ + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + int PacketSize = 2; + + for (int i = 0; i < EntryCount; ++i, ++emu) + PacketSize += (12 + strlen(emu->name)); + + emu = (Track_Struct *)__emu_buffer; + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); + + for (int i = 0; i < EntryCount; ++i, ++emu) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + VARSTRUCT_ENCODE_STRING(Buffer, emu->name); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToSoDSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (unsigned int i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strcpy(vr->items[x].item_name, ivr->items[x].item_name); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_WhoAllResponse) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *InBuffer = (char *)in->pBuffer; + + WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; + + int Count = wars->playercount; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); + + char *OutBuffer = (char *)outapp->pBuffer; + + memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); + + OutBuffer += sizeof(WhoAllReturnStruct); + InBuffer += sizeof(WhoAllReturnStruct); + + for (int i = 0; i < Count; ++i) + { + uint32 x; + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + InBuffer += 4; + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); + + char Name[64]; + + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + for (int j = 0; j < 7; ++j) + { + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + delete in; + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = zps->bind_zone_id; + zph->bind_instance_id = zps->bind_instance_id; + strcpy(zph->zone_name, zps->zone_name); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + + //this is SUCH bullshit to be doing from down here. but the + // new client requires us to close immediately following this + // packet, so do it. + //dest->Close(); + } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - emu = (Spawn_Struct *) __emu_buffer; + emu = (Spawn_Struct *)__emu_buffer; //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - char *Buffer = (char *) in->pBuffer; - + char *Buffer = (char *)in->pBuffer; int r; int k; - for(r = 0; r < entrycount; r++, emu++) { + for (r = 0; r < entrycount; r++, emu++) { int PacketSize = sizeof(structs::Spawn_Struct); PacketSize += strlen(emu->name); PacketSize += strlen(emu->lastName); - if(strlen(emu->title)) + if (strlen(emu->title)) PacketSize += strlen(emu->title) + 1; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) PacketSize += strlen(emu->suffix) + 1; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { PacketSize = PacketSize - 4; // No bodytype PacketSize += 53; // Fixed portion @@ -1010,7 +2315,7 @@ ENCODE(OP_ZoneSpawns) { } bool ShowName = 1; - if(emu->bodytype >= 66) + if (emu->bodytype >= 66) { emu->race = 127; emu->bodytype = 11; @@ -1019,30 +2324,30 @@ ENCODE(OP_ZoneSpawns) { } float SpawnSize = emu->size; - if(!((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) + if (!((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) { PacketSize -= (sizeof(structs::EquipStruct) * 9); - if(emu->size == 0) + if (emu->size == 0) { emu->size = 6; SpawnSize = 6; } } - if(SpawnSize == 0) + if (SpawnSize == 0) { SpawnSize = 3; } EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - Buffer = (char *) outapp->pBuffer; + Buffer = (char *)outapp->pBuffer; VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 } @@ -1073,10 +2378,10 @@ ENCODE(OP_ZoneSpawns) { Bitfields->showname = ShowName; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); - Buffer = Buffer -4; + Buffer = Buffer - 4; } Bitfields->ispet = emu->is_pet; @@ -1085,18 +2390,18 @@ ENCODE(OP_ZoneSpawns) { uint8 OtherData = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) OtherData = OtherData | 0x04; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) OtherData = OtherData | 0x08; - if(emu->DestructibleObject) + if (emu->DestructibleObject) OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); } @@ -1106,7 +2411,7 @@ ENCODE(OP_ZoneSpawns) { } VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); @@ -1138,15 +2443,15 @@ ENCODE(OP_ZoneSpawns) { /* if(emu->bodytype >=66) { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname } else { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname }*/ - if(!emu->DestructibleObject) + if (!emu->DestructibleObject) { // Setting this next field to zero will cause a crash. Looking at ShowEQ, if it is zero, the bodytype field is not // present. Will sort that out later. @@ -1170,7 +2475,7 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); - if(emu->NPC) + if (emu->NPC) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); @@ -1216,9 +2521,9 @@ ENCODE(OP_ZoneSpawns) { Buffer += sizeof(structs::Spawn_Struct_Position); - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu ->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { - for(k = 0; k < 9; ++k) + for (k = 0; k < 9; ++k) { { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); @@ -1241,11 +2546,11 @@ ENCODE(OP_ZoneSpawns) { } - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - for(k = 0; k < 9; k++) { + for (k = 0; k < 9; k++) { Equipment[k].equip0 = emu->equipment[k]; Equipment[k].equip1 = 0; Equipment[k].itemId = 0; @@ -1253,12 +2558,12 @@ ENCODE(OP_ZoneSpawns) { Buffer += (sizeof(structs::EquipStruct) * 9); } - if(strlen(emu->title)) + if (strlen(emu->title)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->title); } - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); } @@ -1269,2294 +2574,1101 @@ ENCODE(OP_ZoneSpawns) { Buffer += 24; // Unknown; dest->FastQueuePacket(&outapp, ack_req); - } - - delete in; -} - -ENCODE(OP_MercenaryDataResponse) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; - - PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - 4) * emu->MercCount; - - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); - for(r = 0; r < emu->MercTypeCount; r++) - { - if(emu->MercTypeCount > 0) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); } - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - if(emu->MercCount) - { - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); - if(emu->Mercs[r].StanceCount > 0) - { - for(k = 0; k < emu->Mercs[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); - } - } - } - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -// This packet does not appear to exist in SoD, but leaving it here just in case -ENCODE(OP_MercenaryDataUpdate) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - EQApplicationPacket *outapp; - - uint32 PacketSize = 0; - - // There are 2 different sized versions of this packet depending if a merc is hired or not - if (emu->MercStatus >= 0) - { - PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; - } - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); - for(k = 0; k < emu->MercData[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); - } - } - else - { - PacketSize += sizeof(structs::NoMercenaryHired_Struct); - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; - - dest->FastQueuePacket(&in, ack_req); - - return; - } - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); delete in; - - return; } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strcpy(eq[r].name, emu[r].name); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0076 = 0; - eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0078 = 0; - eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 0; - eq[r].unknown0082 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) { - ENCODE_LENGTH_EXACT(Object_Struct); - SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); - OUT(drop_id); - OUT(zone_id); - OUT(zone_instance); - OUT(heading); - OUT(x); - OUT(y); - OUT(z); - OUT_str(object_name); - OUT(object_type); - OUT(spawn_id); - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown020 = 0; - eq->unknown024 = 0; - eq->size = 1; //This forces all objects to standard size for now - eq->unknown088 = 0; - memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); - FINISH_ENCODE(); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - memcpy(eq->Title, emu->Title, sizeof(eq->Title)); - memcpy(eq->Text, emu->Text, sizeof(eq->Text)); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - OUT(NegativeID); - // These two field names are used if Buttons == 1. - OUT_str(ButtonName0); - OUT_str(ButtonName1); - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerBuy) -{ - ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); - SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - OUT(npcid); - OUT(playerid); - OUT(itemslot); - OUT(quantity); - OUT(price); - - FINISH_ENCODE(); -} - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strcpy(eq->worldshortname, emu->worldshortname); - - OUT(enablevoicemacros); - OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - // These next two need to be set like this for the Tutorial Button to work. - eq->unknown263[0] = 0; - eq->unknown263[2] = 1; - - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - OUT(entityid); - OUT(slot); - OUT(level); - OUT(effect); - //eq->unknown7 = 10; - OUT(spellid); - OUT(duration); - OUT(slotid); - OUT(bufffade); - FINISH_ENCODE(); -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToSoDSlot(emu->itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToSoDSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToSoDSlot(emu->from_slot); - eq->to_slot = ServerToSoDSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToSoDSlot(emu->from_slot); - eq->to_slot = ServerToSoDSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToSoDSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - ENCODE_FORWARD(OP_TraderBuy); -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToSoDSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strcpy(eq->model_name, emu->model_name); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = ServerToSoDSlot(emu->invslot); - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = zps->bind_zone_id; - zph->bind_instance_id = zps->bind_instance_id; - strcpy(zph->zone_name, zps->zone_name); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToSoDSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + IN(npcid); + emu->slot = SoDToServerSlot(eq->slot); + IN(charges); + IN(sell_price); - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); - } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); - } - delete[] __emu_buffer; -} - -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strcpy(vr->items[x].item_name, ivr->items[x].item_name); - vr->items[x].charges = ivr->items[x].charges; - } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + FINISH_DIRECT_DECODE(); } - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_WhoAllResponse) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *InBuffer = (char *)in->pBuffer; - - WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; - - int Count = wars->playercount; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); - - char *OutBuffer = (char *)outapp->pBuffer; - - memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); - - OutBuffer += sizeof(WhoAllReturnStruct); - InBuffer += sizeof(WhoAllReturnStruct); - - for(int i = 0; i < Count; ++i) + DECODE(OP_AltCurrencySell) { - uint32 x; + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + IN(merchant_entity_id); + emu->slot_id = SoDToServerSlot(eq->slot_id); + IN(charges); + IN(cost); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - - InBuffer += 4; - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); - - char Name[64]; - - - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - for(int j = 0; j < 7; ++j) - { - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); - } - - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + FINISH_DIRECT_DECODE(); } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; -} - -ENCODE(OP_GroupInvite) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); - memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow2) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupCancelInvite) -{ - ENCODE_LENGTH_EXACT(GroupCancel_Struct); - SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - OUT(toggle); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->unknown004 = 785316192; - eq->unknown008 = 435601; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) + DECODE(OP_AltCurrencySellSelection) { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = SoDToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); } - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) + DECODE(OP_ApplyPoison) { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = SoDToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); } - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) + DECODE(OP_AugmentInfo) { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); } - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_TargetBuffs) -{ - SETUP_VAR_ENCODE(BuffIcon_Struct); - - uint32 sz = 7 + (13 * emu->count); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - uchar *ptr = __packet->pBuffer; - *((uint32*)ptr) = emu->entity_id; - ptr += sizeof(uint32); - - *((uint16*)ptr) = emu->count; - ptr += sizeof(uint16); - - for(uint16 i = 0; i < emu->count; ++i) + DECODE(OP_AugmentItem) { - *((uint32*)ptr) = emu->entries[i].buff_slot; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].spell_id; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].tics_remaining; - ptr += sizeof(uint32); - ptr += 1; - } - /*std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - uint8 write_var8 = 1; - ss.write((const char*)&emu->entity_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint16)); - write_var8 = 0; - for(uint16 i = 0; i < emu->count; ++i) - { - ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); - ss.write((const char*)&write_var8, sizeof(uint8)); + emu->container_slot = SoDToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); } - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - */ - - FINISH_ENCODE(); -} - -ENCODE(OP_GroupUpdate) -{ - //_log(NET__ERROR, "OP_GroupUpdate"); - EQApplicationPacket *in = *p; - - GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; - - //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); - if((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + DECODE(OP_BazaarSearch) { - if((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) - { - //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + char *Buffer = (char *)__packet->pBuffer; - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); - dest->FastQueuePacket(&outapp); - - // Make an empty GLAA packet to clear out their useable GLAAs - // - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - dest->FastQueuePacket(&outapp); - - delete in; + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) return; - } - //if(gjs->action == groupActLeave) - // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; - return; + SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); + MEMSET_IN(structs::NewBazaarInspect_Struct); + IN(Beginning.Action); + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + IN(SerialNumber); + FINISH_DIRECT_DECODE(); } - if(in->size == sizeof(GroupUpdate2_Struct)) + DECODE(OP_Buff) { - // Group Update2 - //_log(NET__ERROR, "Struct is GroupUpdate2"); + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - unsigned char *__emu_buffer = in->pBuffer; - GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*) __emu_buffer; + IN(entityid); + IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); - //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + FINISH_DIRECT_DECODE(); + } - int MemberCount = 1; + DECODE(OP_Bug) + { + DECODE_LENGTH_EXACT(structs::BugStruct); + SETUP_DIRECT_DECODE(BugStruct, structs::BugStruct); - int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + strn0cpy(emu->chartype, eq->chartype, sizeof(emu->chartype)); + strn0cpy(emu->name, eq->name, sizeof(emu->name)); + strn0cpy(emu->ui, eq->ui, sizeof(emu->ui)); + IN(x); + IN(y); + IN(z); + IN(heading); + strn0cpy(emu->target_name, eq->target_name, sizeof(emu->target_name)); + strn0cpy(emu->bug, eq->bug, sizeof(emu->bug)); + strn0cpy(emu->system_info, eq->system_info, sizeof(emu->system_info)); - for(int i = 0; i < 5; ++i) + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = SoDToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(hairstyle); + IN(gender); + IN(race); + IN(start_zone); + IN(haircolor); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(tutorial); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = SoDToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = SoDToServerSlot(eq->from_slot); + emu->to_slot = SoDToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupCancelInvite) + { + DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); + SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + IN(toggle); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupDisband) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_Disband"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupInvite"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); + memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite2) + { + //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); + DECODE_FORWARD(OP_GroupInvite); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + IN(icon); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemVerifyRequest) + { + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = SoDToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LoadSpellSet) + { + DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); + SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); + + for (uint32 i = 0; i < MAX_PP_MEMSPELL; ++i) + emu->spell[i] = eq->spell[i]; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = SoDToServerCorpseSlot(eq->slot_id); + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = SoDToServerSlot(eq->from_slot); + emu->to_slot = SoDToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RaidInvite) + { + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); + + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } + } + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + emu->invslot = SoDToServerSlot(eq->invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Save) + { + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerBuy) + { + DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); + SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + IN(npcid); + IN(playerid); + IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = SoDToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = SoDToServerSlot(eq->container_slot); + IN(guildtribute_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = SoDToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(unknown06); + IN(elite_material); + IN(color.color); + IN(wear_slot_id); + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; + + static inline int32 GetNextItemInstSerialNumber() + { + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; + else + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; + } + + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); + SoD::structs::ItemSerializationHeader hdr; + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + int32 slot_id = ServerToSoDSlot(slot_id_in); + + hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.unknown062 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(SoD::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) { - //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); - if(gu2->membername[i][0] != '\0') - { - PacketLength += (22 + strlen(gu2->membername[i]) + 1); - ++MemberCount; + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + if (strlen(item->Lore) > 0) + { + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoD::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(SoD::structs::ItemBodyStruct)); + + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; + + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.unknown6 = 0; + ibs.SkillModType = item->SkillModType; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + ibs.RecLevel = item->RecLevel; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.SellRate = item->SellRate; + + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; + + ss.write((const char*)&ibs, sizeof(SoD::structs::ItemBodyStruct)); + + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoD::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(SoD::structs::ItemSecondaryBodyStruct)); + + isbs.augtype = item->AugType; + isbs.augrestrict = item->AugRestrict; + + for (int x = 0; x < 5; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } + + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; + + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; + + isbs.book = item->Book; + isbs.booktype = item->BookType; + + ss.write((const char*)&isbs, sizeof(SoD::structs::ItemSecondaryBodyStruct)); + + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoD::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(SoD::structs::ItemTertiaryBodyStruct)); + + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.no_pet = item->NoPet; + + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; + + ss.write((const char*)&itbs, sizeof(SoD::structs::ItemTertiaryBodyStruct)); + + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; + + SoD::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(SoD::structs::ClickEffectStruct)); + + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; + + ss.write((const char*)&ices, sizeof(SoD::structs::ClickEffectStruct)); + + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + + SoD::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(SoD::structs::ProcEffectStruct)); + + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; + + ss.write((const char*)&ipes, sizeof(SoD::structs::ProcEffectStruct)); + + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + + SoD::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(SoD::structs::WornEffectStruct)); + + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; + + ss.write((const char*)&iwes, sizeof(SoD::structs::WornEffectStruct)); + + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoD::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(SoD::structs::WornEffectStruct)); + + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; + + ss.write((const char*)&ifes, sizeof(SoD::structs::WornEffectStruct)); + + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoD::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(SoD::structs::WornEffectStruct)); + + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; + + ss.write((const char*)&ises, sizeof(SoD::structs::WornEffectStruct)); + + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects + + SoD::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); + + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.unknown15 = 0xffffffff; + + iqbs.Purity = item->Purity; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + iqbs.clairvoyance = item->Clairvoyance; + + iqbs.subitem_count = 0; + + char *SubSerializations[10]; // + + uint32 SubLengths[10]; + + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + + SubSerializations[x] = nullptr; + + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if (subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? + + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ + + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); } } - //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + ss.write((const char*)&iqbs, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + for (int x = 0; x < 10; ++x) { - char *Buffer = (char *)outapp->pBuffer; + if (SubSerializations[x]) { - // Header - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + ss.write((const char*)&x, sizeof(uint32)); - // Leader - // + ss.write(SubSerializations[x], SubLengths[x]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - - int MemberNumber = 1; - - for(int i = 0; i < 5; ++i) - { - if(gu2->membername[i][0] == '\0') - continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + safe_delete_array(SubSerializations[x]); + } } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = gu2->NPCMarkerID; - memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); - - dest->FastQueuePacket(&outapp); - delete in; - - return; + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + *length = ss.tellp(); + return item_serial; } - //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - ENCODE_LENGTH_EXACT(GroupJoin_Struct); - SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); - memcpy(eq->membername, emu->membername, sizeof(eq->membername)); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = emu->NPCMarkerID; - - memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); - //_hex(NET__ERROR, __packet->pBuffer, __packet->size); - FINISH_ENCODE(); - - dest->FastQueuePacket(&outapp); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToSoDSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -DECODE(OP_BazaarSearch) -{ - char *Buffer = (char *)__packet->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) - return; - - SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); - MEMSET_IN(structs::NewBazaarInspect_Struct); - IN(Beginning.Action); - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - IN(SerialNumber); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = SoDToServerSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = SoDToServerSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = SoDToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = SoDToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - IN(slot); - IN(spell_id); - emu->inventoryslot = SoDToServerSlot(eq->inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = SoDToServerSlot(eq->from_slot); - emu->to_slot = SoDToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); - - emu->from_slot = SoDToServerSlot(eq->from_slot); - emu->to_slot = SoDToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerBuy) -{ - DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); - SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - - IN(npcid); - IN(playerid); - IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(hairstyle); - IN(gender); - IN(race); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupInvite2) -{ - //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); - DECODE_FORWARD(OP_GroupInvite); -} - -DECODE(OP_GroupInvite) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupInvite"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); - memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupDisband) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_Disband"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupCancelInvite) -{ - DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); - SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - IN(toggle); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - IN(entityid); - IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = SoDToServerSlot(eq->itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(unknown06); - IN(elite_material); - IN(color.color); - IN(wear_slot_id); - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) -{ - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = SoDToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = SoDToServerSlot(eq->invslot); - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - emu->container_slot = SoDToServerSlot(eq->container_slot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = SoDToServerSlot(eq->container_slot); - emu->augment_slot = eq->augment_slot; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LoadSpellSet) -{ - DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); - SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); - - for(uint32 i = 0; i < MAX_PP_MEMSPELL; ++i) - emu->spell[i] = eq->spell[i]; - - FINISH_DIRECT_DECODE(); -} - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - SoD::structs::ItemSerializationHeader hdr; - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - int32 slot_id = ServerToSoDSlot(slot_id_in); - - hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.unknown062 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(SoD::structs::ItemSerializationHeader)); - - if(strlen(item->Name) > 0) + static inline uint32 ServerToSoDSlot(uint32 ServerSlot) { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); + uint32 SoDSlot = 0; + + if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + SoDSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) + SoDSlot = ServerSlot + 11; + else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) + SoDSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) + SoDSlot = ServerSlot + 1; + else if (ServerSlot == MainPowerSource) + SoDSlot = slots::MainPowerSource; + else + SoDSlot = ServerSlot; + return SoDSlot; } - else + + static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse) { - ss.write((const char*)&null_term, sizeof(uint8)); + //uint32 SoDCorpse; + return (ServerCorpse + 1); } - if(strlen(item->Lore) > 0) + static inline uint32 SoDToServerSlot(uint32 SoDSlot) { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); + uint32 ServerSlot = 0; + + if (SoDSlot >= slots::MainAmmo && SoDSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots + ServerSlot = SoDSlot - 1; + else if (SoDSlot >= consts::GENERAL_BAGS_BEGIN && SoDSlot <= consts::CURSOR_BAG_END) + ServerSlot = SoDSlot - 11; + else if (SoDSlot >= consts::BANK_BAGS_BEGIN && SoDSlot <= consts::BANK_BAGS_END) + ServerSlot = SoDSlot - 1; + else if (SoDSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoDSlot <= consts::SHARED_BANK_BAGS_END) + ServerSlot = SoDSlot - 1; + else if (SoDSlot == slots::MainPowerSource) + ServerSlot = MainPowerSource; + else + ServerSlot = SoDSlot; + return ServerSlot; } - else + + static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse) { - ss.write((const char*)&null_term, sizeof(uint8)); + //uint32 ServerCorpse; + return (SoDCorpse - 1); } - - if(strlen(item->IDFile) > 0) - { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoD::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(SoD::structs::ItemBodyStruct)); - - uint32 adjusted_slots = item->Slots; - - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) - { - // Do nothing - } - else - { - if(item->Slots & (1 << 21)) // Ammo Slot from Database - { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF - } - - if(item->Slots & (1 << 22)) // Power Source Slot from Database - { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF - } - } - - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; - - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.unknown6 = 0; - ibs.SkillModType = item->SkillModType; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - ibs.RecLevel = item->RecLevel; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.SellRate = item->SellRate; - - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; - - ss.write((const char*)&ibs, sizeof(SoD::structs::ItemBodyStruct)); - - //charm text - if(strlen(item->CharmFile) > 0) - { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoD::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(SoD::structs::ItemSecondaryBodyStruct)); - - isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; - - for(int x = 0; x < 5; ++x) - { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; - } - - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; - - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; - - isbs.book = item->Book; - isbs.booktype = item->BookType; - - ss.write((const char*)&isbs, sizeof(SoD::structs::ItemSecondaryBodyStruct)); - - if(strlen(item->Filename) > 0) - { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoD::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(SoD::structs::ItemTertiaryBodyStruct)); - - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.no_pet = item->NoPet; - - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; - - ss.write((const char*)&itbs, sizeof(SoD::structs::ItemTertiaryBodyStruct)); - - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; - - SoD::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(SoD::structs::ClickEffectStruct)); - - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; - - ss.write((const char*)&ices, sizeof(SoD::structs::ClickEffectStruct)); - - if(strlen(item->ClickName) > 0) - { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - - SoD::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(SoD::structs::ProcEffectStruct)); - - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; - - ss.write((const char*)&ipes, sizeof(SoD::structs::ProcEffectStruct)); - - if(strlen(item->ProcName) > 0) - { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - - SoD::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(SoD::structs::WornEffectStruct)); - - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; - - ss.write((const char*)&iwes, sizeof(SoD::structs::WornEffectStruct)); - - if(strlen(item->WornName) > 0) - { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoD::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(SoD::structs::WornEffectStruct)); - - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; - - ss.write((const char*)&ifes, sizeof(SoD::structs::WornEffectStruct)); - - if(strlen(item->FocusName) > 0) - { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoD::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(SoD::structs::WornEffectStruct)); - - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; - - ss.write((const char*)&ises, sizeof(SoD::structs::WornEffectStruct)); - - if(strlen(item->ScrollName) > 0) - { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects - - SoD::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); - - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.unknown15 = 0xffffffff; - - iqbs.Purity = item->Purity; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - iqbs.clairvoyance = item->Clairvoyance; - - iqbs.subitem_count = 0; - - char *SubSerializations[10]; // - - uint32 SubLengths[10]; - - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - - SubSerializations[x] = nullptr; - - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - - if(subitem) { - - int SubSlotNumber; - - iqbs.subitem_count++; - - if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? - - /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); - */ - - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); - } - } - - ss.write((const char*)&iqbs, sizeof(SoD::structs::ItemQuaternaryBodyStruct)); - - for(int x = 0; x < 10; ++x) { - - if(SubSerializations[x]) { - - ss.write((const char*)&x, sizeof(uint32)); - - ss.write(SubSerializations[x], SubLengths[x]); - - safe_delete_array(SubSerializations[x]); - } - } - - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); - - *length = ss.tellp(); - return item_serial; } - -DECODE(OP_Bug) -{ - DECODE_LENGTH_EXACT(structs::BugStruct); - SETUP_DIRECT_DECODE(BugStruct, structs::BugStruct); - strn0cpy(emu->chartype, eq->chartype, sizeof(emu->chartype)); - strn0cpy(emu->name, eq->name, sizeof(emu->name)); - strn0cpy(emu->ui, eq->ui, sizeof(emu->ui)); - IN(x); - IN(y); - IN(z); - IN(heading); - strn0cpy(emu->target_name, eq->target_name, sizeof(emu->target_name)); - strn0cpy(emu->bug, eq->bug, sizeof(emu->bug)); - strn0cpy(emu->system_info, eq->system_info, sizeof(emu->system_info)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoDToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoDToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - - -} //end namespace SoD +// end namespace SoD diff --git a/common/patches/sod_constants.h b/common/patches/sod_constants.h index 76427d55e..8bdf45532 100644 --- a/common/patches/sod_constants.h +++ b/common/patches/sod_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef SoD_CONSTANTS_H_ #define SoD_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace SoD { namespace maps { @@ -181,6 +181,7 @@ namespace SoD { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = false; } diff --git a/common/patches/sod_ops.h b/common/patches/sod_ops.h index d7b87d514..792c9e5d3 100644 --- a/common/patches/sod_ops.h +++ b/common/patches/sod_ops.h @@ -1,117 +1,115 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_GroupInvite) -E(OP_GroupFollow) -E(OP_GroupFollow2) -E(OP_GroupUpdate) -E(OP_GroupCancelInvite) -E(OP_WhoAllResponse) -E(OP_Track) -E(OP_ShopPlayerBuy) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -E(OP_Barter) +E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_Barter) +E(OP_BazaarSearch) +E(OP_Buff) +E(OP_CancelTrade) +E(OP_CharInventory) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_TargetBuffs) -E(OP_AltCurrencySell) -E(OP_WearChange) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) +E(OP_GroundSpawn) +E(OP_GroupCancelInvite) +E(OP_GroupFollow) +E(OP_GroupFollow2) +E(OP_GroupInvite) +E(OP_GroupUpdate) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) +E(OP_OnLevelMessage) +E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendZonepoints) +E(OP_ShopPlayerBuy) +E(OP_ShopPlayerSell) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_TargetBuffs) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_WhoAllResponse) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) +D(OP_BazaarSearch) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) +D(OP_Bug) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) -D(OP_GroupInvite) -D(OP_GroupInvite2) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_DeleteItem) +D(OP_FaceChange) +D(OP_FindPersonRequest) +D(OP_GroupCancelInvite) +D(OP_GroupDisband) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_GroupDisband) -D(OP_GroupCancelInvite) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) +D(OP_GroupInvite) +D(OP_GroupInvite2) D(OP_InspectRequest) -D(OP_WearChange) -D(OP_ShopPlayerBuy) -D(OP_BazaarSearch) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) D(OP_LoadSpellSet) -D(OP_ApplyPoison) -D(OP_DeleteItem) -D(OP_AugmentItem) -D(OP_Bug) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerBuy) +D(OP_ShopPlayerSell) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) +D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/sod_structs.h b/common/patches/sod_structs.h index 99a904b86..88c0ed91a 100644 --- a/common/patches/sod_structs.h +++ b/common/patches/sod_structs.h @@ -519,7 +519,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -711,14 +711,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -1469,11 +1517,11 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; /*0016*/ }; @@ -1503,17 +1551,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; @@ -3090,10 +3138,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -3129,7 +3177,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3562,6 +3610,21 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name @@ -3693,7 +3756,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's @@ -4109,9 +4172,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 4116d3251..2075fd142 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "sof.h" #include "../opcodemgr.h" @@ -7,6 +6,7 @@ #include "../crc32.h" #include "../eq_packet_structs.h" +#include "../misc_functions.h" #include "../string_util.h" #include "../item.h" #include "sof_structs.h" @@ -15,903 +15,1896 @@ #include #include -namespace SoF { +namespace SoF +{ + static const char *name = "SoF"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "SoF"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToSoFSlot(uint32 ServerSlot); + static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 SoFToServerSlot(uint32 SoFSlot); + static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "sof_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "sof_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientSoF; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientSoF; + } #include "ss_define.h" - -// Converts Server Slot IDs to SoF Slot IDs for use in Encodes -static inline uint32 ServerToSoFSlot(uint32 ServerSlot) { - uint32 SoFSlot = 0; - - if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - SoFSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) - SoFSlot = ServerSlot + 11; - - else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) - SoFSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) - SoFSlot = ServerSlot + 1; - - else if (ServerSlot == MainPowerSource) - SoFSlot = slots::MainPowerSource; - - else - SoFSlot = ServerSlot; - - return SoFSlot; -} - -// Converts SoF Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 SoFToServerSlot(uint32 SoFSlot) { - uint32 ServerSlot = 0; - - if(SoFSlot >= slots::MainAmmo && SoFSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = SoFSlot - 1; - - else if(SoFSlot >= consts::GENERAL_BAGS_BEGIN && SoFSlot <= consts::CURSOR_BAG_END) - ServerSlot = SoFSlot - 11; - - else if(SoFSlot >= consts::BANK_BAGS_BEGIN && SoFSlot <= consts::BANK_BAGS_END) - ServerSlot = SoFSlot - 1; - - else if(SoFSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoFSlot <= consts::SHARED_BANK_BAGS_END) - ServerSlot = SoFSlot - 1; - - else if(SoFSlot == slots::MainPowerSource) - ServerSlot = MainPowerSource; - - else - ServerSlot = SoFSlot; - - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to SoF Corpse Slot IDs for use in Encodes -static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse) { - uint32 SoFCorpse; - // reserved -} -*/ -/* -// Converts SoF Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_OpenNewTasksWindow) { - - AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; - AvailableTaskData1_Struct* __emu_AvailableTaskData1; - AvailableTaskData2_Struct* __emu_AvailableTaskData2; - AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; - - structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; - structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; - structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; - structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; - - // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. - // - in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); - - in->pBuffer = new unsigned char[in->size]; - - unsigned char *__eq_buffer = in->pBuffer; - - __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; - - char *__eq_ptr, *__emu_Ptr; - - // Copy Header - // - // - - __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; - __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; - __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; - - __emu_Ptr = (char *) __emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); - __eq_ptr = (char *) __eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); - - for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in Live packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Title - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Description - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); - - - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; - - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } - - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->level = emu->level[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->gender = emu->gender[r]; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->beard = emu->beard[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; - } - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->tutorial = emu->tutorial[r]; // was u15 - eq2->u15 = 0xff; - eq2->deity = emu->deity[r]; - eq2->zone = emu->zone[r]; - eq2->u19 = 0xFF; - eq2->race = emu->race[r]; - eq2->gohome = emu->gohome[r]; - eq2->class_ = emu->class_[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; - } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); - } - - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); - - //this is SUCH bullshit to be doing from down here. but the - // new client requires us to close immediately following this - // packet, so do it. - //dest->Close(); -} - -//hack hack hack -ENCODE(OP_SendZonepoints) { - ENCODE_LENGTH_ATLEAST(ZonePoints); - - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); - - memcpy(eq, emu, __packet->size); - - FINISH_ENCODE(); -// unknown0xxx[24]; - //this is utter crap... the client is waiting for this - //certain 0 length opcode to come after the reqclientspawn - //stuff... so this is a dirty way to put it in there. - // this needs to be done better - - //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); - //dest->QueuePacket(&hack_test); - -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 4 is for SoF - if (emu->clientver <= 4 ) +// ENCODE methods + ENCODE(OP_Action) { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + eq->sequence = emu->sequence; + OUT(type); + //OUT(damage); + OUT(spell); + eq->level2 = emu->level; + OUT(buff_unknown); // if this is 4, a buff icon is made + //eq->unknown0036 = -1; + //eq->unknown0040 = -1; + //eq->unknown0044 = -1; + + FINISH_ENCODE(); + } + + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToSoFSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); + } + + ENCODE(OP_AltCurrencySell) + { + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + OUT(merchant_entity_id); + eq->slot_id = ServerToSoFSlot(emu->slot_id); + OUT(charges); OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); - eq->type = emu->sof_type; - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); - memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(points); // Relocation Test -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - eq->equipment[r].equip0 = emu->item_material[r]; - eq->equipment[r].equip1 = 0; - eq->equipment[r].itemId = 0; - //eq->colors[r].color = emu->colors[r].color; - } - for(r = 0; r < 7; r++) { - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - //NOTE: new client supports 300 AAs, our internal rep/PP - //only supports 240.. - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(AGI); - OUT(INT); - OUT(DEX); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); -// OUT(unknown4184[128]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } - OUT_array(recastTimers, structs::MAX_RECAST_TYPES); -// OUT(unknown08124[360]); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - //NOTE: new client supports 20 bandoliers, our internal rep - //only supports 4.. - for(r = 0; r < 4; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[12]); - OUT(exp); -// OUT(unknown13072[8]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(y); //reversed x and y - OUT(x); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - //OUT(expansions); - eq->expansions = 16383; -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); -// OUT(unknown14628); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown17892[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; - OUT(RestTimer); -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0xa3,0x02,0x00,0x00,0x95,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F, -0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - - memcpy(eq->unknown12864, bytes, sizeof(bytes)); - - - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown796 = -1; - eq->unknown840 = 600; - eq->unknown876 = 50; - eq->unknown880 = 10; - eq->unknown884 = 1; - eq->unknown885 = 0; - eq->unknown886 = 1; - eq->unknown887 = 0; - eq->unknown888 = 0; - eq->unknown889 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown891 = 0; - eq->unknown892 = 180; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 2; - eq->unknown908 = 2; - - FINISH_ENCODE(); -} - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_ApplyPoison) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToSoFSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); } - in->size = sizeof(structs::Track_Struct) * EntryCount; - in->pBuffer = new unsigned char[in->size]; - structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++eq, ++emu) + ENCODE(OP_BazaarSearch) { + if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { + + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(BazaarSearchResults_Struct); + if (entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + for (int i = 0; iBeginning.Action = emu->Beginning.Action; + eq->Beginning.Unknown001 = emu->Beginning.Unknown001; + eq->Beginning.Unknown002 = emu->Beginning.Unknown002; + eq->NumItems = emu->NumItems; + eq->SerialNumber = emu->SerialNumber; + eq->SellerID = emu->SellerID; + eq->Cost = emu->Cost; + eq->ItemStat = emu->ItemStat; + strcpy(eq->ItemName, emu->ItemName); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BecomeTrader) + { + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); + + OUT(ID); + OUT(Code); + + FINISH_ENCODE(); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); + OUT(entityid); - OUT(padding002); - OUT(distance); + OUT(slot); + OUT(level); + OUT(effect); + //eq->unknown7 = 10; + OUT(spellid); + OUT(duration); + OUT(slotid); + OUT(bufffade); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_CancelTrade) + { + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - dest->FastQueuePacket(&in, ack_req); -} + OUT(fromid); + OUT(action); -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; - - //determine and verify length - int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); - delete in; - return; + FINISH_ENCODE(); } - //make the EQ struct. - in->size = sizeof(structs::Spawn_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); + *p = nullptr; - //do the transform... - int r; - int k; - for(r = 0; r < entrycount; r++, eq++, emu++) { + if (in->size == 0) { + in->size = 4; + in->pBuffer = new uchar[in->size]; + *((uint32 *)in->pBuffer) = 0; - eq->showname = 1; //New Field - Toggles Name Display on or off - 0 = off, 1 = on - eq->linkdead = 0; //New Field - Toggles LD on or off after name - 0 = off, 1 = on - eq->statue = 0; //New Field - 1 freezes animation - eq->showhelm = emu->showhelm; - eq->deity = emu->deity; - eq->drakkin_heritage = emu->drakkin_heritage; - eq->gender = emu->gender; - for(k = 0; k < 9; k++) { - eq->equipment[k].equip0 = emu->equipment[k]; - eq->equipment[k].equip1 = 0; - eq->equipment[k].itemId = 0; - eq->colors[k].color = emu->colors[k].color; + dest->FastQueuePacket(&in, ack_req); + return; } - eq->StandState = emu->StandState; - eq->guildID = emu->guildID; - eq->spelleffect = 0; - eq->spelleffect2 = 0; - eq->spelleffect3 = 0; - eq->spelleffect4 = 0; - eq->spelleffect5 = 0; - eq->spelleffect6 = 0; - eq->class_ = emu->class_; - eq->flymode = emu->flymode; - eq->gm = emu->gm; - eq->helm = emu->helm; - eq->drakkin_tattoo = emu->drakkin_tattoo; - eq->beardcolor = emu->beardcolor; - eq->runspeed = emu->runspeed; - eq->light = emu->light; - eq->level = emu->level; - eq->lfg = emu->lfg; - eq->hairstyle = emu->hairstyle; - eq->haircolor = emu->haircolor; - eq->race = emu->race; - strcpy(eq->suffix, emu->suffix); - eq->findable = emu->findable; - if(emu->bodytype >= 66) + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + uchar *OldBuffer = in->pBuffer; + + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToSoFSlot(emu->from_slot); + eq->to_slot = ServerToSoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteSpawn) + { + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); + + OUT(spawn_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) { - eq->bodytype = 11; //non-targetable - eq->showname = 0; //no visible name - eq->race = 127; //invisible man - eq->gender = 0; //invisible men are gender 0 + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->enabled_max = 1; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (int i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + //ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + ENCODE_LENGTH_EXACT(Object_Struct); + SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); + + OUT(drop_id); + OUT(zone_id); + OUT(zone_instance); + OUT(heading); + OUT(x); + OUT(y); + OUT(z); + OUT_str(object_name); + OUT(object_type); + OUT(spawn_id); + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown020 = 0; + eq->unknown024 = 0; + eq->size = 1; //This forces all objects to standard size for now + eq->unknown088 = 0; + memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); + +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); + + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); + + eq->slot = ServerToSoFSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strcpy(eq->worldshortname, emu->worldshortname); + + OUT(enablevoicemacros); + OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + // These next two need to be set like this for the Tutorial Button to work. + eq->unknown263[0] = 0; + eq->unknown263[2] = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = ServerToSoFCorpseSlot(emu->slot_id); + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToSoFSlot(emu->from_slot); + eq->to_slot = ServerToSoFSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown796 = -1; + eq->unknown840 = 600; + eq->unknown876 = 50; + eq->unknown880 = 10; + eq->unknown884 = 1; + eq->unknown885 = 0; + eq->unknown886 = 1; + eq->unknown887 = 0; + eq->unknown888 = 0; + eq->unknown889 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown891 = 0; + eq->unknown892 = 180; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 2; + eq->unknown908 = 2; + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + OUT_str(Title); + OUT_str(Text); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + + eq->unknown4236 = 0x00000000; + eq->unknown4240 = 0xffffffff; + + FINISH_ENCODE(); + } + + ENCODE(OP_OpenNewTasksWindow) + { + AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; + AvailableTaskData1_Struct* __emu_AvailableTaskData1; + AvailableTaskData2_Struct* __emu_AvailableTaskData2; + AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; + + structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; + structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; + structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; + structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; + + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; + + // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. + // + in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); + + in->pBuffer = new unsigned char[in->size]; + + unsigned char *__eq_buffer = in->pBuffer; + + __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; + + char *__eq_ptr, *__emu_Ptr; + + // Copy Header + // + // + + __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; + __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; + __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; + + __emu_Ptr = (char *)__emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); + __eq_ptr = (char *)__eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); + + for (uint32 i = 0; i<__emu_AvailableTaskHeader->TaskCount; i++) { + + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in Live packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Title + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Description + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PetBuffWindow) + { + ENCODE_LENGTH_EXACT(PetBuff_Struct); + SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); + + OUT(petid); + OUT(buffcount); + + int EQBuffSlot = 0; + + for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) + { + if (emu->spellid[EmuBuffSlot]) + { + eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; + eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); + memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(points); // Relocation Test + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + eq->equipment[r].equip0 = emu->item_material[r]; + eq->equipment[r].equip1 = 0; + eq->equipment[r].itemId = 0; + //eq->colors[r].color = emu->colors[r].color; + } + for (r = 0; r < 7; r++) { + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + //NOTE: new client supports 300 AAs, our internal rep/PP + //only supports 240.. + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(AGI); + OUT(INT); + OUT(DEX); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + // OUT(unknown4184[128]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + // OUT(unknown08124[360]); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + //NOTE: new client supports 20 bandoliers, our internal rep + //only supports 4.. + for (r = 0; r < 4; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[12]); + OUT(exp); + // OUT(unknown13072[8]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(y); //reversed x and y + OUT(x); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + //OUT(expansions); + eq->expansions = 16383; + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + // OUT(unknown14628); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown17892[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + OUT(RestTimer); + // OUT(unknown19584[4]); + // OUT(unknown19588); + + const uint8 bytes[] = { + 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(eq->unknown12864, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_RaidJoin) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; + } + + ENCODE(OP_RaidUpdate) + { + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates + { + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 14 || raid_gen->action == 30) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); } else { - eq->bodytype = emu->bodytype; + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); } - //eq->bodytype2 = 0; - eq->equip_chest2 = emu->equip_chest2; - eq->curHp = emu->curHp; - eq->invis = emu->invis; - strcpy(eq->lastName, emu->lastName); - eq->eyecolor1 = emu->eyecolor1; - strcpy(eq->title, emu->title); - eq->beard = emu->beard; - eq->targetable = 1; //New Field - Toggle Targetable on or off - 0 = off, 1 = on - eq->NPC = emu->NPC; - eq->targetable_with_hotkey = 1;//New Field - Toggle Targetable on or off - 0 = off, 1 = on - eq->x = emu->x; - eq->deltaX = emu->deltaX; - eq->deltaY = emu->deltaY; - eq->z = emu->z; - eq->deltaHeading = emu->deltaHeading; - eq->y = emu->y; - eq->deltaZ = emu->deltaZ; - eq->animation = emu->animation; - eq->heading = emu->heading; - eq->spawnId = emu->spawnId; - eq->nonvisible = 0; - strcpy(eq->name, emu->name); - eq->petOwnerId = emu->petOwnerId; - eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name - for(k = 0; k < 9; k++) { - eq->colors[k].color = emu->colors[k].color; - } - eq->anon = emu->anon; - eq->face = emu->face; - eq->drakkin_details = emu->drakkin_details; - eq->size = emu->size; - eq->walkspeed = emu->walkspeed; - /* - //Uncomment this section to use this hack test with NPC last names - //Hack Test for finding more fields in the Struct: - if (emu->lastName[0] == '*') // Test NPC! + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + eq->invslot = ServerToSoFSlot(emu->invslot); + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 4 is for SoF + if (emu->clientver <= 4) { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->level = emu->level[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->gender = emu->gender[r]; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->beard = emu->beard[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->tutorial = emu->tutorial[r]; // was u15 + eq2->u15 = 0xff; + eq2->deity = emu->deity[r]; + eq2->zone = emu->zone[r]; + eq2->u19 = 0xFF; + eq2->race = emu->race[r]; + eq2->gohome = emu->gohome[r]; + eq2->class_ = emu->class_[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + } + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + //hack hack hack + ENCODE(OP_SendZonepoints) + { + ENCODE_LENGTH_ATLEAST(ZonePoints); + + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, __packet->size); + + memcpy(eq, emu, __packet->size); + + FINISH_ENCODE(); + // unknown0xxx[24]; + //this is utter crap... the client is waiting for this + //certain 0 length opcode to come after the reqclientspawn + //stuff... so this is a dirty way to put it in there. + // this needs to be done better + + //EQApplicationPacket hack_test(OP_PetitionUnCheckout, 0); + //dest->QueuePacket(&hack_test); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToSoFSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strcpy(eq->model_name, emu->model_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + int r; + for (r = 0; r < door_count; r++) { + strcpy(eq[r].name, emu[r].name); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0076 = 0; + eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0078 = 0; + eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 0; + eq[r].unknown0082 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + in->size = sizeof(structs::Track_Struct) * EntryCount; + in->pBuffer = new unsigned char[in->size]; + structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++eq, ++emu) + { + OUT(entityid); + OUT(padding002); + OUT(distance); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToSoFSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (uint32 i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strcpy(vr->items[x].item_name, ivr->items[x].item_name); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = zps->bind_zone_id; + zph->bind_instance_id = zps->bind_instance_id; + strcpy(zph->zone_name, zps->zone_name); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + + //this is SUCH bullshit to be doing from down here. but the + // new client requires us to close immediately following this + // packet, so do it. + //dest->Close(); + } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(Spawn_Struct); + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::Spawn_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + //do the transform... + int r; + int k; + for (r = 0; r < entrycount; r++, eq++, emu++) { + + eq->showname = 1; //New Field - Toggles Name Display on or off - 0 = off, 1 = on + eq->linkdead = 0; //New Field - Toggles LD on or off after name - 0 = off, 1 = on + eq->statue = 0; //New Field - 1 freezes animation + eq->showhelm = emu->showhelm; + eq->deity = emu->deity; + eq->drakkin_heritage = emu->drakkin_heritage; + eq->gender = emu->gender; + for (k = 0; k < 9; k++) { + eq->equipment[k].equip0 = emu->equipment[k]; + eq->equipment[k].equip1 = 0; + eq->equipment[k].itemId = 0; + eq->colors[k].color = emu->colors[k].color; + } + eq->StandState = emu->StandState; + eq->guildID = emu->guildID; + eq->spelleffect = 0; + eq->spelleffect2 = 0; + eq->spelleffect3 = 0; + eq->spelleffect4 = 0; + eq->spelleffect5 = 0; + eq->spelleffect6 = 0; + eq->class_ = emu->class_; + eq->flymode = emu->flymode; + eq->gm = emu->gm; + eq->helm = emu->helm; + eq->drakkin_tattoo = emu->drakkin_tattoo; + eq->beardcolor = emu->beardcolor; + eq->runspeed = emu->runspeed; + eq->light = emu->light; + eq->level = emu->level; + eq->lfg = emu->lfg; + eq->hairstyle = emu->hairstyle; + eq->haircolor = emu->haircolor; + eq->race = emu->race; + strcpy(eq->suffix, emu->suffix); + eq->findable = emu->findable; + if (emu->bodytype >= 66) + { + eq->bodytype = 11; //non-targetable + eq->showname = 0; //no visible name + eq->race = 127; //invisible man + eq->gender = 0; //invisible men are gender 0 + } + else + { + eq->bodytype = emu->bodytype; + } + //eq->bodytype2 = 0; + eq->equip_chest2 = emu->equip_chest2; + eq->curHp = emu->curHp; + eq->invis = emu->invis; + strcpy(eq->lastName, emu->lastName); + eq->eyecolor1 = emu->eyecolor1; + strcpy(eq->title, emu->title); + eq->beard = emu->beard; + eq->targetable = 1; //New Field - Toggle Targetable on or off - 0 = off, 1 = on + eq->NPC = emu->NPC; + eq->targetable_with_hotkey = 1;//New Field - Toggle Targetable on or off - 0 = off, 1 = on + eq->x = emu->x; + eq->deltaX = emu->deltaX; + eq->deltaY = emu->deltaY; + eq->z = emu->z; + eq->deltaHeading = emu->deltaHeading; + eq->y = emu->y; + eq->deltaZ = emu->deltaZ; + eq->animation = emu->animation; + eq->heading = emu->heading; + eq->spawnId = emu->spawnId; + eq->nonvisible = 0; + strcpy(eq->name, emu->name); + eq->petOwnerId = emu->petOwnerId; + eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name + for (k = 0; k < 9; k++) { + eq->colors[k].color = emu->colors[k].color; + } + eq->anon = emu->anon; + eq->face = emu->face; + eq->drakkin_details = emu->drakkin_details; + eq->size = emu->size; + eq->walkspeed = emu->walkspeed; + /* + //Uncomment this section to use this hack test with NPC last names + //Hack Test for finding more fields in the Struct: + if (emu->lastName[0] == '*') // Test NPC! + { char code = emu->lastName[1]; size_t len = strlen(emu->lastName); char* sep = (char*)memchr(&emu->lastName[2], '=', len - 2); @@ -921,29 +1914,29 @@ ENCODE(OP_ZoneSpawns) { uint8 rnd = rand() & 0x0F; if (sep == nullptr) { - ofs = 0; - if ((emu->lastName[2] < '0') || (emu->lastName[2] > '9')) - { - val = rnd; - } - else - { - val = atoi(&emu->lastName[2]); - } + ofs = 0; + if ((emu->lastName[2] < '0') || (emu->lastName[2] > '9')) + { + val = rnd; } else { - sep[0] = nullptr; - ofs = atoi(&emu->lastName[2]); - sep[0] = '='; - if ((sep[1] < '0') || (sep[1] > '9')) - { - val = rnd; - } - else - { - val = atoi(&sep[1]); - } + val = atoi(&emu->lastName[2]); + } + } + else + { + sep[0] = nullptr; + ofs = atoi(&emu->lastName[2]); + sep[0] = '='; + if ((sep[1] < '0') || (sep[1] > '9')) + { + val = rnd; + } + else + { + val = atoi(&sep[1]); + } } char hex[] = "0123456789ABCDEF"; @@ -962,1906 +1955,1042 @@ ENCODE(OP_ZoneSpawns) { switch (code) { - case 'a': - eq->unknown0001[ofs % 4] = val; break; - case 'b': - eq->unknown0008 = val; break; - case 'c': - eq->unknown0011[ofs % 3] = val; break; - case 'd': - eq->unknown0018[ofs % 4] = val; break; - case 'e': - eq->unknown0023[ofs % 4] = val; break; - case 'f': - eq->unknown0136 = val; break; - case 'g': - eq->unknown0166[ofs % 8] = val; break; - case 'h': - eq->unknown0175[ofs % 192] = val; break; - case 'i': - eq->unknown0370[ofs % 3] = val; break; - case 'j': - eq->unknown0374[ofs % 128] = val; break; - case 'k': - eq->unknown0507[ofs % 4] = val; break; - case 'l': - eq->unknown0512[ofs % 16] = val; break; - case 'm': - eq->unknown0529[ofs % 4] = val; break; - case 'n': - eq->unknown0539[ofs % 41] = val; break; - case 'o': - eq->unknown0614[ofs % 11] = val; break; - case 'p': - eq->unknown0626[ofs % 28] = val; break; - case 'q': - eq->unknown0690 = val; break; - case 'r': - eq->unknown0726[ofs % 4] = val; break; - case 's': - eq->unknown0731[ofs % 11] = val; break; - case 't': - eq->unknown0767[ofs % 3] = val; break; - case 'u': - eq->unknown0883[ofs % 4] = val; break; - case 'v': - eq->unknown0895[ofs % 2] = val; break; - case 'X': - ((uint8*)eq)[ofs % 897] = val; break; - case 'Z': - eq->size = (float)val; break; // Test w/ size. + case 'a': + eq->unknown0001[ofs % 4] = val; break; + case 'b': + eq->unknown0008 = val; break; + case 'c': + eq->unknown0011[ofs % 3] = val; break; + case 'd': + eq->unknown0018[ofs % 4] = val; break; + case 'e': + eq->unknown0023[ofs % 4] = val; break; + case 'f': + eq->unknown0136 = val; break; + case 'g': + eq->unknown0166[ofs % 8] = val; break; + case 'h': + eq->unknown0175[ofs % 192] = val; break; + case 'i': + eq->unknown0370[ofs % 3] = val; break; + case 'j': + eq->unknown0374[ofs % 128] = val; break; + case 'k': + eq->unknown0507[ofs % 4] = val; break; + case 'l': + eq->unknown0512[ofs % 16] = val; break; + case 'm': + eq->unknown0529[ofs % 4] = val; break; + case 'n': + eq->unknown0539[ofs % 41] = val; break; + case 'o': + eq->unknown0614[ofs % 11] = val; break; + case 'p': + eq->unknown0626[ofs % 28] = val; break; + case 'q': + eq->unknown0690 = val; break; + case 'r': + eq->unknown0726[ofs % 4] = val; break; + case 's': + eq->unknown0731[ofs % 11] = val; break; + case 't': + eq->unknown0767[ofs % 3] = val; break; + case 'u': + eq->unknown0883[ofs % 4] = val; break; + case 'v': + eq->unknown0895[ofs % 2] = val; break; + case 'X': + ((uint8*)eq)[ofs % 897] = val; break; + case 'Z': + eq->size = (float)val; break; // Test w/ size. } - }*/ - } + }*/ + } + //kill off the emu structure and send the eq packet. + delete[] __emu_buffer; - //kill off the emu structure and send the eq packet. - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending zone spawns"); - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; + //_log(NET__ERROR, "Sending zone spawns"); + //_hex(NET__ERROR, in->pBuffer, in->size); dest->FastQueuePacket(&in, ack_req); - - return; } - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); - - delete in; - - return; - } - - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strcpy(eq[r].name, emu[r].name); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0076 = 0; - eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0078 = 0; - eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 0; - eq[r].unknown0082 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) { - ENCODE_LENGTH_EXACT(Object_Struct); - SETUP_DIRECT_ENCODE(Object_Struct, structs::Object_Struct); - OUT(drop_id); - OUT(zone_id); - OUT(zone_instance); - OUT(heading); - OUT(x); - OUT(y); - OUT(z); - OUT_str(object_name); - OUT(object_type); - OUT(spawn_id); - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown020 = 0; - eq->unknown024 = 0; - eq->size = 1; //This forces all objects to standard size for now - eq->unknown088 = 0; - memset(eq->unknown096, 0xFF, sizeof(eq->unknown096)); - FINISH_ENCODE(); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - - FINISH_ENCODE(); -} - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strcpy(eq->worldshortname, emu->worldshortname); - - OUT(enablevoicemacros); - OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - // These next two need to be set like this for the Tutorial Button to work. - eq->unknown263[0] = 0; - eq->unknown263[2] = 1; - - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1; - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - OUT(entityid); - OUT(slot); - OUT(level); - OUT(effect); - //eq->unknown7 = 10; - OUT(spellid); - OUT(duration); - OUT(slotid); - OUT(bufffade); - FINISH_ENCODE(); -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToSoFSlot(emu->itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToSoFSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToSoFSlot(emu->from_slot); - eq->to_slot = ServerToSoFSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToSoFSlot(emu->from_slot); - eq->to_slot = ServerToSoFSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToSoFSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_BazaarSearch) { - - if(((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { - - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - //determine and verify length - int entrycount = in->size / sizeof(BazaarSearchResults_Struct); - if(entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; - } - - //make the EQ struct. - in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - for(int i=0; iBeginning.Action = emu->Beginning.Action; - eq->Beginning.Unknown001 = emu->Beginning.Unknown001; - eq->Beginning.Unknown002 = emu->Beginning.Unknown002; - eq->NumItems = emu->NumItems; - eq->SerialNumber = emu->SerialNumber; - eq->SellerID = emu->SellerID; - eq->Cost = emu->Cost; - eq->ItemStat = emu->ItemStat; - strcpy(eq->ItemName, emu->ItemName); - } - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); - - -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - ENCODE_FORWARD(OP_TraderBuy); -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToSoFSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 175; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strcpy(eq->model_name, emu->model_name); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = ServerToSoFSlot(emu->invslot); - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = zps->bind_zone_id; - zph->bind_instance_id = zps->bind_instance_id; - strcpy(zph->zone_name, zps->zone_name); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToSoFSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + IN(npcid); + emu->slot = SoFToServerSlot(eq->slot); + IN(charges); + IN(sell_price); - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); + FINISH_DIRECT_DECODE(); } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); + DECODE(OP_AltCurrencySell) + { + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = SoFToServerSlot(eq->slot_id); + IN(charges); + IN(cost); + + FINISH_DIRECT_DECODE(); } - delete[] __emu_buffer; -} -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(uint32 i = 0; i < count; ++i) + DECODE(OP_AltCurrencySellSelection) { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) + IN(merchant_entity_id); + emu->slot_id = SoFToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = SoFToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentInfo) + { + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = SoFToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Buff) + { + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); + + IN(entityid); + IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = SoFToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(hairstyle); + IN(gender); + IN(race); + IN(start_zone); + IN(haircolor); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(tutorial); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = SoFToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = SoFToServerSlot(eq->from_slot); + emu->to_slot = SoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + IN(icon); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemVerifyRequest) + { + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = SoFToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = SoFToServerCorpseSlot(eq->slot_id); + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = SoFToServerSlot(eq->from_slot); + emu->to_slot = SoFToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_RaidInvite) + { + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); + + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } + } + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + emu->invslot = SoFToServerSlot(eq->invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Save) + { + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = SoFToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = SoFToServerSlot(eq->container_slot); + IN(guildtribute_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = SoFToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(unknown06); + IN(elite_material); + IN(color.color); + IN(wear_slot_id); + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; + + static inline int32 GetNextItemInstSerialNumber() + { + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; + else + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; + } + + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); + SoF::structs::ItemSerializationHeader hdr; + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + int32 slot_id = ServerToSoFSlot(slot_id_in); + + hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(SoF::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) { - vr->items[x].item_id = ivr->items[x].item_id; - strcpy(vr->items[x].item_name, ivr->items[x].item_name); - vr->items[x].charges = ivr->items[x].charges; + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); - } - - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_DeleteSpawn) { - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->enabled_max = 1; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(int i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - //ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_BecomeTrader) -{ - ENCODE_LENGTH_EXACT(BecomeTrader_Struct); - SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - OUT(ID); - OUT(Code); - FINISH_ENCODE(); -} - -ENCODE(OP_PetBuffWindow) -{ - ENCODE_LENGTH_EXACT(PetBuff_Struct); - SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); - - OUT(petid); - OUT(buffcount); - - int EQBuffSlot = 0; - - for(uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) - { - if(emu->spellid[EmuBuffSlot]) + else { - eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; - eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + ss.write((const char*)&null_term, sizeof(uint8)); } - } - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - OUT_str(Title); - OUT_str(Text); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - - eq->unknown4236 = 0x00000000; - eq->unknown4240 = 0xffffffff; - - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToSoFSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = SoFToServerSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = SoFToServerSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = SoFToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = SoFToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - IN(slot); - IN(spell_id); - emu->inventoryslot = SoFToServerSlot(eq->inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = SoFToServerSlot(eq->from_slot); - emu->to_slot = SoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); - - emu->from_slot = SoFToServerSlot(eq->from_slot); - emu->to_slot = SoFToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(hairstyle); - IN(gender); - IN(race); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct); - IN(entityid); - IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = SoFToServerSlot(eq->itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(unknown06); - IN(elite_material); - IN(color.color); - IN(wear_slot_id); - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) { - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = SoFToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = SoFToServerSlot(eq->invslot); - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - emu->container_slot = SoFToServerSlot(eq->container_slot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = SoFToServerSlot(eq->container_slot); - emu->augment_slot = eq->augment_slot; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - SoF::structs::ItemSerializationHeader hdr; - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - int32 slot_id = ServerToSoFSlot(slot_id_in); - - hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(SoF::structs::ItemSerializationHeader)); - - if(strlen(item->Name) > 0) - { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - if(strlen(item->Lore) > 0) - { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - if(strlen(item->IDFile) > 0) - { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoF::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(SoF::structs::ItemBodyStruct)); - - uint32 adjusted_slots = item->Slots; - - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) - { - // Do nothing - } - else - { - if(item->Slots & (1 << 21)) // Ammo Slot from Database + if (strlen(item->Lore) > 0) { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); } - - if(item->Slots & (1 << 22)) // Power Source Slot from Database + else { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF + ss.write((const char*)&null_term, sizeof(uint8)); } - } - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; - - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.unknown6 = 0; - ibs.SkillModType = item->SkillModType; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - ibs.RecLevel = item->RecLevel; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.SellRate = item->SellRate; - - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; - - ss.write((const char*)&ibs, sizeof(SoF::structs::ItemBodyStruct)); - - //charm text - if(strlen(item->CharmFile) > 0) - { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoF::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(SoF::structs::ItemSecondaryBodyStruct)); - - isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; - - for(int x = 0; x < 5; ++x) - { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; - } - - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; - - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; - - isbs.book = item->Book; - isbs.booktype = item->BookType; - - ss.write((const char*)&isbs, sizeof(SoF::structs::ItemSecondaryBodyStruct)); - - if(strlen(item->Filename) > 0) - { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - SoF::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(SoF::structs::ItemTertiaryBodyStruct)); - - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.no_pet = item->NoPet; - - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; - - ss.write((const char*)&itbs, sizeof(SoF::structs::ItemTertiaryBodyStruct)); - - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; - - SoF::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(SoF::structs::ClickEffectStruct)); - - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; - - ss.write((const char*)&ices, sizeof(SoF::structs::ClickEffectStruct)); - - if(strlen(item->ClickName) > 0) - { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - - SoF::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(SoF::structs::ProcEffectStruct)); - - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; - - ss.write((const char*)&ipes, sizeof(SoF::structs::ProcEffectStruct)); - - if(strlen(item->ProcName) > 0) - { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - - SoF::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(SoF::structs::WornEffectStruct)); - - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; - - ss.write((const char*)&iwes, sizeof(SoF::structs::WornEffectStruct)); - - if(strlen(item->WornName) > 0) - { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoF::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(SoF::structs::WornEffectStruct)); - - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; - - ss.write((const char*)&ifes, sizeof(SoF::structs::WornEffectStruct)); - - if(strlen(item->FocusName) > 0) - { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - SoF::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(SoF::structs::WornEffectStruct)); - - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; - - ss.write((const char*)&ises, sizeof(SoF::structs::WornEffectStruct)); - - if(strlen(item->ScrollName) > 0) - { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects - - SoF::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); - - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.unknown15 = 0xffffffff; - - iqbs.Purity = item->Purity; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - //iqbs.clairvoyance = item->Clairvoyance; - - iqbs.subitem_count = 0; - - char *SubSerializations[10]; // - - uint32 SubLengths[10]; - - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - - SubSerializations[x] = nullptr; - - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - - if(subitem) { - - int SubSlotNumber; - - iqbs.subitem_count++; - - if(slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if(slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if(slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? - - /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); - */ - - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); } - } - - ss.write((const char*)&iqbs, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); - - for(int x = 0; x < 10; ++x) { - - if(SubSerializations[x]) { - - ss.write((const char*)&x, sizeof(uint32)); - - ss.write(SubSerializations[x], SubLengths[x]); - - safe_delete_array(SubSerializations[x]); + else + { + ss.write((const char*)&null_term, sizeof(uint8)); } + + SoF::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(SoF::structs::ItemBodyStruct)); + + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; + + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.unknown6 = 0; + ibs.SkillModType = item->SkillModType; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + ibs.RecLevel = item->RecLevel; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.SellRate = item->SellRate; + + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; + + ss.write((const char*)&ibs, sizeof(SoF::structs::ItemBodyStruct)); + + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoF::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(SoF::structs::ItemSecondaryBodyStruct)); + + isbs.augtype = item->AugType; + isbs.augrestrict = item->AugRestrict; + + for (int x = 0; x < 5; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } + + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; + + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; + + isbs.book = item->Book; + isbs.booktype = item->BookType; + + ss.write((const char*)&isbs, sizeof(SoF::structs::ItemSecondaryBodyStruct)); + + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + SoF::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(SoF::structs::ItemTertiaryBodyStruct)); + + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.no_pet = item->NoPet; + + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; + + ss.write((const char*)&itbs, sizeof(SoF::structs::ItemTertiaryBodyStruct)); + + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; + + SoF::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(SoF::structs::ClickEffectStruct)); + + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; + + ss.write((const char*)&ices, sizeof(SoF::structs::ClickEffectStruct)); + + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + + SoF::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(SoF::structs::ProcEffectStruct)); + + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; + + ss.write((const char*)&ipes, sizeof(SoF::structs::ProcEffectStruct)); + + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + + SoF::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(SoF::structs::WornEffectStruct)); + + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; + + ss.write((const char*)&iwes, sizeof(SoF::structs::WornEffectStruct)); + + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoF::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(SoF::structs::WornEffectStruct)); + + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; + + ss.write((const char*)&ifes, sizeof(SoF::structs::WornEffectStruct)); + + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + SoF::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(SoF::structs::WornEffectStruct)); + + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; + + ss.write((const char*)&ises, sizeof(SoF::structs::WornEffectStruct)); + + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects + + SoF::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); + + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.unknown15 = 0xffffffff; + + iqbs.Purity = item->Purity; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + //iqbs.clairvoyance = item->Clairvoyance; + + iqbs.subitem_count = 0; + + char *SubSerializations[10]; // + + uint32 SubLengths[10]; + + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + + SubSerializations[x] = nullptr; + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + + if (subitem) { + + int SubSlotNumber; + + iqbs.subitem_count++; + + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? + + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ + + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + } + } + + ss.write((const char*)&iqbs, sizeof(SoF::structs::ItemQuaternaryBodyStruct)); + + for (int x = 0; x < 10; ++x) { + + if (SubSerializations[x]) { + ss.write((const char*)&x, sizeof(uint32)); + ss.write(SubSerializations[x], SubLengths[x]); + + safe_delete_array(SubSerializations[x]); + } + } + + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + + *length = ss.tellp(); + return item_serial; } - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); + static inline uint32 ServerToSoFSlot(uint32 ServerSlot) + { + uint32 SoFSlot = 0; - *length = ss.tellp(); - return item_serial; + if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + SoFSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) + SoFSlot = ServerSlot + 11; + else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) + SoFSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) + SoFSlot = ServerSlot + 1; + else if (ServerSlot == MainPowerSource) + SoFSlot = slots::MainPowerSource; + else + SoFSlot = ServerSlot; + + return SoFSlot; + } + + static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse) + { + //uint32 SoFCorpse; + return (ServerCorpse + 1); + } + + static inline uint32 SoFToServerSlot(uint32 SoFSlot) + { + uint32 ServerSlot = 0; + + if (SoFSlot >= slots::MainAmmo && SoFSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots + ServerSlot = SoFSlot - 1; + else if (SoFSlot >= consts::GENERAL_BAGS_BEGIN && SoFSlot <= consts::CURSOR_BAG_END) + ServerSlot = SoFSlot - 11; + else if (SoFSlot >= consts::BANK_BAGS_BEGIN && SoFSlot <= consts::BANK_BAGS_END) + ServerSlot = SoFSlot - 1; + else if (SoFSlot >= consts::SHARED_BANK_BAGS_BEGIN && SoFSlot <= consts::SHARED_BANK_BAGS_END) + ServerSlot = SoFSlot - 1; + else if (SoFSlot == slots::MainPowerSource) + ServerSlot = MainPowerSource; + else + ServerSlot = SoFSlot; + + return ServerSlot; + } + + static inline uint32 SoFToServerCorpseSlot(uint32 SoFCorpse) + { + //uint32 ServerCorpse; + return (SoFCorpse - 1); + } } - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoFToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = SoFToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - - -} //end namespace SoF - - - - +// end namespace SoF diff --git a/common/patches/sof_constants.h b/common/patches/sof_constants.h index 0ae0d728d..0b959ef3a 100644 --- a/common/patches/sof_constants.h +++ b/common/patches/sof_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef SoF_CONSTANTS_H_ #define SoF_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace SoF { namespace maps { @@ -181,6 +181,7 @@ namespace SoF { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/sof_opcode_list.h b/common/patches/sof_opcode_list.h index 28a1ce650..15236862f 100644 --- a/common/patches/sof_opcode_list.h +++ b/common/patches/sof_opcode_list.h @@ -187,7 +187,7 @@ 0x1ee9, 0x7f5d, OP_CastSpell 0x0659, OP_ManaChange -0x3bc7, OP_BuffFadeMsg +0x3bc7, OP_ColoredText 0x3209, 0x6a93, OP_MemorizeSpell 0x1237, diff --git a/common/patches/sof_ops.h b/common/patches/sof_ops.h index ac8dd2008..f039c2189 100644 --- a/common/patches/sof_ops.h +++ b/common/patches/sof_ops.h @@ -1,100 +1,98 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_Track) -E(OP_DeleteSpawn) +E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_BazaarSearch) +E(OP_BecomeTrader) +E(OP_Buff) +E(OP_CancelTrade) +E(OP_CharInventory) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DeleteSpawn) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_BecomeTrader) -E(OP_PetBuffWindow) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) +E(OP_GroundSpawn) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) E(OP_OnLevelMessage) -E(OP_AltCurrencySell) +E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendZonepoints) +E(OP_ShopPlayerSell) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) E(OP_WearChange) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_DeleteItem) +D(OP_FaceChange) +D(OP_FindPersonRequest) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) D(OP_InspectRequest) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerSell) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) D(OP_WearChange) -D(OP_ApplyPoison) -D(OP_DeleteItem) -D(OP_AugmentItem) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/sof_structs.h b/common/patches/sof_structs.h index a31d60ef8..a101d3cba 100644 --- a/common/patches/sof_structs.h +++ b/common/patches/sof_structs.h @@ -497,7 +497,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -689,14 +689,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -1445,11 +1493,11 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; /*0016*/ }; @@ -1479,17 +1527,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; @@ -2953,10 +3001,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -2992,7 +3040,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3425,6 +3473,21 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name @@ -3556,7 +3619,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's @@ -3963,9 +4026,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index c79fa5d4f..0366e73d8 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "titanium.h" #include "../opcodemgr.h" @@ -13,1491 +12,1756 @@ #include "titanium_structs.h" #include -namespace Titanium { +namespace Titanium +{ + static const char *name = "Titanium"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "Titanium"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth); -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline int16 ServerToTitaniumSlot(uint32 ServerSlot); + static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot); + static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "titanium_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "titanium_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientTitanium; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientTitanium; + } #include "ss_define.h" +// ENCODE methods + EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ; -/* -// Converts Server Slot IDs to Titanium Slot IDs for use in Encodes -static inline uint32 ServerToTitaniumSlot(uint32 ServerSlot) { - uint32 TitaniumSlot; - // reserved -} -*/ -/* -// Converts Titanium Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 TitaniumToServerSlot(uint32 TitaniumSlot) { - uint32 ServerSlot; - // reserved -} -*/ -/* -// Converts Server Corpse Slot IDs to Titanium Corpse Slot IDs for use in Encodes -static inline uint32 ServerToTitaniumCorpseSlot(uint32 ServerCorpse) { - uint32 TitaniumCorpse; - // reserved -} -*/ -/* -// Converts Titanium Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 TitaniumToServerCorpseSlot(uint32 TitaniumCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ -/* -static inline uint32 RemovePowerSourceBit(uint32 slots) { // shouldn't need to add one..just grab the actual server reference, if so... - static const uint32 BIT21 = 1 << 21; - static const uint32 BIT22 = 1 << 22; - static const uint32 KEEPBITS = ~(BIT21 | BIT22); - - bool wearammo = slots & BIT22; - - slots &= KEEPBITS; - - if (wearammo) - slots |= BIT21; - - return slots; -} -*/ - - -EAT_ENCODE(OP_ZoneServerReady) -EAT_ENCODE(OP_GuildMemberLevelUpdate) - -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); - int r; - for(r = 0; r < 10; r++) { - OUT(zone[r]); - OUT(eyecolor1[r]); - OUT(eyecolor2[r]); - OUT(hairstyle[r]); - OUT(primary[r]); - if(emu->race[r] > 473) - eq->race[r] = 1; - else - eq->race[r] = emu->race[r]; - OUT(class_[r]); - OUT_str(name[r]); - OUT(gender[r]); - OUT(level[r]); - OUT(secondary[r]); - OUT(face[r]); - OUT(beard[r]); - int k; - for(k = 0; k < 9; k++) { - OUT(equip[r][k]); - OUT(cs_colors[r][k].color); - } - OUT(haircolor[r]); - OUT(gohome[r]); - OUT(tutorial[r]); - OUT(deity[r]); - OUT(beardcolor[r]); - eq->unknown820[r] = 0xFF; - eq->unknown902[r] = 0xFF; - } - FINISH_ENCODE(); -} - - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for Titanium - // clientver 1 is for all clients and 3 is for Titanium - if (emu->clientver <= 3 ) + EAT_ENCODE(OP_ZoneServerReady); // added ; + + ENCODE(OP_Action) { - OUT(id); - eq->unknown004 = 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - eq->title_sid = emu->id - emu->current_level + 1; - eq->desc_sid = emu->id - emu->current_level + 1; - OUT(class_type); - OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); + + OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + OUT(sequence); OUT(type); - OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - OUT(unknown80[0]); - OUT(unknown80[1]); - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + //OUT(damage); + OUT(spell); + OUT(buff_unknown); // if this is 4, a buff icon is made + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); - memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - OUT(item_material[r]); - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - for(r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(points); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(DEX); - OUT(INT); - OUT(AGI); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); -// OUT(unknown4184[448]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - for(r = 0; r < structs::BUFF_COUNT; r++) { - OUT(buffs[r].slotid); - OUT(buffs[r].level); - OUT(buffs[r].bard_modifier); - OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - OUT(buffs[r].player_id); - } - for(r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } -// OUT(unknown05008[360]); -// OUT_array(recastTimers, structs::MAX_RECAST_TYPES); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[8]); - OUT(exp); -// OUT(unknown13072[12]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(x); - OUT(y); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - OUT(expansions); -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown14932[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0x78,0x03,0x00,0x00,0x1A,0x04,0x00,0x00,0x1A,0x04,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F,0x09,0x00,0x00,0x00, -0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x14 -}; - memcpy(eq->unknown12864, bytes, sizeof(bytes)); - - - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_Track) -{ - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + ENCODE(OP_AdventureMerchantSell) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToTitaniumSlot(emu->slot); + OUT(charges); + OUT(sell_price); + + FINISH_ENCODE(); } - in->size = sizeof(structs::Track_Struct) * EntryCount; - in->pBuffer = new unsigned char[in->size]; - structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++eq, ++emu) + ENCODE(OP_ApplyPoison) { - OUT(entityid); - OUT(padding002); - OUT(distance); + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToTitaniumSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_BazaarSearch) + { + if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; - - //determine and verify length - int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); - delete in; - return; - } - - //make the EQ struct. - in->size = sizeof(structs::Spawn_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - //do the transform... - int r; - int k; - for(r = 0; r < entrycount; r++, eq++, emu++) { -// eq->unknown0000 = emu->unknown0000; - eq->gm = emu->gm; -// eq->unknown0003 = emu->unknown0003; - eq->aaitle = emu->aaitle; -// eq->unknown0004 = emu->unknown0004; - eq->anon = emu->anon; - eq->face = emu->face; - strcpy(eq->name, emu->name); - eq->deity = emu->deity; -// eq->unknown0073 = emu->unknown0073; - eq->size = emu->size; -// eq->unknown0079 = emu->unknown0079; - eq->NPC = emu->NPC; - eq->invis = emu->invis; - eq->haircolor = emu->haircolor; - eq->curHp = emu->curHp; - eq->max_hp = emu->max_hp; - eq->findable = emu->findable; -// eq->unknown0089[5] = emu->unknown0089[5]; - eq->deltaHeading = emu->deltaHeading; - eq->x = emu->x; -// eq->padding0054 = emu->padding0054; - eq->y = emu->y; - eq->animation = emu->animation; -// eq->padding0058 = emu->padding0058; - eq->z = emu->z; - eq->deltaY = emu->deltaY; - eq->deltaX = emu->deltaX; - eq->heading = emu->heading; -// eq->padding0066 = emu->padding0066; - eq->deltaZ = emu->deltaZ; -// eq->padding0070 = emu->padding0070; - eq->eyecolor1 = emu->eyecolor1; -// eq->unknown0115[24] = emu->unknown0115[24]; - eq->showhelm = emu->showhelm; -// eq->unknown0140[4] = emu->unknown0140[4]; - eq->is_npc = emu->is_npc; - eq->hairstyle = emu->hairstyle; - - //if(emu->gender == 1){ - // eq->hairstyle = eq->hairstyle == 0xFF ? 0 : eq->hairstyle; - //} - - eq->beardcolor = emu->beardcolor; -// eq->unknown0147[4] = emu->unknown0147[4]; - eq->level = emu->level; -// eq->unknown0259[4] = emu->unknown0259[4]; - eq->beard = emu->beard; - strcpy(eq->suffix, emu->suffix); - eq->petOwnerId = emu->petOwnerId; - eq->guildrank = emu->guildrank; -// eq->unknown0194[3] = emu->unknown0194[3]; - for(k = 0; k < 9; k++) { - eq->equipment[k] = emu->equipment[k]; - eq->colors[k].color = emu->colors[k].color; - } - for(k = 0; k < 8; k++) { - eq->set_to_0xFF[k] = 0xFF; + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; } - eq->runspeed = emu->runspeed; - eq->afk = emu->afk; - eq->guildID = emu->guildID; - strcpy(eq->title, emu->title); -// eq->unknown0274 = emu->unknown0274; - eq->helm = emu->helm; - if(emu->race > 473) - eq->race = 1; - else - eq->race = emu->race; -// eq->unknown0288 = emu->unknown0288; - strcpy(eq->lastName, emu->lastName); - eq->walkspeed = emu->walkspeed; -// eq->unknown0328 = emu->unknown0328; - eq->is_pet = emu->is_pet; - eq->light = emu->light; - eq->class_ = emu->class_; - eq->eyecolor2 = emu->eyecolor2; -// eq->unknown0333 = emu->unknown0333; - eq->flymode = emu->flymode; - eq->gender = emu->gender; - eq->bodytype = emu->bodytype; -// eq->unknown0336[3] = emu->unknown0336[3]; - eq->equip_chest2 = emu->equip_chest2; - eq->spawnId = emu->spawnId; -// eq->unknown0344[4] = emu->unknown0344[4]; - eq->lfg = emu->lfg; - - /* - if (emu->face == 99) {eq->face = 0;} - if (emu->eyecolor1 == 99) {eq->eyecolor1 = 0;} - if (emu->eyecolor2 == 99) {eq->eyecolor2 = 0;} - if (emu->hairstyle == 99) {eq->hairstyle = 0;} - if (emu->haircolor == 99) {eq->haircolor = 0;} - if (emu->beard == 99) {eq->beard = 0;} - if (emu->beardcolor == 99) {eq->beardcolor = 0;} - */ - - } - - - //kill off the emu structure and send the eq packet. - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+5; // ItemPacketType + Serialization + \0 - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length+1); - - delete[] __emu_buffer; - safe_delete_array(serialized); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int itemcount = in->size / sizeof(InternalSerializedItem_Struct); - if(itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); - delete in; - return; - } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - //do the transform... - int r; - std::string serial_string; - for(r = 0; r < itemcount; r++, eq++) { - uint32 length; - char *serialized=SerializeItem((const ItemInst*)eq->inst,eq->slot_id,&length,0); - if (serialized) { - serial_string.append(serialized,length+1); - safe_delete_array(serialized); - } else { - _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - - } - - in->size = serial_string.length(); - in->pBuffer = new unsigned char[in->size]; - memcpy(in->pBuffer,serial_string.c_str(),serial_string.length()); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_BazaarSearch) { - - if(((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) { - + //consume the packet EQApplicationPacket *in = *p; *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(BazaarSearchResults_Struct); + if (entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) in the loop. + memset(in->pBuffer, 0, in->size); + + for (int i = 0; i < entrycount; i++, eq++, emu++) { + eq->Beginning.Action = emu->Beginning.Action; + eq->Beginning.Unknown001 = emu->Beginning.Unknown001; + eq->Beginning.Unknown002 = emu->Beginning.Unknown002; + eq->NumItems = emu->NumItems; + eq->SerialNumber = emu->SerialNumber; + eq->SellerID = emu->SellerID; + eq->Cost = emu->Cost; + eq->ItemStat = emu->ItemStat; + strcpy(eq->ItemName, emu->ItemName); + } + + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - return; } - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; + ENCODE(OP_BecomeTrader) + { + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; + OUT(ID); + OUT(Code); - //determine and verify length - int entrycount = in->size / sizeof(BazaarSearchResults_Struct); - if(entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; + FINISH_ENCODE(); } - //make the EQ struct. - in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount; - in->pBuffer = new unsigned char[in->size]; - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer; - - //zero out the packet. We could avoid this memset by setting all fields (including unknowns) - //in the loop. - memset(in->pBuffer, 0, in->size); - - for(int i=0; iBeginning.Action = emu->Beginning.Action; - eq->Beginning.Unknown001 = emu->Beginning.Unknown001; - eq->Beginning.Unknown002 = emu->Beginning.Unknown002; - eq->NumItems = emu->NumItems; - eq->SerialNumber = emu->SerialNumber; - eq->SellerID = emu->SellerID; - eq->Cost = emu->Cost; - eq->ItemStat = emu->ItemStat; - strcpy(eq->ItemName, emu->ItemName); - } - - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); - - -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { + ENCODE(OP_CharInventory) + { + //consume the packet EQApplicationPacket *in = *p; *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int itemcount = in->size / sizeof(InternalSerializedItem_Struct); + if (itemcount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + delete in; + return; + } + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + //do the transform... + int r; + std::string serial_string; + for (r = 0; r < itemcount; r++, eq++) { + uint32 length; + char *serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &length, 0); + if (serialized) { + serial_string.append(serialized, length + 1); + safe_delete_array(serialized); + } + else { + _log(NET__STRUCTS, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + + } + + in->size = serial_string.length(); + in->pBuffer = new unsigned char[in->size]; + memcpy(in->pBuffer, serial_string.c_str(), serial_string.length()); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); - return; } - ENCODE_FORWARD(OP_TraderBuy); -} -ENCODE(OP_TraderBuy) { + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); + eq->from_slot = ServerToTitaniumSlot(emu->from_slot); + eq->to_slot = ServerToTitaniumSlot(emu->to_slot); + OUT(number_in_stack); - FINISH_ENCODE(); -} + FINISH_ENCODE(); + } -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; + ENCODE(OP_DeleteSpawn) + { + SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; + OUT(spawn_id); + FINISH_ENCODE(); + } + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + + OUT(count); - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } - uint8 *buffer; - buffer = in->pBuffer; + FINISH_ENCODE(); + } - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); + OUT(minutes_remaining); - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->enabled_max = 1; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + //ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_GuildMemberList) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data ); - const char *emu_note = (emu_name + + const char *emu_note = (emu_name + emu->name_length + //skip name contents emu->count //skip string terminators ); - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { - //the order we set things here must match the struct + //the order we set things here must match the struct -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ #define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); #undef SlideStructString #undef PutFieldN - e++; + e++; + } } - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ReadBook) { - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; - - in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); - - in->pBuffer = new unsigned char[in->size]; - - structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; - - eq_BookText_Struct->window = emu_BookText_Struct->window; - eq_BookText_Struct->type = emu_BookText_Struct->type; - strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - if(emu->race > 473){ - eq->race = 1; - } - else { - OUT(race); - } - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - /* - //Test code for identifying the structure - uint8 ofs; - uint8 val; - ofs = emu->texture; - val = emu->face; - ((uint8*)eq)[ofs % 168] = val; - */ - FINISH_ENCODE(); -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(uint32 i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_id = ivr->claim_id; - vr->item.item_id = ivr->items[0].item_id; - strcpy(vr->item.item_name, ivr->items[0].item_name); - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); - } - - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_InspectAnswer) { - ENCODE_LENGTH_EXACT(InspectResponse_Struct); - SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); - - OUT(TargetID); - OUT(playerid); - - int r; - for (r = 0; r <= 20; r++) { - strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); - } - - // move arrow item down to last element in titanium array - strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); - - int k; - for (k = 0; k <= 20; k++) { - OUT(itemicons[k]); - } - - // move arrow icon down to last element in titanium array - eq->itemicons[21] = emu->itemicons[22]; - - strn0cpy(eq->text, emu->text, sizeof(eq->text)); - - FINISH_ENCODE(); -} - -ENCODE(OP_RespondAA) { - ENCODE_LENGTH_EXACT(AATable_Struct); - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - unsigned int r; - for(r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { - OUT(aa_list[r].aa_skill); - OUT(aa_list[r].aa_value); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteSpawn) { - SETUP_DIRECT_ENCODE(DeleteSpawn_Struct, structs::DeleteSpawn_Struct); - OUT(spawn_id); - FINISH_ENCODE(); -} - -ENCODE(OP_WearChange) { - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->enabled_max = 1; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - //ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::Action_Struct); - OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - OUT(sequence); - OUT(type); - //OUT(damage); - OUT(spell); - OUT(buff_unknown); // if this is 4, a buff icon is made - FINISH_ENCODE(); -} - -ENCODE(OP_BecomeTrader) -{ - ENCODE_LENGTH_EXACT(BecomeTrader_Struct); - SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); - OUT(ID); - OUT(Code); - FINISH_ENCODE(); -} - -ENCODE(OP_PetBuffWindow) -{ - ENCODE_LENGTH_EXACT(PetBuff_Struct); - SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); - - OUT(petid); - OUT(buffcount); - - int EQBuffSlot = 0; - - for(uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) - { - if(emu->spellid[EmuBuffSlot]) - { - eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; - eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; - } - } - - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - OUT_str(Title); - OUT_str(Text); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - - eq->unknown4236 = 0x00000000; - eq->unknown4240 = 0xffffffff; - - FINISH_ENCODE(); -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_InspectAnswer) { - DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); - SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); - - IN(TargetID); - IN(playerid); - - int r; - for (r = 0; r <= 20; r++) { - strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); - } - - // move arrow item up to last element in server array - strn0cpy(emu->itemnames[21], "", sizeof(emu->itemnames[21])); - strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); - - int k; - for (k = 0; k <= 20; k++) { - IN(itemicons[k]); - } - - // move arrow icon up to last element in server array - emu->itemicons[21] = 0xFFFFFFFF; - emu->itemicons[22] = eq->itemicons[21]; - - strn0cpy(emu->text, eq->text, sizeof(emu->text)); - - FINISH_DIRECT_DECODE(); -} - -ENCODE(OP_LFGuild) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - uint32 Command = in->ReadUInt32(); - - if(Command != 0) - { + delete[] __emu_buffer; dest->FastQueuePacket(&in, ack_req); - return; } - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(structs::LFGuild_PlayerToggle_Struct)); + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - memcpy(outapp->pBuffer, in->pBuffer, sizeof(structs::LFGuild_PlayerToggle_Struct)); + OUT(spawnid); + OUT_str(charname); - dest->FastQueuePacket(&outapp, ack_req); + if (emu->race > 473) + eq->race = 1; + else + OUT(race); - delete in; -} + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + /* + //Test code for identifying the structure + uint8 ofs; + uint8 val; + ofs = emu->texture; + val = emu->face; + ((uint8*)eq)[ofs % 168] = val; + */ -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(color.color); - IN(wear_slot_id); - emu->unknown06 = 0; - emu->elite_material = 0; - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) { - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); + FINISH_ENCODE(); } - IN(link_hash); - FINISH_DIRECT_DECODE(); -} + ENCODE(OP_InspectAnswer) + { + ENCODE_LENGTH_EXACT(InspectResponse_Struct); + SETUP_DIRECT_ENCODE(InspectResponse_Struct, structs::InspectResponse_Struct); -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} + OUT(TargetID); + OUT(playerid); - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(haircolor); - IN(gender); - IN(race); - IN(start_zone); - IN(hairstyle); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - emu->type = 3; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(window); - IN(type); - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LFGuild) -{ - uint32 Command = __packet->ReadUInt32(); - - if(Command != 0) - return; - - SETUP_DIRECT_DECODE(LFGuild_PlayerToggle_Struct, structs::LFGuild_PlayerToggle_Struct); - memcpy(emu, eq, sizeof(structs::LFGuild_PlayerToggle_Struct)); - memset(emu->Unknown612, 0, sizeof(emu->Unknown612)); - - FINISH_DIRECT_DECODE(); -} - -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth) { - char *serialization = nullptr; - char *instance = nullptr; - const char *protection=(const char *)"\\\\\\\\\\"; - char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; - bool stackable=inst->IsStackable(); - uint32 merchant_slot=inst->GetMerchantSlot(); - int16 charges=inst->GetCharges(); - const Item_Struct *item=inst->GetItem(); - int i; - uint32 sub_length; - - MakeAnyLenString(&instance, - "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", - stackable ? charges : 0, - 0, - (merchant_slot==0) ? slot_id : merchant_slot, - inst->GetPrice(), - (merchant_slot==0) ? 1 : inst->GetMerchantCount(), - 0, - //merchant_slot, //instance ID, bullshit for now - (merchant_slot==0) ? inst->GetSerialNumber() : merchant_slot, - 0, - (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges), - inst->IsInstNoDrop() ? 1 : 0, - 0 - ); - - for(i=0;i<10;i++) { - ItemInst *sub=inst->GetItem(i); - if (sub) { - sub_items[i]=SerializeItem(sub,0,&sub_length,depth+1); + int r; + for (r = 0; r <= 20; r++) { + strn0cpy(eq->itemnames[r], emu->itemnames[r], sizeof(eq->itemnames[r])); } + + // move arrow item down to last element in titanium array + strn0cpy(eq->itemnames[21], emu->itemnames[22], sizeof(eq->itemnames[21])); + + int k; + for (k = 0; k <= 20; k++) { + OUT(itemicons[k]); + } + + // move arrow icon down to last element in titanium array + eq->itemicons[21] = emu->itemicons[22]; + strn0cpy(eq->text, emu->text, sizeof(eq->text)); + + FINISH_ENCODE(); } + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - *length=MakeAnyLenString(&serialization, - "%.*s%s" // For leading quotes (and protection) if a subitem; - "%s" // Instance data - "%.*s\"" // Quotes (and protection, if needed) around static data - "%i" // item->ItemClass so we can do |%s instead of %s| + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 5; // ItemPacketType + Serialization + \0 + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length + 1); + + delete[] __emu_buffer; + safe_delete_array(serialized); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LFGuild) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + uint32 Command = in->ReadUInt32(); + + if (Command != 0) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LFGuild, sizeof(structs::LFGuild_PlayerToggle_Struct)); + + memcpy(outapp->pBuffer, in->pBuffer, sizeof(structs::LFGuild_PlayerToggle_Struct)); + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = ServerToTitaniumCorpseSlot(emu->slot_id); + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToTitaniumSlot(emu->from_slot); + eq->to_slot = ServerToTitaniumSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + OUT_str(Title); + OUT_str(Text); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + + eq->unknown4236 = 0x00000000; + eq->unknown4240 = 0xffffffff; + + FINISH_ENCODE(); + } + + ENCODE(OP_PetBuffWindow) + { + ENCODE_LENGTH_EXACT(PetBuff_Struct); + SETUP_DIRECT_ENCODE(PetBuff_Struct, PetBuff_Struct); + + OUT(petid); + OUT(buffcount); + + int EQBuffSlot = 0; + + for (uint32 EmuBuffSlot = 0; EmuBuffSlot < BUFF_COUNT; ++EmuBuffSlot) + { + if (emu->spellid[EmuBuffSlot]) + { + eq->spellid[EQBuffSlot] = emu->spellid[EmuBuffSlot]; + eq->ticsremaining[EQBuffSlot++] = emu->ticsremaining[EmuBuffSlot]; + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown4184, 0xff, sizeof(eq->unknown4184)); + memset(eq->unknown04396, 0xff, sizeof(eq->unknown04396)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + OUT(item_material[r]); + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(points); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(DEX); + OUT(INT); + OUT(AGI); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); + // OUT(unknown4184[448]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + for (r = 0; r < structs::BUFF_COUNT; r++) { + OUT(buffs[r].slotid); + OUT(buffs[r].level); + OUT(buffs[r].bard_modifier); + OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + OUT(buffs[r].player_id); + } + for (r = 0; r < structs::MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + // OUT(unknown05008[360]); + // OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_PLAYER_BANDOLIER_ITEMS; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[8]); + OUT(exp); + // OUT(unknown13072[12]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(x); + OUT(y); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + OUT(expansions); + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown14932[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + // OUT(unknown19584[4]); + // OUT(unknown19588); + + + const uint8 bytes[] = { + 0x78, 0x03, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x1A, 0x04, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, 0x09, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x14 + }; + memcpy(eq->unknown12864, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_ReadBook) + { + // no apparent slot translation needed -U + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + BookText_Struct *emu_BookText_Struct = (BookText_Struct *)__emu_buffer; + + in->size = sizeof(structs::BookText_Struct) + strlen(emu_BookText_Struct->booktext); + in->pBuffer = new unsigned char[in->size]; + + structs::BookText_Struct *eq_BookText_Struct = (structs::BookText_Struct*)in->pBuffer; + + eq_BookText_Struct->window = emu_BookText_Struct->window; + eq_BookText_Struct->type = emu_BookText_Struct->type; + strcpy(eq_BookText_Struct->booktext, emu_BookText_Struct->booktext); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_RespondAA) + { + ENCODE_LENGTH_EXACT(AATable_Struct); + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + unsigned int r; + for (r = 0; r < structs::MAX_PP_AA_ARRAY; r++) { + OUT(aa_list[r].aa_skill); + OUT(aa_list[r].aa_value); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for Titanium + // clientver 1 is for all clients and 3 is for Titanium + if (emu->clientver <= 3) + { + OUT(id); + eq->unknown004 = 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->id - emu->current_level + 1); + eq->title_sid = emu->id - emu->current_level + 1; + eq->desc_sid = emu->id - emu->current_level + 1; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + OUT(type); + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + OUT(unknown80[0]); + OUT(unknown80[1]); + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_DIRECT_ENCODE(CharacterSelect_Struct, structs::CharacterSelect_Struct); + + int r; + for (r = 0; r < 10; r++) { + OUT(zone[r]); + OUT(eyecolor1[r]); + OUT(eyecolor2[r]); + OUT(hairstyle[r]); + OUT(primary[r]); + if (emu->race[r] > 473) + eq->race[r] = 1; + else + eq->race[r] = emu->race[r]; + OUT(class_[r]); + OUT_str(name[r]); + OUT(gender[r]); + OUT(level[r]); + OUT(secondary[r]); + OUT(face[r]); + OUT(beard[r]); + int k; + for (k = 0; k < 9; k++) { + OUT(equip[r][k]); + OUT(cs_colors[r][k].color); + } + OUT(haircolor[r]); + OUT(gohome[r]); + OUT(tutorial[r]); + OUT(deity[r]); + OUT(beardcolor[r]); + eq->unknown820[r] = 0xFF; + eq->unknown902[r] = 0xFF; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToTitaniumSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + in->size = sizeof(structs::Track_Struct) * EntryCount; + in->pBuffer = new unsigned char[in->size]; + structs::Track_Struct *eq = (structs::Track_Struct *) in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++eq, ++emu) + { + OUT(entityid); + OUT(padding002); + OUT(distance); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToTitaniumSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (uint32 i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_id = ivr->claim_id; + vr->item.item_id = ivr->items[0].item_id; + strcpy(vr->item.item_name, ivr->items[0].item_name); + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; + + //determine and verify length + int entrycount = in->size / sizeof(Spawn_Struct); + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); + delete in; + return; + } + + //make the EQ struct. + in->size = sizeof(structs::Spawn_Struct)*entrycount; + in->pBuffer = new unsigned char[in->size]; + structs::Spawn_Struct *eq = (structs::Spawn_Struct *) in->pBuffer; + + //zero out the packet. We could avoid this memset by setting all fields (including unknowns) + //in the loop. + memset(in->pBuffer, 0, in->size); + + //do the transform... + int r; + int k; + for (r = 0; r < entrycount; r++, eq++, emu++) { + // eq->unknown0000 = emu->unknown0000; + eq->gm = emu->gm; + // eq->unknown0003 = emu->unknown0003; + eq->aaitle = emu->aaitle; + // eq->unknown0004 = emu->unknown0004; + eq->anon = emu->anon; + eq->face = emu->face; + strcpy(eq->name, emu->name); + eq->deity = emu->deity; + // eq->unknown0073 = emu->unknown0073; + eq->size = emu->size; + // eq->unknown0079 = emu->unknown0079; + eq->NPC = emu->NPC; + eq->invis = emu->invis; + eq->haircolor = emu->haircolor; + eq->curHp = emu->curHp; + eq->max_hp = emu->max_hp; + eq->findable = emu->findable; + // eq->unknown0089[5] = emu->unknown0089[5]; + eq->deltaHeading = emu->deltaHeading; + eq->x = emu->x; + // eq->padding0054 = emu->padding0054; + eq->y = emu->y; + eq->animation = emu->animation; + // eq->padding0058 = emu->padding0058; + eq->z = emu->z; + eq->deltaY = emu->deltaY; + eq->deltaX = emu->deltaX; + eq->heading = emu->heading; + // eq->padding0066 = emu->padding0066; + eq->deltaZ = emu->deltaZ; + // eq->padding0070 = emu->padding0070; + eq->eyecolor1 = emu->eyecolor1; + // eq->unknown0115[24] = emu->unknown0115[24]; + eq->showhelm = emu->showhelm; + // eq->unknown0140[4] = emu->unknown0140[4]; + eq->is_npc = emu->is_npc; + eq->hairstyle = emu->hairstyle; + + //if(emu->gender == 1){ + // eq->hairstyle = eq->hairstyle == 0xFF ? 0 : eq->hairstyle; + //} + + eq->beardcolor = emu->beardcolor; + // eq->unknown0147[4] = emu->unknown0147[4]; + eq->level = emu->level; + // eq->unknown0259[4] = emu->unknown0259[4]; + eq->beard = emu->beard; + strcpy(eq->suffix, emu->suffix); + eq->petOwnerId = emu->petOwnerId; + eq->guildrank = emu->guildrank; + // eq->unknown0194[3] = emu->unknown0194[3]; + for (k = 0; k < 9; k++) { + eq->equipment[k] = emu->equipment[k]; + eq->colors[k].color = emu->colors[k].color; + } + for (k = 0; k < 8; k++) { + eq->set_to_0xFF[k] = 0xFF; + } + + eq->runspeed = emu->runspeed; + eq->afk = emu->afk; + eq->guildID = emu->guildID; + strcpy(eq->title, emu->title); + // eq->unknown0274 = emu->unknown0274; + eq->helm = emu->helm; + if (emu->race > 473) + eq->race = 1; + else + eq->race = emu->race; + // eq->unknown0288 = emu->unknown0288; + strcpy(eq->lastName, emu->lastName); + eq->walkspeed = emu->walkspeed; + // eq->unknown0328 = emu->unknown0328; + eq->is_pet = emu->is_pet; + eq->light = emu->light; + eq->class_ = emu->class_; + eq->eyecolor2 = emu->eyecolor2; + // eq->unknown0333 = emu->unknown0333; + eq->flymode = emu->flymode; + eq->gender = emu->gender; + eq->bodytype = emu->bodytype; + // eq->unknown0336[3] = emu->unknown0336[3]; + eq->equip_chest2 = emu->equip_chest2; + eq->spawnId = emu->spawnId; + // eq->unknown0344[4] = emu->unknown0344[4]; + eq->lfg = emu->lfg; + + /* + if (emu->face == 99) {eq->face = 0;} + if (emu->eyecolor1 == 99) {eq->eyecolor1 = 0;} + if (emu->eyecolor2 == 99) {eq->eyecolor2 = 0;} + if (emu->hairstyle == 99) {eq->hairstyle = 0;} + if (emu->haircolor == 99) {eq->haircolor = 0;} + if (emu->beard == 99) {eq->beard = 0;} + if (emu->beardcolor == 99) {eq->beardcolor = 0;} + */ + + } + + //kill off the emu structure and send the eq packet. + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + +// DECODE methods + DECODE(OP_AdventureMerchantSell) + { + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); + + IN(npcid); + emu->slot = TitaniumToServerSlot(eq->slot); + IN(charges); + IN(sell_price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = TitaniumToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = TitaniumToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + IN(slot); + IN(spell_id); + emu->inventoryslot = TitaniumToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(haircolor); + IN(gender); + IN(race); + IN(start_zone); + IN(hairstyle); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(tutorial); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = TitaniumToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = TitaniumToServerSlot(eq->from_slot); + emu->to_slot =TitaniumToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_InspectAnswer) + { + DECODE_LENGTH_EXACT(structs::InspectResponse_Struct); + SETUP_DIRECT_DECODE(InspectResponse_Struct, structs::InspectResponse_Struct); + + IN(TargetID); + IN(playerid); + + int r; + for (r = 0; r <= 20; r++) { + strn0cpy(emu->itemnames[r], eq->itemnames[r], sizeof(emu->itemnames[r])); + } + + // move arrow item up to last element in server array + strn0cpy(emu->itemnames[21], "", sizeof(emu->itemnames[21])); + strn0cpy(emu->itemnames[22], eq->itemnames[21], sizeof(emu->itemnames[22])); + + int k; + for (k = 0; k <= 20; k++) { + IN(itemicons[k]); + } + + // move arrow icon up to last element in server array + emu->itemicons[21] = 0xFFFFFFFF; + emu->itemicons[22] = eq->itemicons[21]; + + strn0cpy(emu->text, eq->text, sizeof(emu->text)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); + } + IN(link_hash); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LFGuild) + { + uint32 Command = __packet->ReadUInt32(); + + if (Command != 0) + return; + + SETUP_DIRECT_DECODE(LFGuild_PlayerToggle_Struct, structs::LFGuild_PlayerToggle_Struct); + memcpy(emu, eq, sizeof(structs::LFGuild_PlayerToggle_Struct)); + memset(emu->Unknown612, 0, sizeof(emu->Unknown612)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = TitaniumToServerCorpseSlot(eq->slot_id); + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = TitaniumToServerSlot(eq->from_slot); + emu->to_slot = TitaniumToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ReadBook) + { + // no apparent slot translation needed -U + DECODE_LENGTH_ATLEAST(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(window); + IN(type); + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); + } + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ShopPlayerSell) + { + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = TitaniumToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TraderBuy) + { + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TradeSkillCombine) + { + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = TitaniumToServerSlot(eq->container_slot); + IN(guildtribute_slot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_TributeItem) + { + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = TitaniumToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WearChange) + { + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(color.color); + IN(wear_slot_id); + emu->unknown06 = 0; + emu->elite_material = 0; + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_WhoAllRequest) + { + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + emu->type = 3; + + FINISH_DIRECT_DECODE(); + } + +// file scope helper methods + char *SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + char *serialization = nullptr; + char *instance = nullptr; + const char *protection = (const char *)"\\\\\\\\\\"; + char *sub_items[10] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; + bool stackable = inst->IsStackable(); + int16 slot_id = ServerToTitaniumSlot(slot_id_in); + uint32 merchant_slot = inst->GetMerchantSlot(); + int16 charges = inst->GetCharges(); + const Item_Struct *item = inst->GetItem(); + int i; + uint32 sub_length; + + MakeAnyLenString(&instance, + "%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|%i|", + stackable ? charges : 0, + 0, + //(merchant_slot == 0) ? slot_id : merchant_slot, // change when translator activated + (merchant_slot == 0) ? slot_id_in : merchant_slot, + inst->GetPrice(), + (merchant_slot == 0) ? 1 : inst->GetMerchantCount(), + 0, + //merchant_slot, //instance ID, bullshit for now + (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot, + 0, // item recast timer timestamp field (aka..last_cast_time field in SoF+ clients) + (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges), + inst->IsInstNoDrop() ? 1 : 0, + 0 + ); + + for (i = 0; i<10; i++) { + ItemInst *sub = inst->GetItem(i); + if (sub) { + sub_items[i] = SerializeItem(sub, 0, &sub_length, depth + 1); + } + } + + *length = MakeAnyLenString(&serialization, + "%.*s%s" // For leading quotes (and protection) if a subitem; + "%s" // Instance data + "%.*s\"" // Quotes (and protection, if needed) around static data + "%i" // item->ItemClass so we can do |%s instead of %s| #define I(field) "|%i" #define C(field) "|%s" #define S(field) "|%s" #define F(field) "|%f" #include "titanium_itemfields.h" - "%.*s\"" // Quotes (and protection, if needed) around static data - "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items - "%.*s%s" // For trailing quotes (and protection) if a subitem; - ,depth ? depth-1 : 0,protection,(depth) ? "\"" : "" - ,instance - ,depth,protection - ,item->ItemClass + "%.*s\"" // Quotes (and protection, if needed) around static data + "|%s|%s|%s|%s|%s|%s|%s|%s|%s|%s" // Sub items + "%.*s%s" // For trailing quotes (and protection) if a subitem; + , depth ? depth - 1 : 0, protection, (depth) ? "\"" : "" + , instance + , depth, protection + , item->ItemClass #define I(field) ,item->field #define C(field) ,field #define S(field) ,item->field #define F(field) ,item->field #include "titanium_itemfields.h" - ,depth,protection - ,sub_items[0] ? sub_items[0] : "" - ,sub_items[1] ? sub_items[1] : "" - ,sub_items[2] ? sub_items[2] : "" - ,sub_items[3] ? sub_items[3] : "" - ,sub_items[4] ? sub_items[4] : "" - ,sub_items[5] ? sub_items[5] : "" - ,sub_items[6] ? sub_items[6] : "" - ,sub_items[7] ? sub_items[7] : "" - ,sub_items[8] ? sub_items[8] : "" - ,sub_items[9] ? sub_items[9] : "" - ,(depth) ? depth-1 : 0,protection,(depth) ? "\"" : "" - ); + , depth, protection + , sub_items[0] ? sub_items[0] : "" + , sub_items[1] ? sub_items[1] : "" + , sub_items[2] ? sub_items[2] : "" + , sub_items[3] ? sub_items[3] : "" + , sub_items[4] ? sub_items[4] : "" + , sub_items[5] ? sub_items[5] : "" + , sub_items[6] ? sub_items[6] : "" + , sub_items[7] ? sub_items[7] : "" + , sub_items[8] ? sub_items[8] : "" + , sub_items[9] ? sub_items[9] : "" + , (depth) ? depth - 1 : 0, protection, (depth) ? "\"" : "" + ); - for(i=0;i<10;i++) { - if (sub_items[i]) - safe_delete_array(sub_items[i]); + for (i = 0; i<10; i++) { + if (sub_items[i]) + safe_delete_array(sub_items[i]); + } + + safe_delete_array(instance); + return serialization; } - safe_delete_array(instance); - return serialization; + static inline int16 ServerToTitaniumSlot(uint32 ServerSlot) + { + //int16 TitaniumSlot; + if (ServerSlot == INVALID_INDEX) + return INVALID_INDEX; + + return ServerSlot; // deprecated + } + + static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse) + { + //int16 TitaniumCorpse; + return ServerCorpse; + } + + static inline uint32 TitaniumToServerSlot(int16 TitaniumSlot) + { + //uint32 ServerSlot; + if (TitaniumSlot == INVALID_INDEX) + return INVALID_INDEX; + + return TitaniumSlot; // deprecated + } + + static inline uint32 TitaniumToServerCorpseSlot(int16 TitaniumCorpse) + { + //uint32 ServerCorpse; + return TitaniumCorpse; + } } - -} //end namespace Titanium - - - - +// end namespace Titanium diff --git a/common/patches/titanium_constants.h b/common/patches/titanium_constants.h index c07244f2d..85e525fe8 100644 --- a/common/patches/titanium_constants.h +++ b/common/patches/titanium_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef Titanium_CONSTANTS_H_ #define Titanium_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace Titanium { namespace maps { @@ -180,6 +180,7 @@ namespace Titanium { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = true; } diff --git a/common/patches/titanium_ops.h b/common/patches/titanium_ops.h index ead6bcecc..338558602 100644 --- a/common/patches/titanium_ops.h +++ b/common/patches/titanium_ops.h @@ -1,53 +1,71 @@ - -//list of packets we need to encode on the way out: -E(OP_SendCharInfo) -E(OP_SendAATable) -E(OP_LeadershipExpUpdate) -E(OP_PlayerProfile) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) +// out-going packets that require an ENCODE translation: +E(OP_Action) +E(OP_AdventureMerchantSell) +E(OP_ApplyPoison) E(OP_BazaarSearch) -E(OP_GuildMemberList) -E(OP_ZoneServerReady) -E(OP_GuildMemberLevelUpdate) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_ReadBook) -E(OP_Illusion) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_InspectAnswer) -E(OP_Track) -E(OP_RespondAA) +E(OP_BecomeTrader) +E(OP_CharInventory) +E(OP_DeleteCharge) +E(OP_DeleteItem) E(OP_DeleteSpawn) -E(OP_WearChange) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_Action) -E(OP_BecomeTrader) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_GuildMemberLevelUpdate) +E(OP_GuildMemberList) +E(OP_Illusion) +E(OP_InspectAnswer) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_LeadershipExpUpdate) E(OP_LFGuild) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) +E(OP_LootItem) +E(OP_MoveItem) +E(OP_OnLevelMessage) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_NewSpawn) +E(OP_ReadBook) +E(OP_RespondAA) +E(OP_SendCharInfo) +E(OP_SendAATable) +E(OP_ShopPlayerSell) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_ZoneEntry) +E(OP_ZoneServerReady) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_ApplyPoison) +D(OP_AugmentItem) +D(OP_CastSpell) D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_TraderBuy) -D(OP_WhoAllRequest) -D(OP_ReadBook) +D(OP_Consume) +D(OP_DeleteItem) D(OP_FaceChange) -D(OP_InspectRequest) D(OP_InspectAnswer) -D(OP_WearChange) +D(OP_InspectRequest) +D(OP_ItemLinkClick) D(OP_LFGuild) +D(OP_LootItem) +D(OP_MoveItem) +D(OP_ReadBook) +D(OP_SetServerFilter) +D(OP_ShopPlayerSell) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) +D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/titanium_structs.h b/common/patches/titanium_structs.h index 1c058fb77..e8e5a9f23 100644 --- a/common/patches/titanium_structs.h +++ b/common/patches/titanium_structs.h @@ -419,7 +419,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint8 cs_unknown[4]; }; /* @@ -565,7 +565,7 @@ struct CharCreate_Struct /*0064*/ uint32 face; // Could be unknown0076 /*0068*/ uint32 eyecolor1; //its possiable we could have these switched /*0073*/ uint32 eyecolor2; //since setting one sets the other we really can't check -/*0076*/ uint32 unknown0076; // Could be face +/*0076*/ uint32 tutorial; /*0080*/ }; @@ -619,14 +619,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -1217,19 +1265,27 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; }; +struct DeleteItem_Struct +{ +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ +}; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; +/*0012*/ }; // @@ -1431,12 +1487,6 @@ struct CombatAbility_Struct { uint32 m_skill; }; -struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; -}; - //Instill Doubt struct Instill_Doubt_Struct { uint8 i_id; @@ -1590,6 +1640,14 @@ struct Adventure_Purchase_Struct { /*008*/ uint32 variable; }; +struct Adventure_Sell_Struct { +/*000*/ uint32 unknown000; //0x01 +/*004*/ uint32 npcid; +/*008*/ uint32 slot; +/*012*/ uint32 charges; +/*016*/ uint32 sell_price; +}; + struct AdventurePoints_Update_Struct { /*000*/ uint32 ldon_available_points; // Total available points /*004*/ uint8 unkown_apu004[20]; @@ -2583,10 +2641,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -2622,7 +2680,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3046,7 +3104,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; @@ -3154,6 +3212,11 @@ struct AnnoyingZoneUnknown_Struct { uint32 value; //always 4 }; +struct ApplyPoison_Struct { + uint32 inventorySlot; + uint32 success; +}; + struct GuildMemberUpdate_Struct { /*000*/ uint32 guild_id; //not sure /*004*/ char member_name[64]; diff --git a/common/patches/underfoot.cpp b/common/patches/underfoot.cpp index bdac3c72b..ad850be36 100644 --- a/common/patches/underfoot.cpp +++ b/common/patches/underfoot.cpp @@ -1,4 +1,3 @@ - #include "../debug.h" #include "underfoot.h" #include "../opcodemgr.h" @@ -18,1001 +17,2580 @@ namespace Underfoot { + static const char *name = "Underfoot"; + static OpcodeManager *opcodes = nullptr; + static Strategy struct_strategy; -static const char *name = "Underfoot"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; + char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); -char* SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); + // server to client inventory location converters + static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot); + static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse); -void Register(EQStreamIdentifier &into) { - //create our opcode manager if we havent already - if(opcodes == nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - //load up the opcode manager. - //TODO: figure out how to support shared memory with multiple patches... - opcodes = new RegularOpcodeManager(); - if(!opcodes->LoadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); - return; + // client to server inventory location converters + static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot); + static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse); + + void Register(EQStreamIdentifier &into) + { + //create our opcode manager if we havent already + if (opcodes == nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + //load up the opcode manager. + //TODO: figure out how to support shared memory with multiple patches... + opcodes = new RegularOpcodeManager(); + if (!opcodes->LoadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error loading opcodes file %s. Not registering patch %s.", opfile.c_str(), name); + return; + } + } + + //ok, now we have what we need to register. + + EQStream::Signature signature; + std::string pname; + + //register our world signature. + pname = std::string(name) + "_world"; + signature.ignore_eq_opcode = 0; + signature.first_length = sizeof(structs::LoginInfo_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + //register our zone signature. + pname = std::string(name) + "_zone"; + signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); + signature.first_length = sizeof(structs::ClientZoneEntry_Struct); + signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); + into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); + + + + _log(NET__IDENTIFY, "Registered patch %s", name); + } + + void Reload() + { + //we have a big problem to solve here when we switch back to shared memory + //opcode managers because we need to change the manager pointer, which means + //we need to go to every stream and replace it's manager. + + if (opcodes != nullptr) { + //TODO: get this file name from the config file + std::string opfile = "patch_"; + opfile += name; + opfile += ".conf"; + if (!opcodes->ReloadOpcodes(opfile.c_str())) { + _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); + return; + } + _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); } } - //ok, now we have what we need to register. - - EQStream::Signature signature; - std::string pname; - - //register our world signature. - pname = std::string(name) + "_world"; - signature.ignore_eq_opcode = 0; - signature.first_length = sizeof(structs::LoginInfo_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_SendLoginInfo); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - //register our zone signature. - pname = std::string(name) + "_zone"; - signature.ignore_eq_opcode = opcodes->EmuToEQ(OP_AckPacket); - signature.first_length = sizeof(structs::ClientZoneEntry_Struct); - signature.first_eq_opcode = opcodes->EmuToEQ(OP_ZoneEntry); - into.RegisterPatch(signature, pname.c_str(), &opcodes, &struct_strategy); - - - - _log(NET__IDENTIFY, "Registered patch %s", name); -} - -void Reload() { - - //we have a big problem to solve here when we switch back to shared memory - //opcode managers because we need to change the manager pointer, which means - //we need to go to every stream and replace it's manager. - - if(opcodes != nullptr) { - //TODO: get this file name from the config file - std::string opfile = "patch_"; - opfile += name; - opfile += ".conf"; - if(!opcodes->ReloadOpcodes(opfile.c_str())) { - _log(NET__OPCODES, "Error reloading opcodes file %s for patch %s.", opfile.c_str(), name); - return; - } - _log(NET__OPCODES, "Reloaded opcodes for patch %s", name); + Strategy::Strategy() : StructStrategy() + { + //all opcodes default to passthrough. +#include "ss_register.h" +#include "underfoot_ops.h" } -} + std::string Strategy::Describe() const + { + std::string r; + r += "Patch "; + r += name; + return(r); + } - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "ss_register.h" - #include "underfoot_ops.h" -} - -std::string Strategy::Describe() const { - std::string r; - r += "Patch "; - r += name; - return(r); -} - -const EQClientVersion Strategy::ClientVersion() const -{ - return EQClientUnderfoot; -} + const EQClientVersion Strategy::ClientVersion() const + { + return EQClientUnderfoot; + } #include "ss_define.h" +// ENCODE methods + ENCODE(OP_Action) + { + ENCODE_LENGTH_EXACT(Action_Struct); + SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); -// Converts Server Slot IDs to Underfoot Slot IDs for use in Encodes -static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot) { - uint32 UnderfootSlot = 0; + OUT(target); + OUT(source); + OUT(level); + eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; + eq->knockback_angle = emu->sequence; + OUT(type); + OUT(spell); + eq->level2 = eq->level; + eq->effect_flag = emu->buff_unknown; + eq->unknown37 = 0x01; + eq->unknown44 = 0xFFFFFFFF; + eq->unknown48 = 0xFFFFFFFF; + eq->unknown52 = 0xFFFFFFFF; - if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots - UnderfootSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) - UnderfootSlot = ServerSlot + 11; - - else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) - UnderfootSlot = ServerSlot + 1; - - else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) - UnderfootSlot = ServerSlot + 1; - - else if (ServerSlot == MainPowerSource) - UnderfootSlot = slots::MainPowerSource; - - else - UnderfootSlot = ServerSlot; + /*OUT(target); + OUT(source); + OUT(level); + OUT(instrument_mod); + eq->sequence = emu->sequence; + OUT(type); + //OUT(damage); + OUT(spell); + eq->level2 = emu->level; + OUT(buff_unknown); // if this is 4, a buff icon is made + //eq->unknown0036 = -1; + //eq->unknown0040 = -1; + //eq->unknown0044 = -1;*/ - return UnderfootSlot; -} - -// Converts Underfoot Slot IDs to Server Slot IDs for use in Decodes -static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot) { - uint32 ServerSlot = 0; - - if(UnderfootSlot >= slots::MainAmmo && UnderfootSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots - ServerSlot = UnderfootSlot - 1; - - else if(UnderfootSlot >= consts::GENERAL_BAGS_BEGIN && UnderfootSlot <= consts::CURSOR_BAG_END) - ServerSlot = UnderfootSlot - 11; - - else if(UnderfootSlot >= consts::BANK_BAGS_BEGIN && UnderfootSlot <= consts::BANK_BAGS_END) - ServerSlot = UnderfootSlot - 1; - - else if(UnderfootSlot >= consts::SHARED_BANK_BAGS_BEGIN && UnderfootSlot <= consts::SHARED_BANK_BAGS_END) - ServerSlot = UnderfootSlot - 1; - - else if(UnderfootSlot == slots::MainPowerSource) - ServerSlot = MainPowerSource; - - else - ServerSlot = UnderfootSlot; - - return ServerSlot; -} - -/* -// Converts Server Corpse Slot IDs to Underfoot Corpse Slot IDs for use in Encodes -static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse) { - uint32 UnderfootCorpse; - // reserved -} -*/ -/* -// Converts Underfoot Corpse Slot IDs to Server Corpse Slot IDs for use in Decodes -static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse) { - uint32 ServerCorpse; - // reserved -} -*/ - - -ENCODE(OP_OpenNewTasksWindow) { - - AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; - AvailableTaskData1_Struct* __emu_AvailableTaskData1; - AvailableTaskData2_Struct* __emu_AvailableTaskData2; - AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; - - structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; - structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; - structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; - structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; - - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; - - // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. - // - in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); - - in->pBuffer = new unsigned char[in->size]; - - unsigned char *__eq_buffer = in->pBuffer; - - __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; - - char *__eq_ptr, *__emu_Ptr; - - // Copy Header - // - // - - __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; - __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; - __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; - - __emu_Ptr = (char *) __emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); - __eq_ptr = (char *) __eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); - - for(uint32 i=0; i<__emu_AvailableTaskHeader->TaskCount; i++) { - - __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; - __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; - - __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; - // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen - // in Underfoot packets. Changing it to 0x3f000000 makes the title red. - __eq_AvailableTaskData1->unknown1 = 0x3f800000; - __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; - __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; - - __emu_Ptr += sizeof(AvailableTaskData1_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Title - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Description - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __eq_ptr[0] = 0; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; - __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; - - __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; - __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; - __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; - __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; - - __emu_Ptr += sizeof(AvailableTaskData2_Struct); - __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; - - __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; - __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; - - __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; - __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; - __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; - __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; - - __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); - __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); - - strcpy(__eq_ptr, __emu_Ptr); // Unknown string - - __emu_Ptr += strlen(__emu_Ptr) + 1; - __eq_ptr += strlen(__eq_ptr) + 1; + FINISH_ENCODE(); } - delete[] __emu_buffer; + ENCODE(OP_AdventureMerchantSell) + { + ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); + SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - dest->FastQueuePacket(&in, ack_req); -} + eq->unknown000 = 1; + OUT(npcid); + eq->slot = ServerToUnderfootSlot(emu->slot); + OUT(charges); + OUT(sell_price); + FINISH_ENCODE(); + } -ENCODE(OP_SendCharInfo) { - ENCODE_LENGTH_EXACT(CharacterSelect_Struct); - SETUP_VAR_ENCODE(CharacterSelect_Struct); + ENCODE(OP_AltCurrency) + { + EQApplicationPacket *in = *p; + *p = nullptr; + unsigned char *emu_buffer = in->pBuffer; + uint32 opcode = *((uint32*)emu_buffer); - //EQApplicationPacket *packet = *p; - //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + if (opcode == 8) { + AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; - int char_count; - int namelen = 0; - for(char_count = 0; char_count < 10; char_count++) { - if(emu->name[char_count][0] == '\0') - break; - if(strcmp(emu->name[char_count], "") == 0) - break; - namelen += strlen(emu->name[char_count]); - } + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) + + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); + structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; - int total_length = sizeof(structs::CharacterSelect_Struct) - + char_count * sizeof(structs::CharacterSelectEntry_Struct) - + namelen; - - ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); - - //unsigned char *eq_buffer = new unsigned char[total_length]; - //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; - - eq->char_count = char_count; - eq->total_chars = 10; - - unsigned char *bufptr = (unsigned char *) eq->entries; - int r; - for(r = 0; r < char_count; r++) { - { //pre-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->level = emu->level[r]; - eq2->hairstyle = emu->hairstyle[r]; - eq2->gender = emu->gender[r]; - memcpy(eq2->name, emu->name[r], strlen(emu->name[r])+1); - } - //adjust for name. - bufptr += strlen(emu->name[r]); - { //post-name section... - structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; - eq2->beard = emu->beard[r]; - eq2->haircolor = emu->haircolor[r]; - eq2->face = emu->face[r]; - int k; - for(k = 0; k < _MaterialCount; k++) { - eq2->equip[k].equip0 = emu->equip[r][k]; - eq2->equip[k].equip1 = 0; - eq2->equip[k].itemid = 0; - eq2->equip[k].color.color = emu->cs_colors[r][k].color; + out_populate->opcode = populate->opcode; + out_populate->count = populate->count; + for (uint32 i = 0; i < populate->count; ++i) { + out_populate->entries[i].currency_number = populate->entries[i].currency_number; + out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; + out_populate->entries[i].item_id = populate->entries[i].item_id; + out_populate->entries[i].item_icon = populate->entries[i].item_icon; + out_populate->entries[i].stack_size = populate->entries[i].stack_size; + out_populate->entries[i].unknown00 = populate->entries[i].unknown00; } - eq2->primary = emu->primary[r]; - eq2->secondary = emu->secondary[r]; - eq2->tutorial = emu->tutorial[r]; // was u15 - eq2->u15 = 0xff; - eq2->deity = emu->deity[r]; - eq2->zone = emu->zone[r]; - eq2->u19 = 0xFF; - eq2->race = emu->race[r]; - eq2->gohome = emu->gohome[r]; - eq2->class_ = emu->class_[r]; - eq2->eyecolor1 = emu->eyecolor1[r]; - eq2->beardcolor = emu->beardcolor[r]; - eq2->eyecolor2 = emu->eyecolor2[r]; - eq2->drakkin_heritage = emu->drakkin_heritage[r]; - eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; - eq2->drakkin_details = emu->drakkin_details[r]; + + dest->FastQueuePacket(&outapp, ack_req); } - bufptr += sizeof(structs::CharacterSelectEntry_Struct); + else { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); + memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); + dest->FastQueuePacket(&outapp, ack_req); + } + + //dest->FastQueuePacket(&outapp, ack_req); + delete in; } - FINISH_ENCODE(); - -} - -ENCODE(OP_ZoneServerInfo) { - SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); - OUT_str(ip); - OUT(port); - FINISH_ENCODE(); -} - -ENCODE(OP_SendZonepoints) { - SETUP_VAR_ENCODE(ZonePoints); - ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); - - eq->count = emu->count; - for(uint32 i = 0; i < emu->count; ++i) + ENCODE(OP_AltCurrencySell) { - eq->zpe[i].iterator = emu->zpe[i].iterator; - eq->zpe[i].x = emu->zpe[i].x; - eq->zpe[i].y = emu->zpe[i].y; - eq->zpe[i].z = emu->zpe[i].z; - eq->zpe[i].heading = emu->zpe[i].heading; - eq->zpe[i].zoneid = emu->zpe[i].zoneid; - eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; - } + ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); + SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - FINISH_ENCODE(); -} - -ENCODE(OP_SendAATable) { - ENCODE_LENGTH_ATLEAST(SendAA_Struct); - - SETUP_VAR_ENCODE(SendAA_Struct); - ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); - - // Check clientver field to verify this AA should be sent for SoF - // clientver 1 is for all clients and 6 is for Underfoot - if (emu->clientver <= 6 ) - { - OUT(id); - eq->unknown004 = 1; - //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); - //eq->title_sid = emu->id - emu->current_level + 1; - //eq->desc_sid = emu->id - emu->current_level + 1; - eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->sof_next_skill); - eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->sof_next_skill); - eq->title_sid = emu->sof_next_skill; - eq->desc_sid = emu->sof_next_skill; - OUT(class_type); + OUT(merchant_entity_id); + eq->slot_id = ServerToUnderfootSlot(emu->slot_id); + OUT(charges); OUT(cost); - OUT(seq); - OUT(current_level); - OUT(prereq_skill); - OUT(prereq_minpoints); - eq->type = emu->sof_type; + + FINISH_ENCODE(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToUnderfootSlot(emu->inventorySlot); + OUT(success); + + FINISH_ENCODE(); + } + + ENCODE(OP_Barter) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + + if (SubAction != Barter_BuyerAppearance) + { + dest->FastQueuePacket(&in, ack_req); + + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = 80; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + char Name[64]; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); + uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); + uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + VARSTRUCT_DECODE_STRING(Name, Buffer); + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + OutBuffer = (char *)in->pBuffer + 72; + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_BazaarSearch) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *Buffer = (char *)in->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if (SubAction != BazaarSearchResults) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + unsigned char *__emu_buffer = in->pBuffer; + + BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); + + if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); + delete in; + return; + } + + in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); + in->pBuffer = new unsigned char[in->size]; + memset(in->pBuffer, 0, in->size); + + structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; + + for (int i = 0; i < EntryCount; ++i, ++emu, ++eq) + { + OUT(Beginning.Action); + OUT(SellerID); + memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); + OUT(NumItems); + OUT(ItemID); + OUT(SerialNumber); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(Cost); + OUT(ItemStat); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Buff) + { + ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); + SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); + + OUT(entityid); + OUT(slot); + OUT(level); + OUT(effect); + //eq->unknown7 = 10; OUT(spellid); - OUT(spell_type); - OUT(spell_refresh); - OUT(classes); - OUT(berserker); - //eq->max_level = emu->sof_max_level; - OUT(max_level); - OUT(last_id); - OUT(next_id); - OUT(cost2); - eq->aa_expansion = emu->aa_expansion; - eq->special_category = emu->special_category; - OUT(total_abilities); - unsigned int r; - for(r = 0; r < emu->total_abilities; r++) { - OUT(abilities[r].skill_id); - OUT(abilities[r].base1); - OUT(abilities[r].base2); - OUT(abilities[r].slot); - } + OUT(duration); + OUT(slotid); + OUT(bufffade); // Live (October 2011) sends a 2 rather than 0 when a buff is created, but it doesn't seem to matter. + OUT(num_hits); + eq->unknown008 = 1.0f; + + FINISH_ENCODE(); } - FINISH_ENCODE(); -} -ENCODE(OP_LeadershipExpUpdate) { - SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); - OUT(group_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_exp); - OUT(raid_leadership_points); - FINISH_ENCODE(); -} - -ENCODE(OP_RespondAA) { - SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); - - eq->aa_spent = emu->aa_spent; - eq->aa_assigned = emu->aa_spent; - eq->aa_spent3 = emu->aa_spent; - eq->unknown012 = 0; - eq->unknown016 = 0; - eq->unknown020 = 0; - - for(uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + ENCODE(OP_BuffCreate) { - eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; - eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; - eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; - } + SETUP_VAR_ENCODE(BuffIcon_Struct); - FINISH_ENCODE(); -} + uint32 sz = 12 + (17 * emu->count); + __packet->size = sz; + __packet->pBuffer = new unsigned char[sz]; + memset(__packet->pBuffer, 0, sz); -ENCODE(OP_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + __packet->WriteUInt32(emu->entity_id); + __packet->WriteUInt32(0); + __packet->WriteUInt8(emu->all_buffs); // 1 = all buffs, 0 = 1 buff + __packet->WriteUInt16(emu->count); - uint32 r; - - eq->available_slots=0xffffffff; - memset(eq->unknown06284, 0xff, sizeof(eq->unknown06284)); - memset(eq->unknown07284, 0xff, sizeof(eq->unknown07284)); - -// OUT(checksum); - OUT(gender); - OUT(race); - OUT(class_); -// OUT(unknown00016); - OUT(level); - eq->level1 = emu->level; -// OUT(unknown00022[2]); - for(r = 0; r < 5; r++) { - OUT(binds[r].zoneId); - OUT(binds[r].x); - OUT(binds[r].y); - OUT(binds[r].z); - OUT(binds[r].heading); - } - OUT(deity); - OUT(intoxication); - OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); - OUT(abilitySlotRefresh); - OUT(points); // Relocation Test -// OUT(unknown0166[4]); - OUT(haircolor); - OUT(beardcolor); - OUT(eyecolor1); - OUT(eyecolor2); - OUT(hairstyle); - OUT(beard); -// OUT(unknown00178[10]); - for(r = 0; r < 9; r++) { - eq->equipment[r].equip0 = emu->item_material[r]; - eq->equipment[r].equip1 = 0; - eq->equipment[r].itemId = 0; - //eq->colors[r].color = emu->colors[r].color; - } - for(r = 0; r < 7; r++) { - OUT(item_tint[r].color); - } -// OUT(unknown00224[48]); - //NOTE: new client supports 300 AAs, our internal rep/PP - //only supports 240.. - for(r = 0; r < MAX_PP_AA_ARRAY; r++) { - OUT(aa_array[r].AA); - OUT(aa_array[r].value); - } -// OUT(unknown02220[4]); - OUT(mana); - OUT(cur_hp); - OUT(STR); - OUT(STA); - OUT(CHA); - OUT(AGI); - OUT(INT); - OUT(DEX); - OUT(WIS); - OUT(face); -// OUT(unknown02264[47]); - OUT_array(spell_book, structs::MAX_PP_SPELLBOOK); -// OUT(unknown4184[128]); - OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); -// OUT(unknown04396[32]); - OUT(platinum); - OUT(gold); - OUT(silver); - OUT(copper); - OUT(platinum_cursor); - OUT(gold_cursor); - OUT(silver_cursor); - OUT(copper_cursor); - - OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) - -// OUT(unknown04760[236]); - OUT(toxicity); - OUT(thirst_level); - OUT(hunger_level); - //PS this needs to be figured out more; but it was 'good enough' - for(r = 0; r < structs::BUFF_COUNT; r++) - { - if(emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + for (uint16 i = 0; i < emu->count; ++i) { - eq->buffs[r].unknown004 = 0x3f800000; - eq->buffs[r].slotid = 2; - eq->buffs[r].player_id = 0x000717fd; + uint16 buffslot = emu->entries[i].buff_slot; + if (emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) + { + buffslot += 5; + } + else if (emu->entries[i].buff_slot >= 37) + { + buffslot += 14; + } + + __packet->WriteUInt32(buffslot); + __packet->WriteUInt32(emu->entries[i].spell_id); + __packet->WriteUInt32(emu->entries[i].tics_remaining); + __packet->WriteUInt32(emu->entries[i].num_hits); + __packet->WriteString(""); } - else + __packet->WriteUInt8(!emu->all_buffs); + + FINISH_ENCODE(); + /* + uint32 write_var32 = 60; + uint8 write_var8 = 1; + ss.write((const char*)&emu->entity_id, sizeof(uint32)); + ss.write((const char*)&write_var32, sizeof(uint32)); + ss.write((const char*)&write_var8, sizeof(uint8)); + ss.write((const char*)&emu->count, sizeof(uint16)); + write_var32 = 0; + write_var8 = 0; + for(uint16 i = 0; i < emu->count; ++i) { - eq->buffs[r].slotid = 0; - } - //OUT(buffs[r].slotid); - OUT(buffs[r].level); - //OUT(buffs[r].bard_modifier); - //OUT(buffs[r].effect); - OUT(buffs[r].spellid); - OUT(buffs[r].duration); - OUT(buffs[r].counters); - //OUT(buffs[r].player_id); - } - for(r = 0; r < MAX_PP_DISCIPLINES; r++) { - OUT(disciplines.values[r]); - } - OUT_array(recastTimers, structs::MAX_RECAST_TYPES); -// OUT(unknown08124[360]); - OUT(endurance); - OUT(aapoints_spent); - OUT(aapoints); -// OUT(unknown06160[4]); - //NOTE: new client supports 20 bandoliers, our internal rep - //only supports 4.. - for(r = 0; r < 4; r++) { - OUT_str(bandoliers[r].name); - uint32 k; - for(k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { - OUT(bandoliers[r].items[k].item_id); - OUT(bandoliers[r].items[k].icon); - OUT_str(bandoliers[r].items[k].item_name); - } - } -// OUT(unknown07444[5120]); - for(r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { - OUT(potionbelt.items[r].item_id); - OUT(potionbelt.items[r].icon); - OUT_str(potionbelt.items[r].item_name); - } -// OUT(unknown12852[8]); -// OUT(unknown12864[76]); - OUT_str(name); - OUT_str(last_name); - OUT(guild_id); - OUT(birthday); - OUT(lastlogin); - OUT(timePlayedMin); - OUT(pvp); - OUT(anon); - OUT(gm); - OUT(guildrank); - OUT(guildbanker); -// OUT(unknown13054[12]); - OUT(exp); -// OUT(unknown13072[8]); - OUT(timeentitledonaccount); - OUT_array(languages, structs::MAX_PP_LANGUAGE); -// OUT(unknown13109[7]); - OUT(y); //reversed x and y - OUT(x); - OUT(z); - OUT(heading); -// OUT(unknown13132[4]); - OUT(platinum_bank); - OUT(gold_bank); - OUT(silver_bank); - OUT(copper_bank); - OUT(platinum_shared); -// OUT(unknown13156[84]); - //OUT(expansions); - eq->expansions = 0xffff; -// OUT(unknown13244[12]); - OUT(autosplit); -// OUT(unknown13260[16]); - OUT(zone_id); - OUT(zoneInstance); - for(r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { - OUT_str(groupMembers[r]); - } - strcpy(eq->groupLeader, emu->groupMembers[0]); -// OUT_str(groupLeader); -// OUT(unknown13728[660]); - OUT(entityid); - OUT(leadAAActive); -// OUT(unknown14392[4]); - OUT(ldon_points_guk); - OUT(ldon_points_mir); - OUT(ldon_points_mmc); - OUT(ldon_points_ruj); - OUT(ldon_points_tak); - OUT(ldon_points_available); -// OUT(unknown14420[132]); - OUT(tribute_time_remaining); - OUT(career_tribute_points); -// OUT(unknown7208); - OUT(tribute_points); -// OUT(unknown7216); - OUT(tribute_active); - for(r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { - OUT(tributes[r].tribute); - OUT(tributes[r].tier); - } -// OUT(unknown14616[8]); - OUT(group_leadership_exp); -// OUT(unknown14628); - OUT(raid_leadership_exp); - OUT(group_leadership_points); - OUT(raid_leadership_points); - OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); -// OUT(unknown14772[128]); - OUT(air_remaining); - OUT(PVPKills); - OUT(PVPDeaths); - OUT(PVPCurrentPoints); - OUT(PVPCareerPoints); - OUT(PVPBestKillStreak); - OUT(PVPWorstDeathStreak); - OUT(PVPCurrentKillStreak); -// OUT(unknown17892[4580]); - OUT(expAA); -// OUT(unknown19516[40]); - OUT(currentRadCrystals); - OUT(careerRadCrystals); - OUT(currentEbonCrystals); - OUT(careerEbonCrystals); - OUT(groupAutoconsent); - OUT(raidAutoconsent); - OUT(guildAutoconsent); -// OUT(unknown19575[5]); - eq->level3 = emu->level; - eq->showhelm = emu->showhelm; - OUT(RestTimer); -// OUT(unknown19584[4]); -// OUT(unknown19588); - - -const uint8 bytes[] = { -0xa3,0x02,0x00,0x00,0x95,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x19,0x00,0x00,0x00, -0x19,0x00,0x00,0x00,0x19,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00, -0x0F,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1F,0x85,0xEB,0x3E,0x33,0x33,0x33,0x3F, -0x04,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x07,0x00,0x00,0x00, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 -}; - - memcpy(eq->unknown18020, bytes, sizeof(bytes)); - - //set the checksum... - CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct)-4); - - FINISH_ENCODE(); -} - -ENCODE(OP_NewZone) { - SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); - OUT_str(char_name); - OUT_str(zone_short_name); - OUT_str(zone_long_name); - OUT(ztype); - int r; - for(r = 0; r < 4; r++) { - OUT(fog_red[r]); - OUT(fog_green[r]); - OUT(fog_blue[r]); - OUT(fog_minclip[r]); - OUT(fog_maxclip[r]); - } - OUT(gravity); - OUT(time_type); - for(r = 0; r < 4; r++) { - OUT(rain_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(rain_duration[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_chance[r]); - } - for(r = 0; r < 4; r++) { - OUT(snow_duration[r]); - } - for(r = 0; r < 32; r++) { - eq->unknown537[r] = 0xFF; //observed - } - OUT(sky); - OUT(zone_exp_multiplier); - OUT(safe_y); - OUT(safe_x); - OUT(safe_z); - OUT(max_z); - OUT(underworld); - OUT(minclip); - OUT(maxclip); - OUT_str(zone_short_name2); - OUT(zone_id); - OUT(zone_instance); - OUT(SuspendBuffs); - - eq->FogDensity = emu->fog_density; - - /*fill in some unknowns with observed values, hopefully it will help */ - eq->unknown800 = -1; - eq->unknown844 = 600; - eq->unknown880 = 50; - eq->unknown884 = 10; - eq->unknown888 = 1; - eq->unknown889 = 0; - eq->unknown890 = 1; - eq->unknown891 = 0; - eq->unknown892 = 0; - eq->unknown893 = 0; - eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off - eq->unknown895 = 0; - eq->unknown896 = 180; - eq->unknown900 = 180; - eq->unknown904 = 180; - eq->unknown908 = 2; - eq->unknown912 = 2; - - FINISH_ENCODE(); -} - - -ENCODE(OP_Track) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - Track_Struct *emu = (Track_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(Track_Struct); - - if(EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); - delete in; - return; - } - - int PacketSize = 2; - - for(int i = 0; i < EntryCount; ++i, ++emu) - PacketSize += (12 + strlen(emu->name)); - - emu = (Track_Struct *) __emu_buffer; - - in->size = PacketSize; - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); - - for(int i = 0; i < EntryCount; ++i, ++emu) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - VARSTRUCT_ENCODE_STRING(Buffer, emu->name); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_PetBuffWindow) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - - PetBuff_Struct *emu = (PetBuff_Struct *) __emu_buffer; - - int PacketSize = 12 + (emu->buffcount * 17); - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - char *Buffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); - - for(unsigned int i = 0; i < BUFF_COUNT; ++i) - { - if(emu->spellid[i]) + if(emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); + emu->entries[i].buff_slot += 5; + } + else if(emu->entries[i].buff_slot >= 37) + { + emu->entries[i].buff_slot += 14; + } + ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); + ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); + ss.write((const char*)&write_var32, sizeof(uint32)); + ss.write((const char*)&write_var8, sizeof(uint8)); + } + ss.write((const char*)&write_var8, sizeof(uint8)); + */ + } + + ENCODE(OP_CancelTrade) + { + ENCODE_LENGTH_EXACT(CancelTrade_Struct); + SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); + + OUT(fromid); + OUT(action); + + FINISH_ENCODE(); + } + + ENCODE(OP_ChannelMessage) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)in->pBuffer; + + unsigned char *__emu_buffer = in->pBuffer; + + in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; + + in->pBuffer = new unsigned char[in->size]; + + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_CharInventory) + { + //consume the packet + EQApplicationPacket *in = *p; + + *p = nullptr; + + if (in->size == 0) { + + in->size = 4; + in->pBuffer = new uchar[in->size]; + *((uint32 *)in->pBuffer) = 0; + + dest->FastQueuePacket(&in, ack_req); + return; + } + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + + int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); + + if (ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { + + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", + opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); + + delete in; + return; + } + + InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *)in->pBuffer; + + in->pBuffer = new uchar[4]; + *(uint32 *)in->pBuffer = ItemCount; + in->size = 4; + + for (int r = 0; r < ItemCount; r++, eq++) { + + uint32 Length = 0; + char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); + + if (Serialized) { + + uchar *OldBuffer = in->pBuffer; + in->pBuffer = new uchar[in->size + Length]; + memcpy(in->pBuffer, OldBuffer, in->size); + + safe_delete_array(OldBuffer); + + memcpy(in->pBuffer + in->size, Serialized, Length); + in->size += Length; + + safe_delete_array(Serialized); + } + else { + _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.", eq->slot_id); + } + } + + delete[] __emu_buffer; + + //_log(NET__ERROR, "Sending inventory to client"); + //_hex(NET__ERROR, in->pBuffer, in->size); + + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ClientUpdate) + { + ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); + SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); + + OUT(spawn_id); + OUT(x_pos); + OUT(delta_x); + OUT(delta_y); + OUT(z_pos); + OUT(delta_heading); + OUT(y_pos); + OUT(delta_z); + OUT(animation); + OUT(heading); + + FINISH_ENCODE(); + } + + ENCODE(OP_Consider) + { + ENCODE_LENGTH_EXACT(Consider_Struct); + SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); + + OUT(playerid); + OUT(targetid); + OUT(faction); + OUT(level); + OUT(pvpcon); + + FINISH_ENCODE(); + } + + ENCODE(OP_Damage) + { + ENCODE_LENGTH_EXACT(CombatDamage_Struct); + SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + OUT(target); + OUT(source); + OUT(type); + OUT(spellid); + OUT(damage); + eq->sequence = emu->sequence; + + FINISH_ENCODE(); + } + + ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToUnderfootSlot(emu->from_slot); + eq->to_slot = ServerToUnderfootSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_DisciplineUpdate) + { + ENCODE_LENGTH_EXACT(Disciplines_Struct); + SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); + + memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzCompass) + { + SETUP_VAR_ENCODE(ExpeditionCompass_Struct); + ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); + OUT(count); + + for (uint32 i = 0; i < emu->count; ++i) + { + OUT(entries[i].x); + OUT(entries[i].y); + OUT(entries[i].z); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionEndsWarning) + { + ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); + SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); + + OUT(minutes_remaining); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionInfo) + { + ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); + + OUT(max_players); + eq->unknown004 = 785316192; + eq->unknown008 = 435601; + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->leader_name, emu->leader_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzExpeditionList) + { + SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); + ss.write((const char*)&null_term, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzJoinExpeditionConfirm) + { + ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); + SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); + + strcpy(eq->expedition_name, emu->expedition_name); + strcpy(eq->player_name, emu->player_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzLeaderStatus) + { + SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + uint32 client_id = 0; + uint8 null_term = 0; + + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write(emu->leader_name, strlen(emu->leader_name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&client_id, sizeof(uint32));//1 + ss.write((const char*)&client_id, sizeof(uint32)); + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_DzMemberList) + { + SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + uint32 client_id = 0; + uint8 null_term = 0; + ss.write((const char*)&client_id, sizeof(uint32)); + ss.write((const char*)&emu->count, sizeof(uint32)); + for (uint32 i = 0; i < emu->count; ++i) + { + ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); + ss.write((const char*)&null_term, sizeof(char)); + ss.write((const char*)&emu->entries[i].status, sizeof(char)); + } + + __packet->size = ss.str().length(); + __packet->pBuffer = new unsigned char[__packet->size]; + memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); + + FINISH_ENCODE(); + } + + ENCODE(OP_ExpansionInfo) + { + ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); + SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); + + OUT(Expansions); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroundSpawn) + { + // We are not encoding the spawn_id field here, or a size but it doesn't appear to matter. + // + EQApplicationPacket *in = *p; + *p = nullptr; + + Object_Struct *emu = (Object_Struct *)in->pBuffer; + unsigned char *__emu_buffer = in->pBuffer; + in->size = strlen(emu->object_name) + 58; + in->pBuffer = new unsigned char[in->size]; + char *OutBuffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); + VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); + VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00006762 + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observer 0x7fffbb64 + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); + // This next field is actually a float. There is a groundspawn in freeportwest (sack of money sitting on some barrels) which requires this + // field to be set to (float)255.0 to appear at all, and also the size field below to be 5, to be the correct size. I think SoD has the same + // issue. + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0 + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // This appears to be the size field. + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); + VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); // Unknown, observed 0xffffffff + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00000014 + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown, observed 0x00 + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GroupCancelInvite) + { + ENCODE_LENGTH_EXACT(GroupCancel_Struct); + SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + OUT(toggle); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupFollow2) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(eq->name1, emu->name1, sizeof(eq->name1)); + memcpy(eq->name2, emu->name2, sizeof(eq->name2)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupInvite) + { + ENCODE_LENGTH_EXACT(GroupGeneric_Struct); + SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); + memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); + + FINISH_ENCODE(); + } + + ENCODE(OP_GroupUpdate) + { + //_log(NET__ERROR, "OP_GroupUpdate"); + EQApplicationPacket *in = *p; + GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; + + //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); + if ((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + { + if ((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) + { + //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); + dest->FastQueuePacket(&outapp); + + // Make an empty GLAA packet to clear out their useable GLAAs + // + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + //if(gjs->action == groupActLeave) + // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); + + structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; + memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); + memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + delete in; + return; + } + + if (in->size == sizeof(GroupUpdate2_Struct)) + { + // Group Update2 + //_log(NET__ERROR, "Struct is GroupUpdate2"); + + unsigned char *__emu_buffer = in->pBuffer; + GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*)__emu_buffer; + + //_log(NET__ERROR, "Yourname is %s", gu2->yourname); + + int MemberCount = 1; + int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; + + for (int i = 0; i < 5; ++i) + { + //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); + if (gu2->membername[i][0] != '\0') + { + PacketLength += (22 + strlen(gu2->membername[i]) + 1); + ++MemberCount; + } + } + + //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); + + char *Buffer = (char *)outapp->pBuffer; + + // Header + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); + + // Leader + // + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + + int MemberNumber = 1; + + for (int i = 0; i < 5; ++i) + { + if (gu2->membername[i][0] == '\0') + continue; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); + VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + //VARSTRUCT_ENCODE_STRING(Buffer, ""); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = gu2->NPCMarkerID; + memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); + + dest->FastQueuePacket(&outapp); + delete in; + return; } + //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); + ENCODE_LENGTH_EXACT(GroupJoin_Struct); + SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); + + memcpy(eq->membername, emu->membername, sizeof(eq->membername)); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); + GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; + + GLAAus->NPCMarkerID = emu->NPCMarkerID; + + memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); + //_hex(NET__ERROR, __packet->pBuffer, __packet->size); + + FINISH_ENCODE(); + + dest->FastQueuePacket(&outapp); } - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); - delete[] __emu_buffer; - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_Barter) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - - if(SubAction != Barter_BuyerAppearance) + ENCODE(OP_GuildMemberList) { - dest->FastQueuePacket(&in, ack_req); - - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = 80; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - char Name[64]; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction); - uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID); - uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - VARSTRUCT_DECODE_STRING(Name, Buffer); - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - OutBuffer = (char *)in->pBuffer + 72; - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); - -} - -ENCODE(OP_BazaarSearch) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *Buffer = (char *)in->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if(SubAction != BazaarSearchResults) - { - dest->FastQueuePacket(&in, ack_req); - - return; - } - - unsigned char *__emu_buffer = in->pBuffer; - - BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *) __emu_buffer; - - int EntryCount = in->size / sizeof(BazaarSearchResults_Struct); - - if(EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) - { - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct)); - delete in; - return; - } - in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct); - - in->pBuffer = new unsigned char[in->size]; - - memset(in->pBuffer, 0, in->size); - - structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer; - - for(int i = 0; i < EntryCount; ++i, ++emu, ++eq) - { - OUT(Beginning.Action); - OUT(SellerID); - memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName)); - OUT(NumItems); - OUT(ItemID); - OUT(SerialNumber); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(Cost); - OUT(ItemStat); - } - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneEntry){ ENCODE_FORWARD(OP_ZoneSpawns); } -ENCODE(OP_ZoneSpawns) { //consume the packet EQApplicationPacket *in = *p; *p = nullptr; //store away the emu struct unsigned char *__emu_buffer = in->pBuffer; - Spawn_Struct *emu = (Spawn_Struct *) __emu_buffer; + Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *)in->pBuffer; + + //make a new EQ buffer. + uint32 pnl = strlen(emu->player_name); + uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + + emu->count*sizeof(structs::GuildMemberEntry_Struct) + + emu->name_length + emu->note_length; + in->pBuffer = new uint8[length]; + in->size = length; + //no memset since we fill every byte. + + uint8 *buffer; + buffer = in->pBuffer; + + //easier way to setup GuildMembers_Struct + //set prefix name + strcpy((char *)buffer, emu->player_name); + buffer += pnl; + *buffer = '\0'; + buffer++; + + //add member count. + *((uint32 *)buffer) = htonl(emu->count); + buffer += sizeof(uint32); + + if (emu->count > 0) { + Internal_GuildMemberEntry_Struct *emu_e = emu->member; + const char *emu_name = (const char *)(__emu_buffer + + sizeof(Internal_GuildMembers_Struct)+ //skip header + emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data + ); + const char *emu_note = (emu_name + + emu->name_length + //skip name contents + emu->count //skip string terminators + ); + + structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; + + uint32 r; + for (r = 0; r < emu->count; r++, emu_e++) { + + //the order we set things here must match the struct + + //nice helper macro + /*#define SlideStructString(field, str) \ + strcpy(e->field, str.c_str()); \ + e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ +#define SlideStructString(field, str) \ + { \ + int sl = strlen(str); \ + memcpy(e->field, str, sl+1); \ + e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ + str += sl + 1; \ + } +#define PutFieldN(field) e->field = htonl(emu_e->field) + + SlideStructString(name, emu_name); + PutFieldN(level); + PutFieldN(banker); + PutFieldN(class_); + PutFieldN(rank); + PutFieldN(time_last_on); + PutFieldN(tribute_enable); + PutFieldN(total_tribute); + PutFieldN(last_tribute); + e->unknown_one = htonl(1); + SlideStructString(public_note, emu_note); + e->zoneinstance = 0; + e->zone_id = htons(emu_e->zone_id); + +#undef SlideStructString +#undef PutFieldN + + e++; + } + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_GuildsList) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + uint32 NumberOfGuilds = in->size / 64; + uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. + + unsigned char *__emu_buffer = in->pBuffer; + char *InBuffer = (char *)__emu_buffer; + uint32 HighestGuildID = 0; + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + PacketSize += (5 + strlen(InBuffer)); + HighestGuildID = i - 1; + } + InBuffer += 64; + } + + PacketSize++; // Appears to be an extra 0x00 at the very end. + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + InBuffer = (char *)__emu_buffer; + char *OutBuffer = (char *)in->pBuffer; + + // Init the first 64 bytes to zero, as per live. + // + memset(OutBuffer, 0, 64); + OutBuffer += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); + + for (unsigned int i = 0; i < NumberOfGuilds; ++i) + { + if (InBuffer[0]) + { + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); + VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); + } + InBuffer += 64; + } + + VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Illusion) + { + ENCODE_LENGTH_EXACT(Illusion_Struct); + SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); + + OUT(spawnid); + OUT_str(charname); + OUT(race); + OUT(unknown006[0]); + OUT(unknown006[1]); + OUT(gender); + OUT(texture); + OUT(helmtexture); + OUT(face); + OUT(hairstyle); + OUT(haircolor); + OUT(beard); + OUT(beardcolor); + OUT(size); + OUT(drakkin_heritage); + OUT(drakkin_tattoo); + OUT(drakkin_details); + + FINISH_ENCODE(); + } + + ENCODE(OP_InspectBuffs) + { + ENCODE_LENGTH_EXACT(InspectBuffs_Struct); + SETUP_DIRECT_ENCODE(InspectBuffs_Struct, structs::InspectBuffs_Struct); + + // we go over the internal 25 instead of the packet's since no entry is 0, which it will be already + for (int i = 0; i < BUFF_COUNT; i++) { + OUT(spell_id[i]); + OUT(tics_remaining[i]); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_InspectRequest) + { + ENCODE_LENGTH_EXACT(Inspect_Struct); + SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); + + OUT(TargetID); + OUT(PlayerID); + + FINISH_ENCODE(); + } + + ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } + + ENCODE(OP_ItemPacket) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *)__emu_buffer; + InternalSerializedItem_Struct *int_struct = (InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); + + uint32 length; + char *serialized = SerializeItem((ItemInst *)int_struct->inst, int_struct->slot_id, &length, 0); + + if (!serialized) { + _log(NET__STRUCTS, "Serialization failed on item slot %d.", int_struct->slot_id); + delete in; + return; + } + in->size = length + 4; + in->pBuffer = new unsigned char[in->size]; + ItemPacket_Struct *new_item_pkt = (ItemPacket_Struct *)in->pBuffer; + new_item_pkt->PacketType = old_item_pkt->PacketType; + memcpy(new_item_pkt->SerializedItem, serialized, length); + + delete[] __emu_buffer; + safe_delete_array(serialized); + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_ItemVerifyReply) + { + ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); + SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); + + eq->slot = ServerToUnderfootSlot(emu->slot); + OUT(spell); + OUT(target); + + FINISH_ENCODE(); + } + + ENCODE(OP_LeadershipExpUpdate) + { + SETUP_DIRECT_ENCODE(LeadershipExpUpdate_Struct, structs::LeadershipExpUpdate_Struct); + + OUT(group_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_exp); + OUT(raid_leadership_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_LogServer) + { + ENCODE_LENGTH_EXACT(LogServer_Struct); + SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); + + strcpy(eq->worldshortname, emu->worldshortname); + + OUT(enablevoicemacros); + OUT(enablemail); + OUT(enable_pvp); + OUT(enable_FV); + + eq->unknown016 = 1; + eq->unknown020[0] = 1; + + // These next two need to be set like this for the Tutorial Button to work. + eq->unknown263[0] = 0; + eq->unknown263[2] = 1; + eq->unknown263[4] = 1; + eq->unknown263[5] = 1; + eq->unknown263[6] = 1; + eq->unknown263[9] = 8; + eq->unknown263[19] = 0x80; + eq->unknown263[20] = 0x3f; + eq->unknown263[23] = 0x80; + eq->unknown263[24] = 0x3f; + eq->unknown263[33] = 1; + + FINISH_ENCODE(); + } + + ENCODE(OP_LootItem) + { + ENCODE_LENGTH_EXACT(LootingItem_Struct); + SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); + + OUT(lootee); + OUT(looter); + eq->slot_id = ServerToUnderFootCorpseSlot(emu->slot_id); + OUT(auto_loot); + + FINISH_ENCODE(); + } + + ENCODE(OP_ManaChange) + { + ENCODE_LENGTH_EXACT(ManaChange_Struct); + SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); + + OUT(new_mana); + OUT(stamina); + OUT(spell_id); + eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? + + FINISH_ENCODE(); + } + + ENCODE(OP_MercenaryDataResponse) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; + PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); + + for (r = 0; r < emu->MercTypeCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); + } + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); + for (k = 0; k < emu->Mercs[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); + } + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + // This packet does not appear to exist in UF, but leaving it here just in case + ENCODE(OP_MercenaryDataUpdate) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *)__emu_buffer; + + char *Buffer = (char *)in->pBuffer; + + EQApplicationPacket *outapp; + + uint32 PacketSize = 0; + + // There are 2 different sized versions of this packet depending if a merc is hired or not + if (emu->MercStatus >= 0) + { + PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; + + uint32 r; + uint32 k; + for (r = 0; r < emu->MercCount; r++) + { + PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; + } + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + + for (r = 0; r < emu->MercCount; r++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); + for (k = 0; k < emu->MercData[r].StanceCount; k++) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); + } + } + else + { + PacketSize += sizeof(structs::NoMercenaryHired_Struct); + + outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); + Buffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); + } + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_MoveItem) + { + ENCODE_LENGTH_EXACT(MoveItem_Struct); + SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); + + eq->from_slot = ServerToUnderfootSlot(emu->from_slot); + eq->to_slot = ServerToUnderfootSlot(emu->to_slot); + OUT(number_in_stack); + + FINISH_ENCODE(); + } + + ENCODE(OP_NewSpawn) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_NewZone) + { + SETUP_DIRECT_ENCODE(NewZone_Struct, structs::NewZone_Struct); + + OUT_str(char_name); + OUT_str(zone_short_name); + OUT_str(zone_long_name); + OUT(ztype); + int r; + for (r = 0; r < 4; r++) { + OUT(fog_red[r]); + OUT(fog_green[r]); + OUT(fog_blue[r]); + OUT(fog_minclip[r]); + OUT(fog_maxclip[r]); + } + OUT(gravity); + OUT(time_type); + for (r = 0; r < 4; r++) { + OUT(rain_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(rain_duration[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_chance[r]); + } + for (r = 0; r < 4; r++) { + OUT(snow_duration[r]); + } + for (r = 0; r < 32; r++) { + eq->unknown537[r] = 0xFF; //observed + } + OUT(sky); + OUT(zone_exp_multiplier); + OUT(safe_y); + OUT(safe_x); + OUT(safe_z); + OUT(max_z); + OUT(underworld); + OUT(minclip); + OUT(maxclip); + OUT_str(zone_short_name2); + OUT(zone_id); + OUT(zone_instance); + OUT(SuspendBuffs); + + eq->FogDensity = emu->fog_density; + + /*fill in some unknowns with observed values, hopefully it will help */ + eq->unknown800 = -1; + eq->unknown844 = 600; + eq->unknown880 = 50; + eq->unknown884 = 10; + eq->unknown888 = 1; + eq->unknown889 = 0; + eq->unknown890 = 1; + eq->unknown891 = 0; + eq->unknown892 = 0; + eq->unknown893 = 0; + eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off + eq->unknown895 = 0; + eq->unknown896 = 180; + eq->unknown900 = 180; + eq->unknown904 = 180; + eq->unknown908 = 2; + eq->unknown912 = 2; + + FINISH_ENCODE(); + } + + ENCODE(OP_OnLevelMessage) + { + ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); + SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); + + memcpy(eq->Title, emu->Title, sizeof(eq->Title)); + memcpy(eq->Text, emu->Text, sizeof(eq->Text)); + OUT(Buttons); + OUT(Duration); + OUT(PopupID); + OUT(NegativeID); + // These two field names are used if Buttons == 1. + OUT_str(ButtonName0); + OUT_str(ButtonName1); + + FINISH_ENCODE(); + } + + ENCODE(OP_OpenNewTasksWindow) + { + AvailableTaskHeader_Struct* __emu_AvailableTaskHeader; + AvailableTaskData1_Struct* __emu_AvailableTaskData1; + AvailableTaskData2_Struct* __emu_AvailableTaskData2; + AvailableTaskTrailer_Struct* __emu_AvailableTaskTrailer; + + structs::AvailableTaskHeader_Struct* __eq_AvailableTaskHeader; + structs::AvailableTaskData1_Struct* __eq_AvailableTaskData1; + structs::AvailableTaskData2_Struct* __eq_AvailableTaskData2; + structs::AvailableTaskTrailer_Struct* __eq_AvailableTaskTrailer; + + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + __emu_AvailableTaskHeader = (AvailableTaskHeader_Struct*)__emu_buffer; + + // For each task, SoF has an extra uint32 and what appears to be space for a null terminated string. + // + in->size = in->size + (__emu_AvailableTaskHeader->TaskCount * 5); + in->pBuffer = new unsigned char[in->size]; + + unsigned char *__eq_buffer = in->pBuffer; + + __eq_AvailableTaskHeader = (structs::AvailableTaskHeader_Struct*)__eq_buffer; + + char *__eq_ptr, *__emu_Ptr; + + // Copy Header + // + // + + __eq_AvailableTaskHeader->TaskCount = __emu_AvailableTaskHeader->TaskCount; + __eq_AvailableTaskHeader->unknown1 = __emu_AvailableTaskHeader->unknown1; + __eq_AvailableTaskHeader->TaskGiver = __emu_AvailableTaskHeader->TaskGiver; + + __emu_Ptr = (char *)__emu_AvailableTaskHeader + sizeof(AvailableTaskHeader_Struct); + __eq_ptr = (char *)__eq_AvailableTaskHeader + sizeof(structs::AvailableTaskHeader_Struct); + + for (uint32 i = 0; i<__emu_AvailableTaskHeader->TaskCount; i++) { + + __emu_AvailableTaskData1 = (AvailableTaskData1_Struct*)__emu_Ptr; + __eq_AvailableTaskData1 = (structs::AvailableTaskData1_Struct*)__eq_ptr; + + __eq_AvailableTaskData1->TaskID = __emu_AvailableTaskData1->TaskID; + // This next unknown seems to affect the colour of the task title. 0x3f80000 is what I have seen + // in Underfoot packets. Changing it to 0x3f000000 makes the title red. + __eq_AvailableTaskData1->unknown1 = 0x3f800000; + __eq_AvailableTaskData1->TimeLimit = __emu_AvailableTaskData1->TimeLimit; + __eq_AvailableTaskData1->unknown2 = __emu_AvailableTaskData1->unknown2; + + __emu_Ptr += sizeof(AvailableTaskData1_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData1_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Title + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Description + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __eq_ptr[0] = 0; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskData2 = (AvailableTaskData2_Struct*)__emu_Ptr; + __eq_AvailableTaskData2 = (structs::AvailableTaskData2_Struct*)__eq_ptr; + + __eq_AvailableTaskData2->unknown1 = __emu_AvailableTaskData2->unknown1; + __eq_AvailableTaskData2->unknown2 = __emu_AvailableTaskData2->unknown2; + __eq_AvailableTaskData2->unknown3 = __emu_AvailableTaskData2->unknown3; + __eq_AvailableTaskData2->unknown4 = __emu_AvailableTaskData2->unknown4; + + __emu_Ptr += sizeof(AvailableTaskData2_Struct); + __eq_ptr += sizeof(structs::AvailableTaskData2_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + + __emu_AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)__emu_Ptr; + __eq_AvailableTaskTrailer = (structs::AvailableTaskTrailer_Struct*)__eq_ptr; + + __eq_AvailableTaskTrailer->ItemCount = __emu_AvailableTaskTrailer->ItemCount; + __eq_AvailableTaskTrailer->unknown1 = __emu_AvailableTaskTrailer->unknown1; + __eq_AvailableTaskTrailer->unknown2 = __emu_AvailableTaskTrailer->unknown2; + __eq_AvailableTaskTrailer->StartZone = __emu_AvailableTaskTrailer->StartZone; + + __emu_Ptr += sizeof(AvailableTaskTrailer_Struct); + __eq_ptr += sizeof(structs::AvailableTaskTrailer_Struct); + + strcpy(__eq_ptr, __emu_Ptr); // Unknown string + + __emu_Ptr += strlen(__emu_Ptr) + 1; + __eq_ptr += strlen(__eq_ptr) + 1; + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PetBuffWindow) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + + PetBuff_Struct *emu = (PetBuff_Struct *)__emu_buffer; + + int PacketSize = 12 + (emu->buffcount * 17); + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petid); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, emu->buffcount); + + for (unsigned int i = 0; i < BUFF_COUNT; ++i) + { + if (emu->spellid[i]) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, i); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spellid[i]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->ticsremaining[i]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string. Name of the caster of the buff. + } + } + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->buffcount); + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_PlayerProfile) + { + SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); + + uint32 r; + + eq->available_slots = 0xffffffff; + memset(eq->unknown07284, 0xff, sizeof(eq->unknown07284)); + + // OUT(checksum); + OUT(gender); + OUT(race); + OUT(class_); + // OUT(unknown00016); + OUT(level); + eq->level1 = emu->level; + // OUT(unknown00022[2]); + for (r = 0; r < 5; r++) { + OUT(binds[r].zoneId); + OUT(binds[r].x); + OUT(binds[r].y); + OUT(binds[r].z); + OUT(binds[r].heading); + } + OUT(deity); + OUT(intoxication); + OUT_array(spellSlotRefresh, structs::MAX_PP_MEMSPELL); + OUT(abilitySlotRefresh); + OUT(points); // Relocation Test + // OUT(unknown0166[4]); + OUT(haircolor); + OUT(beardcolor); + OUT(eyecolor1); + OUT(eyecolor2); + OUT(hairstyle); + OUT(beard); + // OUT(unknown00178[10]); + for (r = 0; r < 9; r++) { + eq->equipment[r].equip0 = emu->item_material[r]; + eq->equipment[r].equip1 = 0; + eq->equipment[r].itemId = 0; + //eq->colors[r].color = emu->colors[r].color; + } + for (r = 0; r < 7; r++) { + OUT(item_tint[r].color); + } + // OUT(unknown00224[48]); + //NOTE: new client supports 300 AAs, our internal rep/PP + //only supports 240.. + for (r = 0; r < MAX_PP_AA_ARRAY; r++) { + OUT(aa_array[r].AA); + OUT(aa_array[r].value); + } + // OUT(unknown02220[4]); + OUT(mana); + OUT(cur_hp); + OUT(STR); + OUT(STA); + OUT(CHA); + OUT(AGI); + OUT(INT); + OUT(DEX); + OUT(WIS); + OUT(face); + // OUT(unknown02264[47]); + memset(eq->spell_book, 0xFF, sizeof(uint32)* structs::MAX_PP_SPELLBOOK); + OUT_array(spell_book, 480U); + // OUT(unknown4184[128]); + OUT_array(mem_spells, structs::MAX_PP_MEMSPELL); + // OUT(unknown04396[32]); + OUT(platinum); + OUT(gold); + OUT(silver); + OUT(copper); + OUT(platinum_cursor); + OUT(gold_cursor); + OUT(silver_cursor); + OUT(copper_cursor); + + OUT_array(skills, structs::MAX_PP_SKILL); // 1:1 direct copy (100 dword) + + // OUT(unknown04760[236]); + OUT(toxicity); + OUT(thirst_level); + OUT(hunger_level); + //PS this needs to be figured out more; but it was 'good enough' + for (r = 0; r < structs::BUFF_COUNT; r++) + { + if (emu->buffs[r].spellid != 0xFFFF && emu->buffs[r].spellid != 0) + { + eq->buffs[r].unknown004 = 0x3f800000; + eq->buffs[r].slotid = 2; + eq->buffs[r].player_id = 0x000717fd; + } + else + { + eq->buffs[r].slotid = 0; + } + //OUT(buffs[r].slotid); + OUT(buffs[r].level); + //OUT(buffs[r].bard_modifier); + //OUT(buffs[r].effect); + OUT(buffs[r].spellid); + OUT(buffs[r].duration); + OUT(buffs[r].counters); + //OUT(buffs[r].player_id); + } + for (r = 0; r < MAX_PP_DISCIPLINES; r++) { + OUT(disciplines.values[r]); + } + OUT_array(recastTimers, structs::MAX_RECAST_TYPES); + // OUT(unknown08124[360]); + OUT(endurance); + OUT(aapoints_spent); + OUT(aapoints); + // OUT(unknown06160[4]); + //NOTE: new client supports 20 bandoliers, our internal rep + //only supports 4.. + for (r = 0; r < 4; r++) { + OUT_str(bandoliers[r].name); + uint32 k; + for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) { + OUT(bandoliers[r].items[k].item_id); + OUT(bandoliers[r].items[k].icon); + OUT_str(bandoliers[r].items[k].item_name); + } + } + // OUT(unknown07444[5120]); + for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) { + OUT(potionbelt.items[r].item_id); + OUT(potionbelt.items[r].icon); + OUT_str(potionbelt.items[r].item_name); + } + // OUT(unknown12852[8]); + // OUT(unknown12864[76]); + OUT_str(name); + OUT_str(last_name); + OUT(guild_id); + OUT(birthday); + OUT(lastlogin); + OUT(timePlayedMin); + OUT(pvp); + OUT(anon); + OUT(gm); + OUT(guildrank); + OUT(guildbanker); + // OUT(unknown13054[12]); + OUT(exp); + // OUT(unknown13072[8]); + OUT(timeentitledonaccount); + OUT_array(languages, structs::MAX_PP_LANGUAGE); + // OUT(unknown13109[7]); + OUT(y); //reversed x and y + OUT(x); + OUT(z); + OUT(heading); + // OUT(unknown13132[4]); + OUT(platinum_bank); + OUT(gold_bank); + OUT(silver_bank); + OUT(copper_bank); + OUT(platinum_shared); + // OUT(unknown13156[84]); + //OUT(expansions); + eq->expansions = 0xffff; + // OUT(unknown13244[12]); + OUT(autosplit); + // OUT(unknown13260[16]); + OUT(zone_id); + OUT(zoneInstance); + for (r = 0; r < structs::MAX_GROUP_MEMBERS; r++) { + OUT_str(groupMembers[r]); + } + strcpy(eq->groupLeader, emu->groupMembers[0]); + // OUT_str(groupLeader); + // OUT(unknown13728[660]); + OUT(entityid); + OUT(leadAAActive); + // OUT(unknown14392[4]); + OUT(ldon_points_guk); + OUT(ldon_points_mir); + OUT(ldon_points_mmc); + OUT(ldon_points_ruj); + OUT(ldon_points_tak); + OUT(ldon_points_available); + // OUT(unknown14420[132]); + OUT(tribute_time_remaining); + OUT(career_tribute_points); + // OUT(unknown7208); + OUT(tribute_points); + // OUT(unknown7216); + OUT(tribute_active); + for (r = 0; r < structs::MAX_PLAYER_TRIBUTES; r++) { + OUT(tributes[r].tribute); + OUT(tributes[r].tier); + } + // OUT(unknown14616[8]); + OUT(group_leadership_exp); + // OUT(unknown14628); + OUT(raid_leadership_exp); + OUT(group_leadership_points); + OUT(raid_leadership_points); + OUT_array(leader_abilities.ranks, structs::MAX_LEADERSHIP_AA_ARRAY); + // OUT(unknown14772[128]); + OUT(air_remaining); + OUT(PVPKills); + OUT(PVPDeaths); + OUT(PVPCurrentPoints); + OUT(PVPCareerPoints); + OUT(PVPBestKillStreak); + OUT(PVPWorstDeathStreak); + OUT(PVPCurrentKillStreak); + // OUT(unknown17892[4580]); + OUT(expAA); + // OUT(unknown19516[40]); + OUT(currentRadCrystals); + OUT(careerRadCrystals); + OUT(currentEbonCrystals); + OUT(careerEbonCrystals); + OUT(groupAutoconsent); + OUT(raidAutoconsent); + OUT(guildAutoconsent); + // OUT(unknown19575[5]); + eq->level3 = emu->level; + eq->showhelm = emu->showhelm; + OUT(RestTimer); + // OUT(unknown19584[4]); + // OUT(unknown19588); + + const uint8 bytes[] = { + 0xa3, 0x02, 0x00, 0x00, 0x95, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x19, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x1F, 0x85, 0xEB, 0x3E, 0x33, 0x33, 0x33, 0x3F, + 0x04, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + memcpy(eq->unknown18020, bytes, sizeof(bytes)); + + //set the checksum... + CRC32::SetEQChecksum(__packet->pBuffer, sizeof(structs::PlayerProfile_Struct) - 4); + + FINISH_ENCODE(); + } + + ENCODE(OP_RaidJoin) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; + + general->action = 8; + general->parameter = 1; + strn0cpy(general->leader_name, raid_create->leader_name, 64); + strn0cpy(general->player_name, raid_create->leader_name, 64); + + dest->FastQueuePacket(&outapp_create); + delete[] __emu_buffer; + } + + ENCODE(OP_RaidUpdate) + { + EQApplicationPacket *inapp = *p; + *p = nullptr; + unsigned char * __emu_buffer = inapp->pBuffer; + RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; + + if (raid_gen->action == 0) // raid add has longer length than other raid updates + { + RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); + structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + + add_member->raidGen.action = in_add_member->raidGen.action; + add_member->raidGen.parameter = in_add_member->raidGen.parameter; + strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); + strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); + add_member->_class = in_add_member->_class; + add_member->level = in_add_member->level; + add_member->isGroupLeader = in_add_member->isGroupLeader; + add_member->flags[0] = in_add_member->flags[0]; + add_member->flags[1] = in_add_member->flags[1]; + add_member->flags[2] = in_add_member->flags[2]; + add_member->flags[3] = in_add_member->flags[3]; + add_member->flags[4] = in_add_member->flags[4]; + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 35) + { + RaidMOTD_Struct *inmotd = (RaidMOTD_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidMOTD_Struct) + strlen(inmotd->motd) + 1); + structs::RaidMOTD_Struct *outmotd = (structs::RaidMOTD_Struct *)outapp->pBuffer; + + outmotd->general.action = inmotd->general.action; + strn0cpy(outmotd->general.player_name, inmotd->general.player_name, 64); + strn0cpy(outmotd->motd, inmotd->motd, strlen(inmotd->motd) + 1); + dest->FastQueuePacket(&outapp); + } + else if (raid_gen->action == 14 || raid_gen->action == 30) + { + RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; + + outlaa->action = inlaa->action; + strn0cpy(outlaa->player_name, inlaa->player_name, 64); + strn0cpy(outlaa->leader_name, inlaa->leader_name, 64); + memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); + dest->FastQueuePacket(&outapp); + } + else + { + RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); + structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); + strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); + raid_general->action = in_raid_general->action; + raid_general->parameter = in_raid_general->parameter; + dest->FastQueuePacket(&outapp); + } + + delete[] __emu_buffer; + } + + ENCODE(OP_ReadBook) + { + ENCODE_LENGTH_ATLEAST(BookText_Struct); + SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); + + if (emu->window == 0xFF) + eq->window = 0xFFFFFFFF; + else + eq->window = emu->window; + OUT(type); + eq->invslot = ServerToUnderfootSlot(emu->invslot); + strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); + + FINISH_ENCODE(); + } + + ENCODE(OP_RespondAA) + { + SETUP_DIRECT_ENCODE(AATable_Struct, structs::AATable_Struct); + + eq->aa_spent = emu->aa_spent; + eq->aa_assigned = emu->aa_spent; + eq->aa_spent3 = emu->aa_spent; + eq->unknown012 = 0; + eq->unknown016 = 0; + eq->unknown020 = 0; + + for (uint32 i = 0; i < MAX_PP_AA_ARRAY; ++i) + { + eq->aa_list[i].aa_skill = emu->aa_list[i].aa_skill; + eq->aa_list[i].aa_value = emu->aa_list[i].aa_value; + eq->aa_list[i].unknown08 = emu->aa_list[i].unknown08; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendAATable) + { + ENCODE_LENGTH_ATLEAST(SendAA_Struct); + SETUP_VAR_ENCODE(SendAA_Struct); + ALLOC_VAR_ENCODE(structs::SendAA_Struct, sizeof(structs::SendAA_Struct) + emu->total_abilities*sizeof(structs::AA_Ability)); + + // Check clientver field to verify this AA should be sent for SoF + // clientver 1 is for all clients and 6 is for Underfoot + if (emu->clientver <= 6) + { + OUT(id); + eq->unknown004 = 1; + //eq->hotkey_sid = (emu->hotkey_sid==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->hotkey_sid2 = (emu->hotkey_sid2==4294967295UL)?0:(emu->id - emu->current_level + 1); + //eq->title_sid = emu->id - emu->current_level + 1; + //eq->desc_sid = emu->id - emu->current_level + 1; + eq->hotkey_sid = (emu->hotkey_sid == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->hotkey_sid2 = (emu->hotkey_sid2 == 4294967295UL) ? 0 : (emu->sof_next_skill); + eq->title_sid = emu->sof_next_skill; + eq->desc_sid = emu->sof_next_skill; + OUT(class_type); + OUT(cost); + OUT(seq); + OUT(current_level); + OUT(prereq_skill); + OUT(prereq_minpoints); + eq->type = emu->sof_type; + OUT(spellid); + OUT(spell_type); + OUT(spell_refresh); + OUT(classes); + OUT(berserker); + //eq->max_level = emu->sof_max_level; + OUT(max_level); + OUT(last_id); + OUT(next_id); + OUT(cost2); + eq->aa_expansion = emu->aa_expansion; + eq->special_category = emu->special_category; + OUT(total_abilities); + unsigned int r; + for (r = 0; r < emu->total_abilities; r++) { + OUT(abilities[r].skill_id); + OUT(abilities[r].base1); + OUT(abilities[r].base2); + OUT(abilities[r].slot); + } + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendCharInfo) + { + ENCODE_LENGTH_EXACT(CharacterSelect_Struct); + SETUP_VAR_ENCODE(CharacterSelect_Struct); + + //EQApplicationPacket *packet = *p; + //const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer; + + int char_count; + int namelen = 0; + for (char_count = 0; char_count < 10; char_count++) { + if (emu->name[char_count][0] == '\0') + break; + if (strcmp(emu->name[char_count], "") == 0) + break; + namelen += strlen(emu->name[char_count]); + } + + int total_length = sizeof(structs::CharacterSelect_Struct) + + char_count * sizeof(structs::CharacterSelectEntry_Struct) + + namelen; + + ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, total_length); + + //unsigned char *eq_buffer = new unsigned char[total_length]; + //structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer; + + eq->char_count = char_count; + eq->total_chars = 10; + + unsigned char *bufptr = (unsigned char *)eq->entries; + int r; + for (r = 0; r < char_count; r++) { + { //pre-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->level = emu->level[r]; + eq2->hairstyle = emu->hairstyle[r]; + eq2->gender = emu->gender[r]; + memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1); + } + //adjust for name. + bufptr += strlen(emu->name[r]); + { //post-name section... + structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr; + eq2->beard = emu->beard[r]; + eq2->haircolor = emu->haircolor[r]; + eq2->face = emu->face[r]; + int k; + for (k = 0; k < _MaterialCount; k++) { + eq2->equip[k].equip0 = emu->equip[r][k]; + eq2->equip[k].equip1 = 0; + eq2->equip[k].itemid = 0; + eq2->equip[k].color.color = emu->cs_colors[r][k].color; + } + eq2->primary = emu->primary[r]; + eq2->secondary = emu->secondary[r]; + eq2->tutorial = emu->tutorial[r]; // was u15 + eq2->u15 = 0xff; + eq2->deity = emu->deity[r]; + eq2->zone = emu->zone[r]; + eq2->u19 = 0xFF; + eq2->race = emu->race[r]; + eq2->gohome = emu->gohome[r]; + eq2->class_ = emu->class_[r]; + eq2->eyecolor1 = emu->eyecolor1[r]; + eq2->beardcolor = emu->beardcolor[r]; + eq2->eyecolor2 = emu->eyecolor2[r]; + eq2->drakkin_heritage = emu->drakkin_heritage[r]; + eq2->drakkin_tattoo = emu->drakkin_tattoo[r]; + eq2->drakkin_details = emu->drakkin_details[r]; + } + + bufptr += sizeof(structs::CharacterSelectEntry_Struct); + } + + FINISH_ENCODE(); + } + + ENCODE(OP_SendZonepoints) + { + SETUP_VAR_ENCODE(ZonePoints); + ALLOC_VAR_ENCODE(structs::ZonePoints, sizeof(structs::ZonePoints) + sizeof(structs::ZonePoint_Entry) * (emu->count + 1)); + + eq->count = emu->count; + for (uint32 i = 0; i < emu->count; ++i) + { + eq->zpe[i].iterator = emu->zpe[i].iterator; + eq->zpe[i].x = emu->zpe[i].x; + eq->zpe[i].y = emu->zpe[i].y; + eq->zpe[i].z = emu->zpe[i].z; + eq->zpe[i].heading = emu->zpe[i].heading; + eq->zpe[i].zoneid = emu->zpe[i].zoneid; + eq->zpe[i].zoneinstance = emu->zpe[i].zoneinstance; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerBuy) + { + ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); + SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + OUT(npcid); + OUT(playerid); + OUT(itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_ShopPlayerSell) + { + ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); + SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + OUT(npcid); + eq->itemslot = ServerToUnderfootSlot(emu->itemslot); + OUT(quantity); + OUT(price); + + FINISH_ENCODE(); + } + + ENCODE(OP_SomeItemPacketMaybe) + { + // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow + // and flying to the target. + // + + ENCODE_LENGTH_EXACT(Arrow_Struct); + SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); + + OUT(src_y); + OUT(src_x); + OUT(src_z); + OUT(velocity); + OUT(launch_angle); + OUT(tilt); + OUT(arc); + OUT(source_id); + OUT(target_id); + OUT(item_id); + + eq->unknown070 = 135; // This needs to be set to something, else we get a 1HS animation instead of ranged. + + OUT(item_type); + OUT(skill); + + strcpy(eq->model_name, emu->model_name); + + FINISH_ENCODE(); + } + + ENCODE(OP_SpawnAppearance) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *emu_buffer = in->pBuffer; + + SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; + + if (sas->type != AT_Size) + { + dest->FastQueuePacket(&in, ack_req); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); + ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; + + css->EntityID = sas->spawn_id; + css->Size = (float)sas->parameter; + css->Unknown08 = 0; + css->Unknown12 = 1.0f; + + dest->FastQueuePacket(&outapp, ack_req); + delete in; + } + + ENCODE(OP_SpawnDoor) + { + SETUP_VAR_ENCODE(Door_Struct); + int door_count = __packet->size / sizeof(Door_Struct); + int total_length = door_count * sizeof(structs::Door_Struct); + ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); + + int r; + for (r = 0; r < door_count; r++) { + strcpy(eq[r].name, emu[r].name); + eq[r].xPos = emu[r].xPos; + eq[r].yPos = emu[r].yPos; + eq[r].zPos = emu[r].zPos; + eq[r].heading = emu[r].heading; + eq[r].incline = emu[r].incline; + eq[r].size = emu[r].size; + eq[r].doorId = emu[r].doorId; + eq[r].opentype = emu[r].opentype; + eq[r].state_at_spawn = emu[r].state_at_spawn; + eq[r].invert_state = emu[r].invert_state; + eq[r].door_param = emu[r].door_param; + eq[r].unknown0076 = 0; + eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0078 = 0; + eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors + eq[r].unknown0080 = 0; + eq[r].unknown0081 = 0; + eq[r].unknown0082 = 0; + } + + FINISH_ENCODE(); + } + + ENCODE(OP_Stun) + { + ENCODE_LENGTH_EXACT(Stun_Struct); + SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); + + OUT(duration); + eq->unknown005 = 163; + eq->unknown006 = 67; + + FINISH_ENCODE(); + } + + ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } + + ENCODE(OP_Track) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + unsigned char *__emu_buffer = in->pBuffer; + Track_Struct *emu = (Track_Struct *)__emu_buffer; + + int EntryCount = in->size / sizeof(Track_Struct); + + if (EntryCount == 0 || ((in->size % sizeof(Track_Struct))) != 0) + { + _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Track_Struct)); + delete in; + return; + } + + int PacketSize = 2; + + for (int i = 0; i < EntryCount; ++i, ++emu) + PacketSize += (12 + strlen(emu->name)); + + emu = (Track_Struct *)__emu_buffer; + + in->size = PacketSize; + in->pBuffer = new unsigned char[in->size]; + + char *Buffer = (char *)in->pBuffer; + + VARSTRUCT_ENCODE_TYPE(uint16, Buffer, EntryCount); + + for (int i = 0; i < EntryCount; ++i, ++emu) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->entityid); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->distance); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + VARSTRUCT_ENCODE_STRING(Buffer, emu->name); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->GroupMember); + } + + delete[] __emu_buffer; + dest->FastQueuePacket(&in, ack_req); + } + + ENCODE(OP_Trader) + { + if ((*p)->size != sizeof(TraderBuy_Struct)) { + EQApplicationPacket *in = *p; + *p = nullptr; + dest->FastQueuePacket(&in, ack_req); + return; + } + + ENCODE_FORWARD(OP_TraderBuy); + } + + ENCODE(OP_TraderBuy) + { + ENCODE_LENGTH_EXACT(TraderBuy_Struct); + SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); + + OUT(Action); + OUT(Price); + OUT(TraderID); + memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); + OUT(ItemID); + OUT(Quantity); + OUT(AlreadySold); + + FINISH_ENCODE(); + } + + ENCODE(OP_TributeItem) + { + ENCODE_LENGTH_EXACT(TributeItem_Struct); + SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); + + eq->slot = ServerToUnderfootSlot(emu->slot); + OUT(quantity); + OUT(tribute_master_id); + OUT(tribute_points); + + FINISH_ENCODE(); + } + + ENCODE(OP_VetRewardsAvaliable) + { + EQApplicationPacket *inapp = *p; + unsigned char * __emu_buffer = inapp->pBuffer; + + uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); + *p = nullptr; + + EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); + uchar *old_data = __emu_buffer; + uchar *data = outapp_create->pBuffer; + for (unsigned int i = 0; i < count; ++i) + { + structs::VeteranReward *vr = (structs::VeteranReward*)data; + InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; + + vr->claim_count = ivr->claim_count; + vr->claim_id = ivr->claim_id; + vr->number_available = ivr->number_available; + for (int x = 0; x < 8; ++x) + { + vr->items[x].item_id = ivr->items[x].item_id; + strcpy(vr->items[x].item_name, ivr->items[x].item_name); + vr->items[x].charges = ivr->items[x].charges; + } + + old_data += sizeof(InternalVeteranReward); + data += sizeof(structs::VeteranReward); + } + + dest->FastQueuePacket(&outapp_create); + delete inapp; + } + + ENCODE(OP_WearChange) + { + ENCODE_LENGTH_EXACT(WearChange_Struct); + SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); + + OUT(spawn_id); + OUT(material); + OUT(unknown06); + OUT(elite_material); + OUT(color.color); + OUT(wear_slot_id); + + FINISH_ENCODE(); + } + + ENCODE(OP_WhoAllResponse) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + char *InBuffer = (char *)in->pBuffer; + + WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; + + int Count = wars->playercount; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); + + char *OutBuffer = (char *)outapp->pBuffer; + + memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); + + OutBuffer += sizeof(WhoAllReturnStruct); + InBuffer += sizeof(WhoAllReturnStruct); + + for (int i = 0; i < Count; ++i) + { + uint32 x; + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + InBuffer += 4; + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); + + char Name[64]; + + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + for (int j = 0; j < 7; ++j) + { + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account + VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + + x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + } + + //_hex(NET__ERROR, outapp->pBuffer, outapp->size); + dest->FastQueuePacket(&outapp); + delete in; + } + + ENCODE(OP_ZoneEntry) { ENCODE_FORWARD(OP_ZoneSpawns); } + + ENCODE(OP_ZonePlayerToBind) + { + ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); + + ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; + structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; + unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; + structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; + + zph->x = zps->x; + zph->y = zps->y; + zph->z = zps->z; + zph->heading = zps->heading; + zph->bind_zone_id = zps->bind_zone_id; + zph->bind_instance_id = zps->bind_instance_id; + strcpy(zph->zone_name, zps->zone_name); + + zpf->unknown021 = 1; + zpf->unknown022 = 0; + zpf->unknown023 = 0; + zpf->unknown024 = 0; + + ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); + ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); + + delete[] buffer1; + delete[] buffer2; + delete[](*p)->pBuffer; + + (*p)->pBuffer = new unsigned char[ss.str().size()]; + (*p)->size = ss.str().size(); + + memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); + dest->FastQueuePacket(&(*p)); + } + + ENCODE(OP_ZoneServerInfo) + { + SETUP_DIRECT_ENCODE(ZoneServerInfo_Struct, ZoneServerInfo_Struct); + + OUT_str(ip); + OUT(port); + + FINISH_ENCODE(); + } + + ENCODE(OP_ZoneSpawns) + { + //consume the packet + EQApplicationPacket *in = *p; + *p = nullptr; + + //store away the emu struct + unsigned char *__emu_buffer = in->pBuffer; + Spawn_Struct *emu = (Spawn_Struct *)__emu_buffer; //determine and verify length int entrycount = in->size / sizeof(Spawn_Struct); - if(entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { + if (entrycount == 0 || (in->size % sizeof(Spawn_Struct)) != 0) { _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(Spawn_Struct)); delete in; return; } - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - emu = (Spawn_Struct *) __emu_buffer; + emu = (Spawn_Struct *)__emu_buffer; //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - char *Buffer = (char *) in->pBuffer; - + char *Buffer = (char *)in->pBuffer; int r; int k; - for(r = 0; r < entrycount; r++, emu++) { + for (r = 0; r < entrycount; r++, emu++) { int PacketSize = sizeof(structs::Spawn_Struct); PacketSize += strlen(emu->name); PacketSize += strlen(emu->lastName); - if(strlen(emu->title)) + if (strlen(emu->title)) PacketSize += strlen(emu->title) + 1; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) PacketSize += strlen(emu->suffix) + 1; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { PacketSize = PacketSize - 4; // No bodytype PacketSize += 53; // Fixed portion @@ -1022,7 +2600,7 @@ ENCODE(OP_ZoneSpawns) { } bool ShowName = 1; - if(emu->bodytype >= 66) + if (emu->bodytype >= 66) { emu->race = 127; emu->bodytype = 11; @@ -1031,30 +2609,30 @@ ENCODE(OP_ZoneSpawns) { } float SpawnSize = emu->size; - if(!((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) + if (!((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522))) { PacketSize -= (sizeof(structs::EquipStruct) * 9); - if(emu->size == 0) + if (emu->size == 0) { emu->size = 6; SpawnSize = 6; } } - if(SpawnSize == 0) + if (SpawnSize == 0) { SpawnSize = 3; } EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - Buffer = (char *) outapp->pBuffer; + Buffer = (char *)outapp->pBuffer; VARSTRUCT_ENCODE_STRING(Buffer, emu->name); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 } @@ -1085,10 +2663,10 @@ ENCODE(OP_ZoneSpawns) { Bitfields->showname = ShowName; - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); - Buffer = Buffer -4; + Buffer = Buffer - 4; } Bitfields->ispet = emu->is_pet; @@ -1097,18 +2675,18 @@ ENCODE(OP_ZoneSpawns) { uint8 OtherData = 0; - if(strlen(emu->title)) + if (strlen(emu->title)) OtherData = OtherData | 0x04; - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) OtherData = OtherData | 0x08; - if(emu->DestructibleObject) + if (emu->DestructibleObject) OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); } @@ -1118,7 +2696,7 @@ ENCODE(OP_ZoneSpawns) { } VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 - if(emu->DestructibleObject) + if (emu->DestructibleObject) { VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); @@ -1150,15 +2728,15 @@ ENCODE(OP_ZoneSpawns) { /* if(emu->bodytype >=66) { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname } else { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname }*/ - if(!emu->DestructibleObject) + if (!emu->DestructibleObject) { // Setting this next field to zero will cause a crash. Looking at ShowEQ, if it is zero, the bodytype field is not // present. Will sort that out later. @@ -1182,7 +2760,7 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // ShowEQ calls this 'Holding' VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); - if(emu->NPC) + if (emu->NPC) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); @@ -1213,7 +2791,6 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown18 VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown19 - structs::Spawn_Struct_Position *Position = (structs::Spawn_Struct_Position*)Buffer; Position->deltaX = emu->deltaX; @@ -1228,9 +2805,9 @@ ENCODE(OP_ZoneSpawns) { Buffer += sizeof(structs::Spawn_Struct_Position); - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu ->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { - for(k = 0; k < 9; ++k) + for (k = 0; k < 9; ++k) { { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); @@ -1252,12 +2829,11 @@ ENCODE(OP_ZoneSpawns) { VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); } - - if((emu->NPC == 0) || (emu->race <=12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) + if ((emu->NPC == 0) || (emu->race <= 12) || (emu->race == 128) || (emu->race == 130) || (emu->race == 330) || (emu->race == 522)) { structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - for(k = 0; k < 9; k++) { + for (k = 0; k < 9; k++) { Equipment[k].equip0 = emu->equipment[k]; Equipment[k].equip1 = 0; Equipment[k].itemId = 0; @@ -1265,12 +2841,12 @@ ENCODE(OP_ZoneSpawns) { Buffer += (sizeof(structs::EquipStruct) * 9); } - if(strlen(emu->title)) + if (strlen(emu->title)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->title); } - if(strlen(emu->suffix)) + if (strlen(emu->suffix)) { VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); } @@ -1280,2083 +2856,499 @@ ENCODE(OP_ZoneSpawns) { Buffer += 28; // Unknown; dest->FastQueuePacket(&outapp, ack_req); - } - - delete in; -} - -ENCODE(OP_MercenaryDataResponse) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryMerchantList_Struct *emu = (MercenaryMerchantList_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - int PacketSize = sizeof(structs::MercenaryMerchantList_Struct) - 4 + emu->MercTypeCount * 4; - PacketSize += (sizeof(structs::MercenaryListEntry_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->Mercs[r].StanceCount; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercTypeCount); - - for(r = 0; r < emu->MercTypeCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercGrades[r]); - } - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->Mercs[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->Mercs[r].MercUnk04); - for(k = 0; k < emu->Mercs[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->Mercs[r].Stances[k].Stance); } - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -// This packet does not appear to exist in UF, but leaving it here just in case -ENCODE(OP_MercenaryDataUpdate) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - MercenaryDataUpdate_Struct *emu = (MercenaryDataUpdate_Struct *) __emu_buffer; - - char *Buffer = (char *) in->pBuffer; - - EQApplicationPacket *outapp; - - uint32 PacketSize = 0; - - // There are 2 different sized versions of this packet depending if a merc is hired or not - if (emu->MercStatus >= 0) - { - PacketSize += sizeof(structs::MercenaryDataUpdate_Struct) + (sizeof(structs::MercenaryData_Struct) - sizeof(structs::MercenaryStance_Struct)) * emu->MercCount; - - uint32 r; - uint32 k; - for(r = 0; r < emu->MercCount; r++) - { - PacketSize += sizeof(structs::MercenaryStance_Struct) * emu->MercData[r].StanceCount; - } - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - - for(r = 0; r < emu->MercCount; r++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercSubType); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].PurchaseCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].UpkeepCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyCost); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyUpkeep); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].AltCurrencyType); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk01); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].TimeLeft); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MerchantSlot); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk02); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].StanceCount); - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercData[r].MercUnk03); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->MercData[r].MercUnk04); - for(k = 0; k < emu->MercData[r].StanceCount; k++) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].StanceIndex); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].Stances[k].Stance); - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercData[r].MercUnk05); - } - } - else - { - PacketSize += sizeof(structs::NoMercenaryHired_Struct); - - outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, PacketSize); - Buffer = (char *) outapp->pBuffer; - - VARSTRUCT_ENCODE_TYPE(int32, Buffer, emu->MercStatus); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->MercCount); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 1); - } - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_ItemLinkResponse) { ENCODE_FORWARD(OP_ItemPacket); } -ENCODE(OP_ItemPacket) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *__emu_buffer = in->pBuffer; - ItemPacket_Struct *old_item_pkt=(ItemPacket_Struct *)__emu_buffer; - InternalSerializedItem_Struct *int_struct=(InternalSerializedItem_Struct *)(old_item_pkt->SerializedItem); - - uint32 length; - char *serialized=SerializeItem((ItemInst *)int_struct->inst,int_struct->slot_id,&length,0); - - if (!serialized) { - _log(NET__STRUCTS, "Serialization failed on item slot %d.",int_struct->slot_id); - delete in; - return; - } - in->size = length+4; - in->pBuffer = new unsigned char[in->size]; - ItemPacket_Struct *new_item_pkt=(ItemPacket_Struct *)in->pBuffer; - new_item_pkt->PacketType=old_item_pkt->PacketType; - memcpy(new_item_pkt->SerializedItem,serialized,length); - - delete[] __emu_buffer; - safe_delete_array(serialized); - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_CharInventory) { - //consume the packet - EQApplicationPacket *in = *p; - - *p = nullptr; - - if(in->size == 0) { - - in->size = 4; - - in->pBuffer = new uchar[in->size]; - - *((uint32 *) in->pBuffer) = 0; - - dest->FastQueuePacket(&in, ack_req); - - return; - } - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - - int ItemCount = in->size / sizeof(InternalSerializedItem_Struct); - - if(ItemCount == 0 || (in->size % sizeof(InternalSerializedItem_Struct)) != 0) { - - _log(NET__STRUCTS, "Wrong size on outbound %s: Got %d, expected multiple of %d", - opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(InternalSerializedItem_Struct)); delete in; - - return; } - InternalSerializedItem_Struct *eq = (InternalSerializedItem_Struct *) in->pBuffer; - - in->pBuffer = new uchar[4]; - - *(uint32 *)in->pBuffer = ItemCount; - - in->size = 4; - - for(int r = 0; r < ItemCount; r++, eq++) { - - uint32 Length = 0; - - char* Serialized = SerializeItem((const ItemInst*)eq->inst, eq->slot_id, &Length, 0); - - if(Serialized) { - - uchar *OldBuffer = in->pBuffer; - - in->pBuffer = new uchar[in->size + Length]; - - memcpy(in->pBuffer, OldBuffer, in->size); - - safe_delete_array(OldBuffer); - - memcpy(in->pBuffer + in->size, Serialized, Length); - - in->size += Length; - - safe_delete_array(Serialized); - - } - else { - _log(NET__ERROR, "Serialization failed on item slot %d during OP_CharInventory. Item skipped.",eq->slot_id); - } - } - - delete[] __emu_buffer; - - //_log(NET__ERROR, "Sending inventory to client"); - - //_hex(NET__ERROR, in->pBuffer, in->size); - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildMemberList) { - //consume the packet - EQApplicationPacket *in = *p; - *p = nullptr; - - //store away the emu struct - unsigned char *__emu_buffer = in->pBuffer; - Internal_GuildMembers_Struct *emu = (Internal_GuildMembers_Struct *) in->pBuffer; - - - - //make a new EQ buffer. - uint32 pnl = strlen(emu->player_name); - uint32 length = sizeof(structs::GuildMembers_Struct) + pnl + - emu->count*sizeof(structs::GuildMemberEntry_Struct) - + emu->name_length + emu->note_length; - in->pBuffer = new uint8[length]; - in->size = length; - //no memset since we fill every byte. - - uint8 *buffer; - buffer = in->pBuffer; - - //easier way to setup GuildMembers_Struct - //set prefix name - strcpy((char *)buffer, emu->player_name); - buffer += pnl; - *buffer = '\0'; - buffer++; - - //add member count. - *((uint32 *) buffer) = htonl( emu->count ); - buffer += sizeof(uint32); - - if(emu->count > 0) { - Internal_GuildMemberEntry_Struct *emu_e = emu->member; - const char *emu_name = (const char *) (__emu_buffer + - sizeof(Internal_GuildMembers_Struct) + //skip header - emu->count * sizeof(Internal_GuildMemberEntry_Struct) //skip static length member data - ); - const char *emu_note = (emu_name + - emu->name_length + //skip name contents - emu->count //skip string terminators - ); - - structs::GuildMemberEntry_Struct *e = (structs::GuildMemberEntry_Struct *) buffer; - - uint32 r; - for(r = 0; r < emu->count; r++, emu_e++) { - - //the order we set things here must match the struct - -//nice helper macro -/*#define SlideStructString(field, str) \ - strcpy(e->field, str.c_str()); \ - e = (GuildMemberEntry_Struct *) ( ((uint8 *)e) + str.length() )*/ -#define SlideStructString(field, str) \ - { \ - int sl = strlen(str); \ - memcpy(e->field, str, sl+1); \ - e = (structs::GuildMemberEntry_Struct *) ( ((uint8 *)e) + sl ); \ - str += sl + 1; \ - } -#define PutFieldN(field) \ - e->field = htonl(emu_e->field) - - SlideStructString( name, emu_name ); - PutFieldN(level); - PutFieldN(banker); - PutFieldN(class_); - PutFieldN(rank); - PutFieldN(time_last_on); - PutFieldN(tribute_enable); - PutFieldN(total_tribute); - PutFieldN(last_tribute); - e->unknown_one = htonl(1); - SlideStructString( public_note, emu_note ); - e->zoneinstance = 0; - e->zone_id = htons(emu_e->zone_id); - - -#undef SlideStructString -#undef PutFieldN - - e++; - } - } - - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - - -ENCODE(OP_SpawnDoor) { - SETUP_VAR_ENCODE(Door_Struct); - int door_count = __packet->size/sizeof(Door_Struct); - int total_length = door_count * sizeof(structs::Door_Struct); - ALLOC_VAR_ENCODE(structs::Door_Struct, total_length); - int r; - for(r = 0; r < door_count; r++) { - strcpy(eq[r].name, emu[r].name); - eq[r].xPos = emu[r].xPos; - eq[r].yPos = emu[r].yPos; - eq[r].zPos = emu[r].zPos; - eq[r].heading = emu[r].heading; - eq[r].incline = emu[r].incline; - eq[r].size = emu[r].size; - eq[r].doorId = emu[r].doorId; - eq[r].opentype = emu[r].opentype; - eq[r].state_at_spawn = emu[r].state_at_spawn; - eq[r].invert_state = emu[r].invert_state; - eq[r].door_param = emu[r].door_param; - eq[r].unknown0076 = 0; - eq[r].unknown0077 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0078 = 0; - eq[r].unknown0079 = 1; // Both must be 1 to allow clicking doors - eq[r].unknown0080 = 0; - eq[r].unknown0081 = 0; - eq[r].unknown0082 = 0; - } - FINISH_ENCODE(); -} - -ENCODE(OP_GroundSpawn) -{ - - // We are not encoding the spawn_id field here, or a size but it doesn't appear to matter. - // - EQApplicationPacket *in = *p; - *p = nullptr; - - Object_Struct *emu = (Object_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->object_name) + 58; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->drop_id); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->object_name); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_id); - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, emu->zone_instance); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00006762 - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observer 0x7fffbb64 - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->heading); - // This next field is actually a float. There is a groundspawn in freeportwest (sack of money sitting on some barrels) which requires this - // field to be set to (float)255.0 to appear at all, and also the size field below to be 5, to be the correct size. I think SoD has the same - // issue. - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0 - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // This appears to be the size field. - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->y); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->x); - VARSTRUCT_ENCODE_TYPE(float, OutBuffer, emu->z); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->object_type); // Unknown, observed 0x00000014 - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); // Unknown, observed 0xffffffff - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown, observed 0x00000014 - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown, observed 0x00 - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_ManaChange) { - ENCODE_LENGTH_EXACT(ManaChange_Struct); - SETUP_DIRECT_ENCODE(ManaChange_Struct, structs::ManaChange_Struct); - OUT(new_mana); - OUT(stamina); - OUT(spell_id); - eq->unknown16 = -1; // Self Interrupt/Success = -1, Fizzle = 1, Other Interrupt = 2? - FINISH_ENCODE(); -} - -ENCODE(OP_OnLevelMessage) -{ - ENCODE_LENGTH_EXACT(OnLevelMessage_Struct); - SETUP_DIRECT_ENCODE(OnLevelMessage_Struct, structs::OnLevelMessage_Struct); - memcpy(eq->Title, emu->Title, sizeof(eq->Title)); - memcpy(eq->Text, emu->Text, sizeof(eq->Text)); - OUT(Buttons); - OUT(Duration); - OUT(PopupID); - OUT(NegativeID); - // These two field names are used if Buttons == 1. - OUT_str(ButtonName0); - OUT_str(ButtonName1); - FINISH_ENCODE(); -} - -ENCODE(OP_Illusion) { - ENCODE_LENGTH_EXACT(Illusion_Struct); - SETUP_DIRECT_ENCODE(Illusion_Struct, structs::Illusion_Struct); - OUT(spawnid); - OUT_str(charname); - OUT(race); - OUT(unknown006[0]); - OUT(unknown006[1]); - OUT(gender); - OUT(texture); - OUT(helmtexture); - OUT(face); - OUT(hairstyle); - OUT(haircolor); - OUT(beard); - OUT(beardcolor); - OUT(size); - OUT(drakkin_heritage); - OUT(drakkin_tattoo); - OUT(drakkin_details); - - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerBuy) -{ - ENCODE_LENGTH_EXACT(Merchant_Sell_Struct); - SETUP_DIRECT_ENCODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - OUT(npcid); - OUT(playerid); - OUT(itemslot); - OUT(quantity); - OUT(price); - - FINISH_ENCODE(); -} - -ENCODE(OP_ClientUpdate) { - ENCODE_LENGTH_EXACT(PlayerPositionUpdateServer_Struct); - SETUP_DIRECT_ENCODE(PlayerPositionUpdateServer_Struct, structs::PlayerPositionUpdateServer_Struct); - OUT(spawn_id); - OUT(x_pos); - OUT(delta_x); - OUT(delta_y); - OUT(z_pos); - OUT(delta_heading); - OUT(y_pos); - OUT(delta_z); - OUT(animation); - OUT(heading); - FINISH_ENCODE(); -} - -ENCODE(OP_ExpansionInfo) { - ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpansionInfo_Struct, structs::ExpansionInfo_Struct); - OUT(Expansions); - FINISH_ENCODE(); -} - -ENCODE(OP_LogServer) { - ENCODE_LENGTH_EXACT(LogServer_Struct); - SETUP_DIRECT_ENCODE(LogServer_Struct, structs::LogServer_Struct); - strcpy(eq->worldshortname, emu->worldshortname); - - OUT(enablevoicemacros); - OUT(enablemail); - OUT(enable_pvp); - OUT(enable_FV); - - eq->unknown016 = 1; - eq->unknown020[0] = 1; - - // These next two need to be set like this for the Tutorial Button to work. - eq->unknown263[0] = 0; - eq->unknown263[2] = 1; - eq->unknown263[4] = 1; - eq->unknown263[5] = 1; - eq->unknown263[6] = 1; - eq->unknown263[9] = 8; - eq->unknown263[19] = 0x80; - eq->unknown263[20] = 0x3f; - eq->unknown263[23] = 0x80; - eq->unknown263[24] = 0x3f; - eq->unknown263[33] = 1; - - FINISH_ENCODE(); -} - -ENCODE(OP_Damage) { - ENCODE_LENGTH_EXACT(CombatDamage_Struct); - SETUP_DIRECT_ENCODE(CombatDamage_Struct, structs::CombatDamage_Struct); - OUT(target); - OUT(source); - OUT(type); - OUT(spellid); - OUT(damage); - eq->sequence = emu->sequence; - FINISH_ENCODE(); -} - -ENCODE(OP_Consider) { - ENCODE_LENGTH_EXACT(Consider_Struct); - SETUP_DIRECT_ENCODE(Consider_Struct, structs::Consider_Struct); - OUT(playerid); - OUT(targetid); - OUT(faction); - OUT(level); - OUT(pvpcon); - FINISH_ENCODE(); -} - -ENCODE(OP_Action) { - ENCODE_LENGTH_EXACT(Action_Struct); - SETUP_DIRECT_ENCODE(Action_Struct, structs::ActionAlt_Struct); - OUT(target); - OUT(source); - OUT(level); - eq->instrument_mod = 1.0f + (emu->instrument_mod - 10) / 10.0f; - eq->knockback_angle = emu->sequence; - OUT(type); - OUT(spell); - eq->level2 = eq->level; - eq->effect_flag = emu->buff_unknown; - eq->unknown37 = 0x01; - eq->unknown44 = 0xFFFFFFFF; - eq->unknown48 = 0xFFFFFFFF; - eq->unknown52 = 0xFFFFFFFF; - - /*OUT(target); - OUT(source); - OUT(level); - OUT(instrument_mod); - eq->sequence = emu->sequence; - OUT(type); - //OUT(damage); - OUT(spell); - eq->level2 = emu->level; - OUT(buff_unknown); // if this is 4, a buff icon is made - //eq->unknown0036 = -1; - //eq->unknown0040 = -1; - //eq->unknown0044 = -1;*/ - FINISH_ENCODE(); -} - -ENCODE(OP_Buff) { - ENCODE_LENGTH_EXACT(SpellBuffFade_Struct); - SETUP_DIRECT_ENCODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); - OUT(entityid); - OUT(slot); - OUT(level); - OUT(effect); - //eq->unknown7 = 10; - OUT(spellid); - OUT(duration); - OUT(slotid); - OUT(bufffade); // Live (October 2011) sends a 2 rather than 0 when a buff is created, but it doesn't seem to matter. - eq->unknown008 = 1.0f; - FINISH_ENCODE(); -} - -ENCODE(OP_CancelTrade) { - ENCODE_LENGTH_EXACT(CancelTrade_Struct); - SETUP_DIRECT_ENCODE(CancelTrade_Struct, structs::CancelTrade_Struct); - OUT(fromid); - OUT(action); - FINISH_ENCODE(); -} - -ENCODE(OP_ShopPlayerSell) { - ENCODE_LENGTH_EXACT(Merchant_Purchase_Struct); - SETUP_DIRECT_ENCODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - OUT(npcid); - eq->itemslot = ServerToUnderfootSlot(emu->itemslot); - OUT(quantity); - OUT(price); - FINISH_ENCODE(); -} - -ENCODE(OP_ApplyPoison) { - ENCODE_LENGTH_EXACT(ApplyPoison_Struct); - SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - eq->inventorySlot = ServerToUnderfootSlot(emu->inventorySlot); - OUT(success); - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteItem) { - ENCODE_LENGTH_EXACT(DeleteItem_Struct); - SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - eq->from_slot = ServerToUnderfootSlot(emu->from_slot); - eq->to_slot = ServerToUnderfootSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } -ENCODE(OP_MoveItem) { - ENCODE_LENGTH_EXACT(MoveItem_Struct); - SETUP_DIRECT_ENCODE(MoveItem_Struct, structs::MoveItem_Struct); - - eq->from_slot = ServerToUnderfootSlot(emu->from_slot); - eq->to_slot = ServerToUnderfootSlot(emu->to_slot); - OUT(number_in_stack); - - FINISH_ENCODE(); -} - -ENCODE(OP_ItemVerifyReply) { - ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); - SETUP_DIRECT_ENCODE(ItemVerifyReply_Struct, structs::ItemVerifyReply_Struct); - - eq->slot = ServerToUnderfootSlot(emu->slot); - OUT(spell); - OUT(target); - - FINISH_ENCODE(); -} - -ENCODE(OP_Trader) { - - if((*p)->size != sizeof(TraderBuy_Struct)) { - EQApplicationPacket *in = *p; - *p = nullptr; - dest->FastQueuePacket(&in, ack_req); - return; - } - ENCODE_FORWARD(OP_TraderBuy); -} - -ENCODE(OP_TraderBuy) { - - ENCODE_LENGTH_EXACT(TraderBuy_Struct); - SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct); - - OUT(Action); - OUT(Price); - OUT(TraderID); - memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName)); - OUT(ItemID); - OUT(Quantity); - OUT(AlreadySold); - - FINISH_ENCODE(); -} - -ENCODE(OP_LootItem) { - - ENCODE_LENGTH_EXACT(LootingItem_Struct); - SETUP_DIRECT_ENCODE(LootingItem_Struct, structs::LootingItem_Struct); - OUT(lootee); - OUT(looter); - eq->slot_id = emu->slot_id + 1; - OUT(auto_loot); - - FINISH_ENCODE(); -} - -ENCODE(OP_TributeItem) { - ENCODE_LENGTH_EXACT(TributeItem_Struct); - SETUP_DIRECT_ENCODE(TributeItem_Struct, structs::TributeItem_Struct); - - eq->slot = ServerToUnderfootSlot(emu->slot); - OUT(quantity); - OUT(tribute_master_id); - OUT(tribute_points); - - FINISH_ENCODE(); -} - -ENCODE(OP_SomeItemPacketMaybe) { - // This Opcode is not named very well. It is used for the animation of arrows leaving the player's bow - // and flying to the target. - // - - ENCODE_LENGTH_EXACT(Arrow_Struct); - SETUP_DIRECT_ENCODE(Arrow_Struct, structs::Arrow_Struct); - - OUT(src_y); - OUT(src_x); - OUT(src_z); - OUT(velocity); - OUT(launch_angle); - OUT(tilt); - OUT(arc); - OUT(source_id); - OUT(target_id); - OUT(item_id); - - eq->unknown070 = 135; // This needs to be set to something, else we get a 1HS animation instead of ranged. - - OUT(item_type); - OUT(skill); - - strcpy(eq->model_name, emu->model_name); - - FINISH_ENCODE(); -} - -ENCODE(OP_ReadBook) { - - ENCODE_LENGTH_ATLEAST(BookText_Struct); - SETUP_DIRECT_ENCODE(BookText_Struct, structs::BookRequest_Struct); - - if(emu->window == 0xFF) - eq->window = 0xFFFFFFFF; - else - eq->window = emu->window; - OUT(type); - eq->invslot = ServerToUnderfootSlot(emu->invslot); - strn0cpy(eq->txtfile, emu->booktext, sizeof(eq->txtfile)); - FINISH_ENCODE(); -} - -ENCODE(OP_Stun) { - - ENCODE_LENGTH_EXACT(Stun_Struct); - SETUP_DIRECT_ENCODE(Stun_Struct, structs::Stun_Struct); - OUT(duration); - eq->unknown005 = 163; - eq->unknown006 = 67; - - FINISH_ENCODE(); -} - -ENCODE(OP_ZonePlayerToBind) -{ - ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct); - ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)]; - structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1; - unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)]; - structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2; - - zph->x = zps->x; - zph->y = zps->y; - zph->z = zps->z; - zph->heading = zps->heading; - zph->bind_zone_id = zps->bind_zone_id; - zph->bind_instance_id = zps->bind_instance_id; - strcpy(zph->zone_name, zps->zone_name); - - zpf->unknown021 = 1; - zpf->unknown022 = 0; - zpf->unknown023 = 0; - zpf->unknown024 = 0; - - ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name))); - ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct)); - - delete[] buffer1; - delete[] buffer2; - delete[] (*p)->pBuffer; - - (*p)->pBuffer = new unsigned char[ss.str().size()]; - (*p)->size = ss.str().size(); - - memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size()); - dest->FastQueuePacket(&(*p)); -} - -ENCODE(OP_AdventureMerchantSell) { - ENCODE_LENGTH_EXACT(Adventure_Sell_Struct); - SETUP_DIRECT_ENCODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - eq->unknown000 = 1; - OUT(npcid); - eq->slot = ServerToUnderfootSlot(emu->slot); - OUT(charges); - OUT(sell_price); - - FINISH_ENCODE(); -} - -ENCODE(OP_RaidUpdate) -{ - EQApplicationPacket *inapp = *p; - *p = nullptr; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidGeneral_Struct *raid_gen = (RaidGeneral_Struct*)__emu_buffer; - - if(raid_gen->action == 0) // raid add has longer length than other raid updates +// DECODE methods + DECODE(OP_AdventureMerchantSell) { - RaidAddMember_Struct* in_add_member = (RaidAddMember_Struct*)__emu_buffer; + DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); + SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidAddMember_Struct)); - structs::RaidAddMember_Struct *add_member = (structs::RaidAddMember_Struct*)outapp->pBuffer; + IN(npcid); + emu->slot = UnderfootToServerSlot(eq->slot); + IN(charges); + IN(sell_price); - add_member->raidGen.action = in_add_member->raidGen.action; - add_member->raidGen.parameter = in_add_member->raidGen.parameter; - strn0cpy(add_member->raidGen.leader_name, in_add_member->raidGen.leader_name, 64); - strn0cpy(add_member->raidGen.player_name, in_add_member->raidGen.player_name, 64); - add_member->_class = in_add_member->_class; - add_member->level= in_add_member->level; - add_member->isGroupLeader = in_add_member->isGroupLeader; - add_member->flags[0] = in_add_member->flags[0]; - add_member->flags[1] = in_add_member->flags[1]; - add_member->flags[2] = in_add_member->flags[2]; - add_member->flags[3] = in_add_member->flags[3]; - add_member->flags[4] = in_add_member->flags[4]; - dest->FastQueuePacket(&outapp); - } - else - { - RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *raid_general = (structs::RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(raid_general->leader_name, in_raid_general->leader_name, 64); - strn0cpy(raid_general->player_name, in_raid_general->player_name, 64); - raid_general->action = in_raid_general->action; - raid_general->parameter = in_raid_general->parameter; - dest->FastQueuePacket(&outapp); - } - delete[] __emu_buffer; -} - -ENCODE(OP_RaidJoin) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - RaidCreate_Struct *raid_create = (RaidCreate_Struct*)__emu_buffer; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidGeneral_Struct)); - structs::RaidGeneral_Struct *general = (structs::RaidGeneral_Struct*)outapp_create->pBuffer; - - general->action = 8; - general->parameter = 1; - strn0cpy(general->leader_name, raid_create->leader_name, 64); - strn0cpy(general->player_name, raid_create->leader_name, 64); - - dest->FastQueuePacket(&outapp_create); - delete[] __emu_buffer; -} - -ENCODE(OP_VetRewardsAvaliable) -{ - EQApplicationPacket *inapp = *p; - unsigned char * __emu_buffer = inapp->pBuffer; - - uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward)); - *p = nullptr; - - EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count)); - uchar *old_data = __emu_buffer; - uchar *data = outapp_create->pBuffer; - for(unsigned int i = 0; i < count; ++i) - { - structs::VeteranReward *vr = (structs::VeteranReward*)data; - InternalVeteranReward *ivr = (InternalVeteranReward*)old_data; - - vr->claim_count = ivr->claim_count; - vr->claim_id = ivr->claim_id; - vr->number_available = ivr->number_available; - for(int x = 0; x < 8; ++x) - { - vr->items[x].item_id = ivr->items[x].item_id; - strcpy(vr->items[x].item_name, ivr->items[x].item_name); - vr->items[x].charges = ivr->items[x].charges; - } - - old_data += sizeof(InternalVeteranReward); - data += sizeof(structs::VeteranReward); + FINISH_DIRECT_DECODE(); } - dest->FastQueuePacket(&outapp_create); - delete inapp; -} - -ENCODE(OP_WhoAllResponse) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - char *InBuffer = (char *)in->pBuffer; - - WhoAllReturnStruct *wars = (WhoAllReturnStruct*)InBuffer; - - int Count = wars->playercount; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_WhoAllResponse, in->size + (Count * 4)); - - char *OutBuffer = (char *)outapp->pBuffer; - - memcpy(OutBuffer, InBuffer, sizeof(WhoAllReturnStruct)); - - OutBuffer += sizeof(WhoAllReturnStruct); - InBuffer += sizeof(WhoAllReturnStruct); - - for(int i = 0; i < Count; ++i) + DECODE(OP_AltCurrencySell) { - uint32 x; + DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + IN(merchant_entity_id); + emu->slot_id = UnderfootToServerSlot(eq->slot_id); + IN(charges); + IN(cost); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AltCurrencySellSelection) + { + DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); + SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); + + IN(merchant_entity_id); + emu->slot_id = UnderfootToServerSlot(eq->slot_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ApplyPoison) + { + DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); + SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + emu->inventorySlot = UnderfootToServerSlot(eq->inventorySlot); + IN(success); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentInfo) + { + DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); + SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + IN(itemid); + IN(window); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_AugmentItem) + { + DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); + SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); + + emu->container_slot = UnderfootToServerSlot(eq->container_slot); + emu->augment_slot = eq->augment_slot; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BazaarSearch) + { + char *Buffer = (char *)__packet->pBuffer; + + uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); + + if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) + return; + + SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); + MEMSET_IN(structs::NewBazaarInspect_Struct); + IN(Beginning.Action); + memcpy(emu->Name, eq->Name, sizeof(emu->Name)); + IN(SerialNumber); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Buff) + { + DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Underfoot); + SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); + + IN(entityid); + IN(slot); + IN(level); + IN(effect); + IN(spellid); + IN(duration); + IN(slotid); + IN(bufffade); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_BuffRemoveRequest) + { + // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. + // + DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); + SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); + + emu->SlotID = (eq->SlotID < 30) ? eq->SlotID : (eq->SlotID - 5); + + IN(EntityID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_CastSpell) + { + DECODE_LENGTH_EXACT(structs::CastSpell_Struct); + SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); + + if (eq->slot == 13) + emu->slot = 10; + else + IN(slot); + + IN(spell_id); + emu->inventoryslot = UnderfootToServerSlot(eq->inventoryslot); + IN(target_id); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ChannelMessage) + { + unsigned char *__eq_buffer = __packet->pBuffer; + + char *InBuffer = (char *)__eq_buffer; + + char Sender[64]; + char Target[64]; + + VARSTRUCT_DECODE_STRING(Sender, InBuffer); + VARSTRUCT_DECODE_STRING(Target, InBuffer); InBuffer += 4; - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0xffffffff); - char Name[64]; + uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); + InBuffer += 5; - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Char Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + __packet->size = sizeof(ChannelMessage_Struct)+strlen(InBuffer) + 1; + __packet->pBuffer = new unsigned char[__packet->size]; + ChannelMessage_Struct *emu = (ChannelMessage_Struct *)__packet->pBuffer; - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Guild Name - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); + strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); + strn0cpy(emu->sender, Target, sizeof(emu->sender)); + emu->language = Language; + emu->chan_num = Channel; + emu->skill_in_language = Skill; + strcpy(emu->message, InBuffer); - for(int j = 0; j < 7; ++j) - { - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + delete[] __eq_buffer; + } + + DECODE(OP_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + IN(class_); + IN(beardcolor); + IN(beard); + IN(hairstyle); + IN(gender); + IN(race); + IN(start_zone); + IN(haircolor); + IN(deity); + IN(STR); + IN(STA); + IN(AGI); + IN(DEX); + IN(WIS); + IN(INT); + IN(CHA); + IN(face); + IN(eyecolor1); + IN(eyecolor2); + IN(tutorial); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ClientUpdate) + { + // for some odd reason, there is an extra byte on the end of this on occasion.. + DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); + SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); + + IN(spawn_id); + IN(sequence); + IN(x_pos); + IN(y_pos); + IN(z_pos); + IN(heading); + IN(delta_x); + IN(delta_y); + IN(delta_z); + IN(delta_heading); + IN(animation); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Consider) + { + DECODE_LENGTH_EXACT(structs::Consider_Struct); + SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); + + IN(playerid); + IN(targetid); + IN(faction); + IN(level); + //emu->cur_hp = 1; + //emu->max_hp = 2; + //emu->pvpcon = 0; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } + + DECODE(OP_Consume) + { + DECODE_LENGTH_EXACT(structs::Consume_Struct); + SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); + + emu->slot = UnderfootToServerSlot(eq->slot); + IN(auto_consumed); + IN(type); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Damage) + { + DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); + SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); + + IN(target); + IN(source); + IN(type); + IN(spellid); + IN(damage); + emu->sequence = eq->sequence; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_DeleteItem) + { + DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); + SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + emu->from_slot = UnderfootToServerSlot(eq->from_slot); + emu->to_slot = UnderfootToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_EnvDamage) + { + DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); + SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); + + IN(id); + IN(damage); + IN(dmgtype); + emu->constant = 0xFFFF; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FaceChange) + { + DECODE_LENGTH_EXACT(structs::FaceChange_Struct); + SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); + + IN(haircolor); + IN(beardcolor); + IN(eyecolor1); + IN(eyecolor2); + IN(hairstyle); + IN(beard); + IN(face); + IN(drakkin_heritage); + IN(drakkin_tattoo); + IN(drakkin_details); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_FindPersonRequest) + { + DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); + SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); + + IN(npc_id); + IN(client_pos.x); + IN(client_pos.y); + IN(client_pos.z); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupCancelInvite) + { + DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); + SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + IN(toggle); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupDisband) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_Disband"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupFollow2) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); + + memcpy(emu->name1, eq->name1, sizeof(emu->name1)); + memcpy(emu->name2, eq->name2, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite) + { + //EQApplicationPacket *in = __packet; + //_log(NET__ERROR, "Received incoming OP_GroupInvite"); + //_hex(NET__ERROR, in->pBuffer, in->size); + DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); + SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); + + memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); + memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_GroupInvite2) + { + //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); + DECODE_FORWARD(OP_GroupInvite); + } + + DECODE(OP_InspectRequest) + { + DECODE_LENGTH_EXACT(structs::Inspect_Struct); + SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); + + IN(TargetID); + IN(PlayerID); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_ItemLinkClick) + { + DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); + SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); + MEMSET_IN(ItemViewRequest_Struct); + + IN(item_id); + int r; + for (r = 0; r < 5; r++) { + IN(augments[r]); } + IN(link_hash); + IN(icon); - VARSTRUCT_DECODE_STRING(Name, InBuffer); // Account - VARSTRUCT_ENCODE_STRING(OutBuffer, Name); - - x = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, x); + FINISH_DIRECT_DECODE(); } - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; -} - -ENCODE(OP_GroupInvite) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(eq->invitee_name, emu->name1, sizeof(eq->invitee_name)); - memcpy(eq->inviter_name, emu->name2, sizeof(eq->inviter_name)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupFollow2) { - ENCODE_LENGTH_EXACT(GroupGeneric_Struct); - SETUP_DIRECT_ENCODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupCancelInvite) -{ - ENCODE_LENGTH_EXACT(GroupCancel_Struct); - SETUP_DIRECT_ENCODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(eq->name1, emu->name1, sizeof(eq->name1)); - memcpy(eq->name2, emu->name2, sizeof(eq->name2)); - OUT(toggle); - FINISH_ENCODE(); -} - -ENCODE(OP_GroupUpdate) -{ - //_log(NET__ERROR, "OP_GroupUpdate"); - EQApplicationPacket *in = *p; - - GroupJoin_Struct *gjs = (GroupJoin_Struct*)in->pBuffer; - - //_log(NET__ERROR, "Received outgoing OP_GroupUpdate with action code %i", gjs->action); - if((gjs->action == groupActLeave) || (gjs->action == groupActDisband)) + DECODE(OP_ItemVerifyRequest) { - if((gjs->action == groupActDisband) || !strcmp(gjs->yourname, gjs->membername)) + DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); + SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); + + emu->slot = UnderfootToServerSlot(eq->slot); + IN(target); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LoadSpellSet) + { + DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); + SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); + + for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + if (eq->spell[i] == 0) + emu->spell[i] = 0xFFFFFFFF; + else + emu->spell[i] = eq->spell[i]; + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_LootItem) + { + DECODE_LENGTH_EXACT(structs::LootingItem_Struct); + SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); + + IN(lootee); + IN(looter); + emu->slot_id = UnderfootToServerCorpseSlot(eq->slot_id); + IN(auto_loot); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_MoveItem) + { + DECODE_LENGTH_EXACT(structs::MoveItem_Struct); + SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); + + _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); + + emu->from_slot = UnderfootToServerSlot(eq->from_slot); + emu->to_slot = UnderfootToServerSlot(eq->to_slot); + IN(number_in_stack); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_PetCommands) + { + DECODE_LENGTH_EXACT(structs::PetCommand_Struct); + SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); + + switch (eq->command) { - //_log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandYou, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name1)); - dest->FastQueuePacket(&outapp); - - // Make an empty GLAA packet to clear out their useable GLAAs - // - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - dest->FastQueuePacket(&outapp); - - delete in; - - return; - } - //if(gjs->action == groupActLeave) - // _log(NET__ERROR, "Group Leave, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupDisbandOther, sizeof(structs::GroupGeneric_Struct)); - - structs::GroupGeneric_Struct *ggs = (structs::GroupGeneric_Struct*)outapp->pBuffer; - memcpy(ggs->name1, gjs->yourname, sizeof(ggs->name1)); - memcpy(ggs->name2, gjs->membername, sizeof(ggs->name2)); - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - delete in; - return; - - - } - - if(in->size == sizeof(GroupUpdate2_Struct)) - { - // Group Update2 - //_log(NET__ERROR, "Struct is GroupUpdate2"); - - unsigned char *__emu_buffer = in->pBuffer; - GroupUpdate2_Struct *gu2 = (GroupUpdate2_Struct*) __emu_buffer; - - //_log(NET__ERROR, "Yourname is %s", gu2->yourname); - - int MemberCount = 1; - - int PacketLength = 8 + strlen(gu2->leadersname) + 1 + 22 + strlen(gu2->yourname) + 1; - - for(int i = 0; i < 5; ++i) - { - //_log(NET__ERROR, "Membername[%i] is %s", i, gu2->membername[i]); - if(gu2->membername[i][0] != '\0') - { - PacketLength += (22 + strlen(gu2->membername[i]) + 1); - ++MemberCount; - } - } - - //_log(NET__ERROR, "Leadername is %s", gu2->leadersname); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupUpdateB, PacketLength); - - char *Buffer = (char *)outapp->pBuffer; - - // Header - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Think this should be SpawnID, but it doesn't seem to matter - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberCount); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->leadersname); - - // Leader - // - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->yourname); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x46); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - - int MemberNumber = 1; - - for(int i = 0; i < 5; ++i) - { - if(gu2->membername[i][0] == '\0') - continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, MemberNumber++); - VARSTRUCT_ENCODE_STRING(Buffer, gu2->membername[i]); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - //VARSTRUCT_ENCODE_STRING(Buffer, ""); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // This is a string - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x41); // Observed 0x41 and 0x46 here - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Low byte is Main Assist Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint16, Buffer, 0); - } - - //_hex(NET__ERROR, outapp->pBuffer, outapp->size); - dest->FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = gu2->NPCMarkerID; - memcpy(&GLAAus->LeaderAAs, &gu2->leader_aas, sizeof(GLAAus->LeaderAAs)); - - dest->FastQueuePacket(&outapp); - delete in; - - return; - - } - //_log(NET__ERROR, "Generic GroupUpdate, yourname = %s, membername = %s", gjs->yourname, gjs->membername); - ENCODE_LENGTH_EXACT(GroupJoin_Struct); - SETUP_DIRECT_ENCODE(GroupJoin_Struct, structs::GroupJoin_Struct); - - memcpy(eq->membername, emu->membername, sizeof(eq->membername)); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupLeadershipAAUpdate, sizeof(GroupLeadershipAAUpdate_Struct)); - - GroupLeadershipAAUpdate_Struct *GLAAus = (GroupLeadershipAAUpdate_Struct*)outapp->pBuffer; - - GLAAus->NPCMarkerID = emu->NPCMarkerID; - - memcpy(&GLAAus->LeaderAAs, &emu->leader_aas, sizeof(GLAAus->LeaderAAs)); - //_hex(NET__ERROR, __packet->pBuffer, __packet->size); - FINISH_ENCODE(); - - dest->FastQueuePacket(&outapp); -} - -ENCODE(OP_ChannelMessage) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) in->pBuffer; - - unsigned char *__emu_buffer = in->pBuffer; - - in->size = strlen(emu->sender) + 1 + strlen(emu->targetname) + 1 + strlen(emu->message) + 1 + 36; - - in->pBuffer = new unsigned char[in->size]; - - char *OutBuffer = (char *)in->pBuffer; - - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->sender); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->targetname); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->language); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->chan_num); - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, emu->skill_in_language); - VARSTRUCT_ENCODE_STRING(OutBuffer, emu->message); - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint16, OutBuffer, 0); // Unknown - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0); // Unknown - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_GuildsList) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - uint32 NumberOfGuilds = in->size / 64; - - uint32 PacketSize = 68; // 64 x 0x00 + a uint32 that I am guessing is the highest guild ID in use. - - unsigned char *__emu_buffer = in->pBuffer; - - char *InBuffer = (char *)__emu_buffer; - - uint32 HighestGuildID = 0; - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - PacketSize += (5 + strlen(InBuffer)); - HighestGuildID = i - 1; - } - InBuffer += 64; - } - - PacketSize++; // Appears to be an extra 0x00 at the very end. - - in->size = PacketSize; - - in->pBuffer = new unsigned char[in->size]; - - InBuffer = (char *)__emu_buffer; - - char *OutBuffer = (char *)in->pBuffer; - - // Init the first 64 bytes to zero, as per live. - // - memset(OutBuffer, 0, 64); - - OutBuffer += 64; - - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, HighestGuildID); - - for(unsigned int i = 0; i < NumberOfGuilds; ++i) - { - if(InBuffer[0]) - { - VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, i - 1); - VARSTRUCT_ENCODE_STRING(OutBuffer, InBuffer); - } - InBuffer += 64; - } - - VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, 0x00); - - delete[] __emu_buffer; - - dest->FastQueuePacket(&in, ack_req); -} - -ENCODE(OP_DzExpeditionEndsWarning) -{ - ENCODE_LENGTH_EXACT(ExpeditionExpireWarning); - SETUP_DIRECT_ENCODE(ExpeditionExpireWarning, structs::ExpeditionExpireWarning); - OUT(minutes_remaining); - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionInfo) -{ - ENCODE_LENGTH_EXACT(ExpeditionInfo_Struct); - SETUP_DIRECT_ENCODE(ExpeditionInfo_Struct, structs::ExpeditionInfo_Struct); - OUT(max_players); - eq->unknown004 = 785316192; - eq->unknown008 = 435601; - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->leader_name, emu->leader_name); - FINISH_ENCODE(); -} - -ENCODE(OP_DzCompass) -{ - SETUP_VAR_ENCODE(ExpeditionCompass_Struct); - ALLOC_VAR_ENCODE(structs::ExpeditionCompass_Struct, sizeof(structs::ExpeditionInfo_Struct) + sizeof(structs::ExpeditionCompassEntry_Struct) * emu->count); - OUT(count); - - for(uint32 i = 0; i < emu->count; ++i) - { - OUT(entries[i].x); - OUT(entries[i].y); - OUT(entries[i].z); - } - - FINISH_ENCODE(); -} - -ENCODE(OP_DzMemberList) -{ - SETUP_VAR_ENCODE(ExpeditionMemberList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].name, strlen(emu->entries[i].name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].status, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzExpeditionList) -{ - SETUP_VAR_ENCODE(ExpeditionLockoutList_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&emu->count, sizeof(uint32)); - for(uint32 i = 0; i < emu->count; ++i) - { - ss.write(emu->entries[i].expedition, strlen(emu->entries[i].expedition)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&emu->entries[i].time_left, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->entries[i].expedition_event, strlen(emu->entries[i].expedition_event)); - ss.write((const char*)&null_term, sizeof(char)); - } - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzLeaderStatus) -{ - SETUP_VAR_ENCODE(ExpeditionLeaderSet_Struct); - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - uint32 client_id = 0; - uint8 null_term = 0; - - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write(emu->leader_name, strlen(emu->leader_name)); - ss.write((const char*)&null_term, sizeof(char)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//0xffffffff - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32)); - ss.write((const char*)&client_id, sizeof(uint32));//1 - ss.write((const char*)&client_id, sizeof(uint32)); - - __packet->size = ss.str().length(); - __packet->pBuffer = new unsigned char[__packet->size]; - memcpy(__packet->pBuffer, ss.str().c_str(), __packet->size); - - FINISH_ENCODE(); -} - -ENCODE(OP_DzJoinExpeditionConfirm) -{ - ENCODE_LENGTH_EXACT(ExpeditionJoinPrompt_Struct); - SETUP_DIRECT_ENCODE(ExpeditionJoinPrompt_Struct, structs::ExpeditionJoinPrompt_Struct); - strcpy(eq->expedition_name, emu->expedition_name); - strcpy(eq->player_name, emu->player_name); - FINISH_ENCODE(); -} - -ENCODE(OP_TargetBuffs) { ENCODE_FORWARD(OP_BuffCreate); } -ENCODE(OP_BuffCreate) -{ - SETUP_VAR_ENCODE(BuffIcon_Struct); - - uint32 sz = 12 + (17 * emu->count); - __packet->size = sz; - __packet->pBuffer = new unsigned char[sz]; - memset(__packet->pBuffer, 0, sz); - - uchar *ptr = __packet->pBuffer; - *((uint32*)ptr) = emu->entity_id; - ptr += sizeof(uint32); - ptr += sizeof(uint32); - *((uint8*)ptr) = 1; - ptr += sizeof(uchar); - *((uint16*)ptr) = emu->count; - ptr += sizeof(uint16); - - for(uint16 i = 0; i < emu->count; ++i) - { - uint16 buffslot = emu->entries[i].buff_slot; - if(emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) - { - buffslot += 5; - } - else if(emu->entries[i].buff_slot >= 37) - { - buffslot += 14; - } - - *((uint32*)ptr) = buffslot; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].spell_id; - ptr += sizeof(uint32); - *((uint32*)ptr) = emu->entries[i].tics_remaining; - ptr += sizeof(uint32); - ptr += sizeof(uint32); - ptr += 1; - } - FINISH_ENCODE(); - /* - uint32 write_var32 = 60; - uint8 write_var8 = 1; - ss.write((const char*)&emu->entity_id, sizeof(uint32)); - ss.write((const char*)&write_var32, sizeof(uint32)); - ss.write((const char*)&write_var8, sizeof(uint8)); - ss.write((const char*)&emu->count, sizeof(uint16)); - write_var32 = 0; - write_var8 = 0; - for(uint16 i = 0; i < emu->count; ++i) - { - if(emu->entries[i].buff_slot >= 25 && emu->entries[i].buff_slot < 37) - { - emu->entries[i].buff_slot += 5; - } - else if(emu->entries[i].buff_slot >= 37) - { - emu->entries[i].buff_slot += 14; - } - ss.write((const char*)&emu->entries[i].buff_slot, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].spell_id, sizeof(uint32)); - ss.write((const char*)&emu->entries[i].tics_remaining, sizeof(uint32)); - ss.write((const char*)&write_var32, sizeof(uint32)); - ss.write((const char*)&write_var8, sizeof(uint8)); - } - ss.write((const char*)&write_var8, sizeof(uint8)); - */ -} - -ENCODE(OP_WearChange) -{ - ENCODE_LENGTH_EXACT(WearChange_Struct); - SETUP_DIRECT_ENCODE(WearChange_Struct, structs::WearChange_Struct); - OUT(spawn_id); - OUT(material); - OUT(unknown06); - OUT(elite_material); - OUT(color.color); - OUT(wear_slot_id); - FINISH_ENCODE(); -} - -ENCODE(OP_SpawnAppearance) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - - SpawnAppearance_Struct *sas = (SpawnAppearance_Struct *)emu_buffer; - - if(sas->type != AT_Size) - { - dest->FastQueuePacket(&in, ack_req); - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ChangeSize, sizeof(ChangeSize_Struct)); - - ChangeSize_Struct *css = (ChangeSize_Struct *)outapp->pBuffer; - - css->EntityID = sas->spawn_id; - css->Size = (float)sas->parameter; - css->Unknown08 = 0; - css->Unknown12 = 1.0f; - - dest->FastQueuePacket(&outapp, ack_req); - - delete in; -} - -ENCODE(OP_DisciplineUpdate) -{ - ENCODE_LENGTH_EXACT(Disciplines_Struct); - SETUP_DIRECT_ENCODE(Disciplines_Struct, structs::Disciplines_Struct); - - memcpy(&eq->values, &emu->values, sizeof(Disciplines_Struct)); - - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrencySell) -{ - ENCODE_LENGTH_EXACT(AltCurrencySellItem_Struct); - SETUP_DIRECT_ENCODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - - OUT(merchant_entity_id); - eq->slot_id = ServerToUnderfootSlot(emu->slot_id); - OUT(charges); - OUT(cost); - FINISH_ENCODE(); -} - -ENCODE(OP_AltCurrency) -{ - EQApplicationPacket *in = *p; - *p = nullptr; - - unsigned char *emu_buffer = in->pBuffer; - uint32 opcode = *((uint32*)emu_buffer); - - if(opcode == 8) { - AltCurrencyPopulate_Struct *populate = (AltCurrencyPopulate_Struct*)emu_buffer; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(structs::AltCurrencyPopulate_Struct) - + sizeof(structs::AltCurrencyPopulateEntry_Struct) * populate->count); - structs::AltCurrencyPopulate_Struct *out_populate = (structs::AltCurrencyPopulate_Struct*)outapp->pBuffer; - - out_populate->opcode = populate->opcode; - out_populate->count = populate->count; - for(uint32 i = 0; i < populate->count; ++i) { - out_populate->entries[i].currency_number = populate->entries[i].currency_number; - out_populate->entries[i].currency_number2 = populate->entries[i].currency_number2; - out_populate->entries[i].item_id = populate->entries[i].item_id; - out_populate->entries[i].item_icon = populate->entries[i].item_icon; - out_populate->entries[i].stack_size = populate->entries[i].stack_size; - out_populate->entries[i].unknown00 = populate->entries[i].unknown00; - } - - dest->FastQueuePacket(&outapp, ack_req); - } else { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_AltCurrency, sizeof(AltCurrencyUpdate_Struct)); - memcpy(outapp->pBuffer, emu_buffer, sizeof(AltCurrencyUpdate_Struct)); - dest->FastQueuePacket(&outapp, ack_req); - } - - //dest->FastQueuePacket(&outapp, ack_req); - delete in; -} - -ENCODE(OP_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - FINISH_ENCODE(); -} - -DECODE(OP_InspectRequest) { - DECODE_LENGTH_EXACT(structs::Inspect_Struct); - SETUP_DIRECT_DECODE(Inspect_Struct, structs::Inspect_Struct); - IN(TargetID); - IN(PlayerID); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_BazaarSearch) -{ - char *Buffer = (char *)__packet->pBuffer; - - uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer); - - if((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct))) - return; - - SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct); - MEMSET_IN(structs::NewBazaarInspect_Struct); - IN(Beginning.Action); - memcpy(emu->Name, eq->Name, sizeof(emu->Name)); - IN(SerialNumber); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_RaidInvite) { - DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); - SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); - - strn0cpy(emu->leader_name, eq->leader_name, 64); - strn0cpy(emu->player_name, eq->player_name, 64); - IN(action); - IN(parameter); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AdventureMerchantSell) { - DECODE_LENGTH_EXACT(structs::Adventure_Sell_Struct); - SETUP_DIRECT_DECODE(Adventure_Sell_Struct, structs::Adventure_Sell_Struct); - - IN(npcid); - emu->slot = UnderfootToServerSlot(eq->slot); - IN(charges); - IN(sell_price); - - FINISH_DIRECT_DECODE(); -} - - -DECODE(OP_ApplyPoison) { - DECODE_LENGTH_EXACT(structs::ApplyPoison_Struct); - SETUP_DIRECT_DECODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); - - emu->inventorySlot = UnderfootToServerSlot(eq->inventorySlot); - IN(success); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemVerifyRequest) { - DECODE_LENGTH_EXACT(structs::ItemVerifyRequest_Struct); - SETUP_DIRECT_DECODE(ItemVerifyRequest_Struct, structs::ItemVerifyRequest_Struct); - - emu->slot = UnderfootToServerSlot(eq->slot); - IN(target); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Consume) { - DECODE_LENGTH_EXACT(structs::Consume_Struct); - SETUP_DIRECT_DECODE(Consume_Struct, structs::Consume_Struct); - - emu->slot = UnderfootToServerSlot(eq->slot); - IN(auto_consumed); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CastSpell) { - DECODE_LENGTH_EXACT(structs::CastSpell_Struct); - SETUP_DIRECT_DECODE(CastSpell_Struct, structs::CastSpell_Struct); - - if(eq->slot == 13) - { - emu->slot = 10; - } - else - { - IN(slot); - } - IN(spell_id); - emu->inventoryslot = UnderfootToServerSlot(eq->inventoryslot); - IN(target_id); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_DeleteItem) -{ - DECODE_LENGTH_EXACT(structs::DeleteItem_Struct); - SETUP_DIRECT_DECODE(DeleteItem_Struct, structs::DeleteItem_Struct); - - emu->from_slot = UnderfootToServerSlot(eq->from_slot); - emu->to_slot = UnderfootToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_MoveItem) -{ - DECODE_LENGTH_EXACT(structs::MoveItem_Struct); - SETUP_DIRECT_DECODE(MoveItem_Struct, structs::MoveItem_Struct); - - _log(NET__ERROR, "Moved item from %u to %u", eq->from_slot, eq->to_slot); - - emu->from_slot = UnderfootToServerSlot(eq->from_slot); - emu->to_slot = UnderfootToServerSlot(eq->to_slot); - IN(number_in_stack); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ItemLinkClick) { - DECODE_LENGTH_EXACT(structs::ItemViewRequest_Struct); - SETUP_DIRECT_DECODE(ItemViewRequest_Struct, structs::ItemViewRequest_Struct); - MEMSET_IN(ItemViewRequest_Struct); - - IN(item_id); - int r; - for (r = 0; r < 5; r++) { - IN(augments[r]); - } - IN(link_hash); - IN(icon); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_SetServerFilter) { - DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); - SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); - int r; - for(r = 0; r < 29; r++) { - IN(filters[r]); - } - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ConsiderCorpse) { DECODE_FORWARD(OP_Consider); } -DECODE(OP_Consider) { - DECODE_LENGTH_EXACT(structs::Consider_Struct); - SETUP_DIRECT_DECODE(Consider_Struct, structs::Consider_Struct); - IN(playerid); - IN(targetid); - IN(faction); - IN(level); - //emu->cur_hp = 1; - //emu->max_hp = 2; - //emu->pvpcon = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerBuy) -{ - DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); - SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); - - IN(npcid); - IN(playerid); - IN(itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ClientUpdate) { - // for some odd reason, there is an extra byte on the end of this on occasion.. - DECODE_LENGTH_ATLEAST(structs::PlayerPositionUpdateClient_Struct); - SETUP_DIRECT_DECODE(PlayerPositionUpdateClient_Struct, structs::PlayerPositionUpdateClient_Struct); - IN(spawn_id); - IN(sequence); - IN(x_pos); - IN(y_pos); - IN(z_pos); - IN(heading); - IN(delta_x); - IN(delta_y); - IN(delta_z); - IN(delta_heading); - IN(animation); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_CharacterCreate) { - DECODE_LENGTH_EXACT(structs::CharCreate_Struct); - SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); - IN(class_); - IN(beardcolor); - IN(beard); - IN(hairstyle); - IN(gender); - IN(race); - - if(RuleB(World, EnableTutorialButton) && eq->tutorial) - emu->start_zone = RuleI(World, TutorialZoneID); - else - emu->start_zone = eq->start_zone; - - IN(haircolor); - IN(deity); - IN(STR); - IN(STA); - IN(AGI); - IN(DEX); - IN(WIS); - IN(INT); - IN(CHA); - IN(face); - IN(eyecolor1); - IN(eyecolor2); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WhoAllRequest) { - DECODE_LENGTH_EXACT(structs::Who_All_Struct); - SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); - - memcpy(emu->whom, eq->whom, sizeof(emu->whom)); - IN(wrace); - IN(wclass); - IN(lvllow); - IN(lvlhigh); - IN(gmlookup); - IN(guildid); - IN(type); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupInvite2) -{ - //_log(NET__ERROR, "Received incoming OP_GroupInvite2. Forwarding"); - DECODE_FORWARD(OP_GroupInvite); -} - -DECODE(OP_GroupInvite) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupInvite"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupInvite_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupInvite_Struct); - memcpy(emu->name1, eq->invitee_name, sizeof(emu->name1)); - memcpy(emu->name2, eq->inviter_name, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupFollow2) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_GroupFollow2"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupFollow_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupFollow_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupDisband) { - //EQApplicationPacket *in = __packet; - //_log(NET__ERROR, "Received incoming OP_Disband"); - //_hex(NET__ERROR, in->pBuffer, in->size); - DECODE_LENGTH_EXACT(structs::GroupGeneric_Struct); - SETUP_DIRECT_DECODE(GroupGeneric_Struct, structs::GroupGeneric_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_GroupCancelInvite) -{ - DECODE_LENGTH_EXACT(structs::GroupCancel_Struct); - SETUP_DIRECT_DECODE(GroupCancel_Struct, structs::GroupCancel_Struct); - memcpy(emu->name1, eq->name1, sizeof(emu->name1)); - memcpy(emu->name2, eq->name2, sizeof(emu->name2)); - IN(toggle); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Buff) { - DECODE_LENGTH_EXACT(structs::SpellBuffFade_Struct_Underfoot); - SETUP_DIRECT_DECODE(SpellBuffFade_Struct, structs::SpellBuffFade_Struct_Underfoot); - IN(entityid); - IN(slot); - IN(level); - IN(effect); - IN(spellid); - IN(duration); - IN(slotid); - IN(bufffade); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ShopPlayerSell) { - DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); - SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); - - IN(npcid); - emu->itemslot = UnderfootToServerSlot(eq->itemslot); - IN(quantity); - IN(price); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Save) { - DECODE_LENGTH_EXACT(structs::Save_Struct); - SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); - memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FindPersonRequest) { - DECODE_LENGTH_EXACT(structs::FindPersonRequest_Struct); - SETUP_DIRECT_DECODE(FindPersonRequest_Struct, structs::FindPersonRequest_Struct); - IN(npc_id); - IN(client_pos.x); - IN(client_pos.y); - IN(client_pos.z); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_WearChange) { - DECODE_LENGTH_EXACT(structs::WearChange_Struct); - SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); - IN(spawn_id); - IN(material); - IN(unknown06); - IN(elite_material); - IN(color.color); - IN(wear_slot_id); - emu->hero_forge_model = 0; - emu->unknown18 = 0; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TraderBuy) -{ - DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); - SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); - MEMSET_IN(TraderBuy_Struct); - - IN(Action); - IN(Price); - IN(TraderID); - memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); - IN(ItemID); - IN(Quantity); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LootItem) { - DECODE_LENGTH_EXACT(structs::LootingItem_Struct); - SETUP_DIRECT_DECODE(LootingItem_Struct, structs::LootingItem_Struct); - IN(lootee); - IN(looter); - emu->slot_id = eq->slot_id - 1; - IN(auto_loot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TributeItem) { - DECODE_LENGTH_EXACT(structs::TributeItem_Struct); - SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); - - emu->slot = UnderfootToServerSlot(eq->slot); - IN(quantity); - IN(tribute_master_id); - IN(tribute_points); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ReadBook) { - DECODE_LENGTH_EXACT(structs::BookRequest_Struct); - SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); - - IN(type); - emu->invslot = UnderfootToServerSlot(eq->invslot); - emu->window = (uint8) eq->window; - strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_TradeSkillCombine) { - DECODE_LENGTH_EXACT(structs::NewCombine_Struct); - SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); - - emu->container_slot = UnderfootToServerSlot(eq->container_slot); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentItem) { - DECODE_LENGTH_EXACT(structs::AugmentItem_Struct); - SETUP_DIRECT_DECODE(AugmentItem_Struct, structs::AugmentItem_Struct); - - emu->container_slot = UnderfootToServerSlot(eq->container_slot); - emu->augment_slot = eq->augment_slot; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AugmentInfo) { - DECODE_LENGTH_EXACT(structs::AugmentInfo_Struct); - SETUP_DIRECT_DECODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); - - IN(itemid); - IN(window); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_FaceChange) { - - DECODE_LENGTH_EXACT(structs::FaceChange_Struct); - SETUP_DIRECT_DECODE(FaceChange_Struct, structs::FaceChange_Struct); - IN(haircolor); - IN(beardcolor); - IN(eyecolor1); - IN(eyecolor2); - IN(hairstyle); - IN(beard); - IN(face); - IN(drakkin_heritage); - IN(drakkin_tattoo); - IN(drakkin_details); - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_LoadSpellSet) -{ - DECODE_LENGTH_EXACT(structs::LoadSpellSet_Struct); - SETUP_DIRECT_DECODE(LoadSpellSet_Struct, structs::LoadSpellSet_Struct); - - for(unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) - if(eq->spell[i]==0) - emu->spell[i] = 0xFFFFFFFF; - else - emu->spell[i] = eq->spell[i]; - - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_Damage) { - DECODE_LENGTH_EXACT(structs::CombatDamage_Struct); - SETUP_DIRECT_DECODE(CombatDamage_Struct, structs::CombatDamage_Struct); - IN(target); - IN(source); - IN(type); - IN(spellid); - IN(damage); - emu->sequence = eq->sequence; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_EnvDamage) { - DECODE_LENGTH_EXACT(structs::EnvDamage2_Struct); - SETUP_DIRECT_DECODE(EnvDamage2_Struct, structs::EnvDamage2_Struct); - IN(id); - IN(damage); - IN(dmgtype); - emu->constant = 0xFFFF; - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_PetCommands) -{ - DECODE_LENGTH_EXACT(structs::PetCommand_Struct); - SETUP_DIRECT_DECODE(PetCommand_Struct, structs::PetCommand_Struct); - - switch(eq->command) - { case 0x00: emu->command = 0x04; // Health break; @@ -3404,574 +3396,712 @@ DECODE(OP_PetCommands) break; default: emu->command = eq->command; - } - OUT(unknown); + } + OUT(unknown); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_ChannelMessage) -{ - unsigned char *__eq_buffer = __packet->pBuffer; - - char *InBuffer = (char *)__eq_buffer; - - char Sender[64]; - char Target[64]; - - VARSTRUCT_DECODE_STRING(Sender, InBuffer); - VARSTRUCT_DECODE_STRING(Target, InBuffer); - - InBuffer += 4; - - uint32 Language = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - uint32 Channel = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - InBuffer += 5; - - uint32 Skill = VARSTRUCT_DECODE_TYPE(uint32, InBuffer); - - __packet->size = sizeof(ChannelMessage_Struct) + strlen(InBuffer) + 1; - __packet->pBuffer = new unsigned char[__packet->size]; - ChannelMessage_Struct *emu = (ChannelMessage_Struct *) __packet->pBuffer; - - strn0cpy(emu->targetname, Target, sizeof(emu->targetname)); - strn0cpy(emu->sender, Target, sizeof(emu->sender)); - emu->language = Language; - emu->chan_num = Channel; - emu->skill_in_language = Skill; - strcpy(emu->message, InBuffer); - - delete [] __eq_buffer; -} - -DECODE(OP_BuffRemoveRequest) -{ - // This is to cater for the fact that short buff box buffs start at 30 as opposed to 25 in prior clients. - // - DECODE_LENGTH_EXACT(structs::BuffRemoveRequest_Struct); - SETUP_DIRECT_DECODE(BuffRemoveRequest_Struct, structs::BuffRemoveRequest_Struct); - - emu->SlotID = (eq->SlotID < 30 ) ? eq->SlotID : (eq->SlotID - 5); - - IN(EntityID); - - FINISH_DIRECT_DECODE(); -} - -uint32 NextItemInstSerialNumber = 1; -uint32 MaxInstances = 2000000000; - -static inline int32 GetNextItemInstSerialNumber() { - - if(NextItemInstSerialNumber >= MaxInstances) - NextItemInstSerialNumber = 1; - else - NextItemInstSerialNumber++; - - return NextItemInstSerialNumber; -} - - -char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { - uint8 null_term = 0; - bool stackable = inst->IsStackable(); - uint32 merchant_slot = inst->GetMerchantSlot(); - uint32 charges = inst->GetCharges(); - if (!stackable && charges > 254) - charges = 0xFFFFFFFF; - - std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); - - const Item_Struct *item = inst->GetItem(); - //_log(NET__ERROR, "Serialize called for: %s", item->Name); - Underfoot::structs::ItemSerializationHeader hdr; - hdr.stacksize = stackable ? charges : 1; - hdr.unknown004 = 0; - - int32 slot_id = ServerToUnderfootSlot(slot_id_in); - - hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; - hdr.price = inst->GetPrice(); - hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); - hdr.unknown020 = 0; - hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; - hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); - hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); - hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; - hdr.unknown044 = 0; - hdr.unknown048 = 0; - hdr.unknown052 = 0; - hdr.unknown056 = 0; - hdr.unknown060 = 0; - hdr.unknown061 = 0; - hdr.unknown062 = 0; - hdr.ItemClass = item->ItemClass; - - ss.write((const char*)&hdr, sizeof(Underfoot::structs::ItemSerializationHeader)); - - if(strlen(item->Name) > 0) - { - ss.write(item->Name, strlen(item->Name)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + FINISH_DIRECT_DECODE(); } - if(strlen(item->Lore) > 0) + DECODE(OP_RaidInvite) { - ss.write(item->Lore, strlen(item->Lore)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } + DECODE_LENGTH_ATLEAST(structs::RaidGeneral_Struct); - if(strlen(item->IDFile) > 0) - { - ss.write(item->IDFile, strlen(item->IDFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); - } - - Underfoot::structs::ItemBodyStruct ibs; - memset(&ibs, 0, sizeof(Underfoot::structs::ItemBodyStruct)); - - uint32 adjusted_slots = item->Slots; - - // Conversions for Ammo and Power Source Slots - if(item->Slots & (1 << 21) & (1 << 22)) - { - // Do nothing - } - else - { - if(item->Slots & (1 << 21)) // Ammo Slot from Database - { - adjusted_slots -= (1 << 21); // Ammo Slot in Titanium - adjusted_slots += (1 << 22); // Ammo Slot in SoF + // This is a switch on the RaidGeneral action + switch (*(uint32 *)__packet->pBuffer) { + case 35: { // raidMOTD + // we don't have a nice macro for this + structs::RaidMOTD_Struct *__eq_buffer = (structs::RaidMOTD_Struct *)__packet->pBuffer; + __eq_buffer->motd[1023] = '\0'; + size_t motd_size = strlen(__eq_buffer->motd) + 1; + __packet->size = sizeof(RaidMOTD_Struct) + motd_size; + __packet->pBuffer = new unsigned char[__packet->size]; + RaidMOTD_Struct *emu = (RaidMOTD_Struct *)__packet->pBuffer; + structs::RaidMOTD_Struct *eq = (structs::RaidMOTD_Struct *)__eq_buffer; + strn0cpy(emu->general.player_name, eq->general.player_name, 64); + strn0cpy(emu->motd, eq->motd, motd_size); + IN(general.action); + IN(general.parameter); + FINISH_DIRECT_DECODE(); + break; + } + case 36: { // raidPlayerNote unhandled + break; + } + default: { + DECODE_LENGTH_EXACT(structs::RaidGeneral_Struct); + SETUP_DIRECT_DECODE(RaidGeneral_Struct, structs::RaidGeneral_Struct); + strn0cpy(emu->leader_name, eq->leader_name, 64); + strn0cpy(emu->player_name, eq->player_name, 64); + IN(action); + IN(parameter); + FINISH_DIRECT_DECODE(); + break; + } } - if(item->Slots & (1 << 22)) // Power Source Slot from Database - { - adjusted_slots -= (1 << 22); // Non Existant Worn Slot in Titanium - adjusted_slots += (1 << 21); // Power Source Slot in SoF + } + + DECODE(OP_ReadBook) + { + DECODE_LENGTH_EXACT(structs::BookRequest_Struct); + SETUP_DIRECT_DECODE(BookRequest_Struct, structs::BookRequest_Struct); + + IN(type); + emu->invslot = UnderfootToServerSlot(eq->invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_Save) + { + DECODE_LENGTH_EXACT(structs::Save_Struct); + SETUP_DIRECT_DECODE(Save_Struct, structs::Save_Struct); + + memcpy(emu->unknown00, eq->unknown00, sizeof(emu->unknown00)); + + FINISH_DIRECT_DECODE(); + } + + DECODE(OP_SetServerFilter) + { + DECODE_LENGTH_EXACT(structs::SetServerFilter_Struct); + SETUP_DIRECT_DECODE(SetServerFilter_Struct, structs::SetServerFilter_Struct); + + int r; + for (r = 0; r < 29; r++) { + IN(filters[r]); } + + FINISH_DIRECT_DECODE(); } - ibs.id = item->ID; - ibs.weight = item->Weight; - ibs.norent = item->NoRent; - ibs.nodrop = item->NoDrop; - ibs.attune = item->Attuneable; - ibs.size = item->Size; - ibs.slots = adjusted_slots; - ibs.price = item->Price; - ibs.icon = item->Icon; - ibs.unknown1 = 1; - ibs.unknown2 = 1; - ibs.BenefitFlag = item->BenefitFlag; - ibs.tradeskills = item->Tradeskills; - ibs.CR = item->CR; - ibs.DR = item->DR; - ibs.PR = item->PR; - ibs.MR = item->MR; - ibs.FR = item->FR; - ibs.SVCorruption = item->SVCorruption; - ibs.AStr = item->AStr; - ibs.ASta = item->ASta; - ibs.AAgi = item->AAgi; - ibs.ADex = item->ADex; - ibs.ACha = item->ACha; - ibs.AInt = item->AInt; - ibs.AWis = item->AWis; - - ibs.HP = item->HP; - ibs.Mana = item->Mana; - ibs.Endur = item->Endur; - ibs.AC = item->AC; - ibs.regen = item->Regen; - ibs.mana_regen = item->ManaRegen; - ibs.end_regen = item->EnduranceRegen; - ibs.Classes = item->Classes; - ibs.Races = item->Races; - ibs.Deity = item->Deity; - ibs.SkillModValue = item->SkillModValue; - ibs.unknown5 = 0; - ibs.SkillModType = item->SkillModType; - ibs.BaneDmgRace = item->BaneDmgRace; - ibs.BaneDmgBody = item->BaneDmgBody; - ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; - ibs.BaneDmgAmt = item->BaneDmgAmt; - ibs.Magic = item->Magic; - ibs.CastTime_ = item->CastTime_; - ibs.ReqLevel = item->ReqLevel; - ibs.RecLevel = item->RecLevel; - ibs.RecSkill = item->RecSkill; - ibs.BardType = item->BardType; - ibs.BardValue = item->BardValue; - ibs.Light = item->Light; - ibs.Delay = item->Delay; - ibs.ElemDmgType = item->ElemDmgType; - ibs.ElemDmgAmt = item->ElemDmgAmt; - ibs.Range = item->Range; - ibs.Damage = item->Damage; - ibs.Color = item->Color; - ibs.ItemType = item->ItemType; - ibs.Material = item->Material; - ibs.unknown7 = 0; - ibs.EliteMaterial = item->EliteMaterial; - ibs.SellRate = item->SellRate; - - ibs.CombatEffects = item->CombatEffects; - ibs.Shielding = item->Shielding; - ibs.StunResist = item->StunResist; - ibs.StrikeThrough = item->StrikeThrough; - ibs.ExtraDmgSkill = item->ExtraDmgSkill; - ibs.ExtraDmgAmt = item->ExtraDmgAmt; - ibs.SpellShield = item->SpellShield; - ibs.Avoidance = item->Avoidance; - ibs.Accuracy = item->Accuracy; - ibs.FactionAmt1 = item->FactionAmt1; - ibs.FactionMod1 = item->FactionMod1; - ibs.FactionAmt2 = item->FactionAmt2; - ibs.FactionMod2 = item->FactionMod2; - ibs.FactionAmt3 = item->FactionAmt3; - ibs.FactionMod3 = item->FactionMod3; - ibs.FactionAmt4 = item->FactionAmt4; - ibs.FactionMod4 = item->FactionMod4; - - ss.write((const char*)&ibs, sizeof(Underfoot::structs::ItemBodyStruct)); - - //charm text - if(strlen(item->CharmFile) > 0) + DECODE(OP_ShopPlayerBuy) { - ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::Merchant_Sell_Struct); + SETUP_DIRECT_DECODE(Merchant_Sell_Struct, structs::Merchant_Sell_Struct); + + IN(npcid); + IN(playerid); + IN(itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); } - Underfoot::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); - - isbs.augtype = item->AugType; - isbs.augrestrict = item->AugRestrict; - - for(int x = 0; x < 5; ++x) + DECODE(OP_ShopPlayerSell) { - isbs.augslots[x].type = item->AugSlotType[x]; - isbs.augslots[x].visible = item->AugSlotVisible[x]; - isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + DECODE_LENGTH_EXACT(structs::Merchant_Purchase_Struct); + SETUP_DIRECT_DECODE(Merchant_Purchase_Struct, structs::Merchant_Purchase_Struct); + + IN(npcid); + emu->itemslot = UnderfootToServerSlot(eq->itemslot); + IN(quantity); + IN(price); + + FINISH_DIRECT_DECODE(); } - isbs.ldonpoint_type = item->PointType; - isbs.ldontheme = item->LDoNTheme; - isbs.ldonprice = item->LDoNPrice; - isbs.ldonsellbackrate = item->LDoNSellBackRate; - isbs.ldonsold = item->LDoNSold; - - isbs.bagtype = item->BagType; - isbs.bagslots = item->BagSlots; - isbs.bagsize = item->BagSize; - isbs.wreduction = item->BagWR; - - isbs.book = item->Book; - isbs.booktype = item->BookType; - - ss.write((const char*)&isbs, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); - - if(strlen(item->Filename) > 0) + DECODE(OP_TraderBuy) { - ss.write((const char*)item->Filename, strlen(item->Filename)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::TraderBuy_Struct); + SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct); + MEMSET_IN(TraderBuy_Struct); + + IN(Action); + IN(Price); + IN(TraderID); + memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName)); + IN(ItemID); + IN(Quantity); + + FINISH_DIRECT_DECODE(); } - Underfoot::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); - - itbs.loregroup = item->LoreGroup; - itbs.artifact = item->ArtifactFlag; - itbs.summonedflag = item->SummonedFlag; - itbs.favor = item->Favor; - itbs.fvnodrop = item->FVNoDrop; - itbs.dotshield = item->DotShielding; - itbs.atk = item->Attack; - itbs.haste = item->Haste; - itbs.damage_shield = item->DamageShield; - itbs.guildfavor = item->GuildFavor; - itbs.augdistil = item->AugDistiller; - itbs.no_pet = item->NoPet; - - itbs.potion_belt_enabled = item->PotionBelt; - itbs.potion_belt_slots = item->PotionBeltSlots; - itbs.stacksize = stackable ? item->StackSize : 0; - itbs.no_transfer = item->NoTransfer; - itbs.expendablearrow = item->ExpendableArrow; - - ss.write((const char*)&itbs, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); - - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 0; - - Underfoot::structs::ClickEffectStruct ices; - memset(&ices, 0, sizeof(Underfoot::structs::ClickEffectStruct)); - - ices.effect = item->Click.Effect; - ices.level2 = item->Click.Level2; - ices.type = item->Click.Type; - ices.level = item->Click.Level; - ices.max_charges = item->MaxCharges; - ices.cast_time = item->CastTime; - ices.recast = item->RecastDelay; - ices.recast_type = item->RecastType; - - ss.write((const char*)&ices, sizeof(Underfoot::structs::ClickEffectStruct)); - - if(strlen(item->ClickName) > 0) + DECODE(OP_TradeSkillCombine) { - ss.write((const char*)item->ClickName, strlen(item->ClickName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::NewCombine_Struct); + SETUP_DIRECT_DECODE(NewCombine_Struct, structs::NewCombine_Struct); + + emu->container_slot = UnderfootToServerSlot(eq->container_slot); + IN(guildtribute_slot); + + FINISH_DIRECT_DECODE(); } - ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 - - Underfoot::structs::ProcEffectStruct ipes; - memset(&ipes, 0, sizeof(Underfoot::structs::ProcEffectStruct)); - - ipes.effect = item->Proc.Effect; - ipes.level2 = item->Proc.Level2; - ipes.type = item->Proc.Type; - ipes.level = item->Proc.Level; - ipes.procrate = item->ProcRate; - - ss.write((const char*)&ipes, sizeof(Underfoot::structs::ProcEffectStruct)); - - if(strlen(item->ProcName) > 0) + DECODE(OP_TributeItem) { - ss.write((const char*)item->ProcName, strlen(item->ProcName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::TributeItem_Struct); + SETUP_DIRECT_DECODE(TributeItem_Struct, structs::TributeItem_Struct); + + emu->slot = UnderfootToServerSlot(eq->slot); + IN(quantity); + IN(tribute_master_id); + IN(tribute_points); + + FINISH_DIRECT_DECODE(); } - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 - - Underfoot::structs::WornEffectStruct iwes; - memset(&iwes, 0, sizeof(Underfoot::structs::WornEffectStruct)); - - iwes.effect = item->Worn.Effect; - iwes.level2 = item->Worn.Level2; - iwes.type = item->Worn.Type; - iwes.level = item->Worn.Level; - - ss.write((const char*)&iwes, sizeof(Underfoot::structs::WornEffectStruct)); - - if(strlen(item->WornName) > 0) + DECODE(OP_WearChange) { - ss.write((const char*)item->WornName, strlen(item->WornName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::WearChange_Struct); + SETUP_DIRECT_DECODE(WearChange_Struct, structs::WearChange_Struct); + + IN(spawn_id); + IN(material); + IN(unknown06); + IN(elite_material); + IN(color.color); + IN(wear_slot_id); + emu->hero_forge_model = 0; + emu->unknown18 = 0; + + FINISH_DIRECT_DECODE(); } - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - Underfoot::structs::WornEffectStruct ifes; - memset(&ifes, 0, sizeof(Underfoot::structs::WornEffectStruct)); - - ifes.effect = item->Focus.Effect; - ifes.level2 = item->Focus.Level2; - ifes.type = item->Focus.Type; - ifes.level = item->Focus.Level; - - ss.write((const char*)&ifes, sizeof(Underfoot::structs::WornEffectStruct)); - - if(strlen(item->FocusName) > 0) + DECODE(OP_WhoAllRequest) { - ss.write((const char*)item->FocusName, strlen(item->FocusName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + DECODE_LENGTH_EXACT(structs::Who_All_Struct); + SETUP_DIRECT_DECODE(Who_All_Struct, structs::Who_All_Struct); + + memcpy(emu->whom, eq->whom, sizeof(emu->whom)); + IN(wrace); + IN(wclass); + IN(lvllow); + IN(lvlhigh); + IN(gmlookup); + IN(guildid); + IN(type); + + FINISH_DIRECT_DECODE(); } - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 +// file scope helper methods + uint32 NextItemInstSerialNumber = 1; + uint32 MaxInstances = 2000000000; - Underfoot::structs::WornEffectStruct ises; - memset(&ises, 0, sizeof(Underfoot::structs::WornEffectStruct)); - - ises.effect = item->Scroll.Effect; - ises.level2 = item->Scroll.Level2; - ises.type = item->Scroll.Type; - ises.level = item->Scroll.Level; - - ss.write((const char*)&ises, sizeof(Underfoot::structs::WornEffectStruct)); - - if(strlen(item->ScrollName) > 0) + static inline int32 GetNextItemInstSerialNumber() { - ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); - ss.write((const char*)&null_term, sizeof(uint8)); - } - else - { - ss.write((const char*)&null_term, sizeof(uint8)); + if (NextItemInstSerialNumber >= MaxInstances) + NextItemInstSerialNumber = 1; + else + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; } - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - // Bard Effect? - Underfoot::structs::WornEffectStruct ibes; - memset(&ibes, 0, sizeof(Underfoot::structs::WornEffectStruct)); - - ibes.effect = item->Bard.Effect; - ibes.level2 = item->Bard.Level2; - ibes.type = item->Bard.Type; - ibes.level = item->Bard.Level; - //ibes.unknown6 = 0xffffffff; - - ss.write((const char*)&ibes, sizeof(Underfoot::structs::WornEffectStruct)); - - /* - if(strlen(item->BardName) > 0) + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) { + uint8 null_term = 0; + bool stackable = inst->IsStackable(); + uint32 merchant_slot = inst->GetMerchantSlot(); + uint32 charges = inst->GetCharges(); + if (!stackable && charges > 254) + charges = 0xFFFFFFFF; + + std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary); + + const Item_Struct *item = inst->GetItem(); + //_log(NET__ERROR, "Serialize called for: %s", item->Name); + Underfoot::structs::ItemSerializationHeader hdr; + hdr.stacksize = stackable ? charges : 1; + hdr.unknown004 = 0; + + int32 slot_id = ServerToUnderfootSlot(slot_id_in); + + hdr.slot = (merchant_slot == 0) ? slot_id : merchant_slot; + hdr.price = inst->GetPrice(); + hdr.merchant_slot = (merchant_slot == 0) ? 1 : inst->GetMerchantCount(); + hdr.unknown020 = 0; + hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; + hdr.unknown028 = 0; + hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); + hdr.inst_nodrop = inst->IsInstNoDrop() ? 1 : 0; + hdr.unknown044 = 0; + hdr.unknown048 = 0; + hdr.unknown052 = 0; + hdr.unknown056 = 0; + hdr.unknown060 = 0; + hdr.unknown061 = 0; + hdr.unknown062 = 0; + hdr.ItemClass = item->ItemClass; + + ss.write((const char*)&hdr, sizeof(Underfoot::structs::ItemSerializationHeader)); + + if (strlen(item->Name) > 0) + { + ss.write(item->Name, strlen(item->Name)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + if (strlen(item->Lore) > 0) + { + ss.write(item->Lore, strlen(item->Lore)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + if (strlen(item->IDFile) > 0) + { + ss.write(item->IDFile, strlen(item->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + Underfoot::structs::ItemBodyStruct ibs; + memset(&ibs, 0, sizeof(Underfoot::structs::ItemBodyStruct)); + + ibs.id = item->ID; + ibs.weight = item->Weight; + ibs.norent = item->NoRent; + ibs.nodrop = item->NoDrop; + ibs.attune = item->Attuneable; + ibs.size = item->Size; + ibs.slots = SwapBits21and22(item->Slots); + ibs.price = item->Price; + ibs.icon = item->Icon; + ibs.unknown1 = 1; + ibs.unknown2 = 1; + ibs.BenefitFlag = item->BenefitFlag; + ibs.tradeskills = item->Tradeskills; + ibs.CR = item->CR; + ibs.DR = item->DR; + ibs.PR = item->PR; + ibs.MR = item->MR; + ibs.FR = item->FR; + ibs.SVCorruption = item->SVCorruption; + ibs.AStr = item->AStr; + ibs.ASta = item->ASta; + ibs.AAgi = item->AAgi; + ibs.ADex = item->ADex; + ibs.ACha = item->ACha; + ibs.AInt = item->AInt; + ibs.AWis = item->AWis; + + ibs.HP = item->HP; + ibs.Mana = item->Mana; + ibs.Endur = item->Endur; + ibs.AC = item->AC; + ibs.regen = item->Regen; + ibs.mana_regen = item->ManaRegen; + ibs.end_regen = item->EnduranceRegen; + ibs.Classes = item->Classes; + ibs.Races = item->Races; + ibs.Deity = item->Deity; + ibs.SkillModValue = item->SkillModValue; + ibs.unknown5 = 0; + ibs.SkillModType = item->SkillModType; + ibs.BaneDmgRace = item->BaneDmgRace; + ibs.BaneDmgBody = item->BaneDmgBody; + ibs.BaneDmgRaceAmt = item->BaneDmgRaceAmt; + ibs.BaneDmgAmt = item->BaneDmgAmt; + ibs.Magic = item->Magic; + ibs.CastTime_ = item->CastTime_; + ibs.ReqLevel = item->ReqLevel; + ibs.RecLevel = item->RecLevel; + ibs.RecSkill = item->RecSkill; + ibs.BardType = item->BardType; + ibs.BardValue = item->BardValue; + ibs.Light = item->Light; + ibs.Delay = item->Delay; + ibs.ElemDmgType = item->ElemDmgType; + ibs.ElemDmgAmt = item->ElemDmgAmt; + ibs.Range = item->Range; + ibs.Damage = item->Damage; + ibs.Color = item->Color; + ibs.ItemType = item->ItemType; + ibs.Material = item->Material; + ibs.unknown7 = 0; + ibs.EliteMaterial = item->EliteMaterial; + ibs.SellRate = item->SellRate; + + ibs.CombatEffects = item->CombatEffects; + ibs.Shielding = item->Shielding; + ibs.StunResist = item->StunResist; + ibs.StrikeThrough = item->StrikeThrough; + ibs.ExtraDmgSkill = item->ExtraDmgSkill; + ibs.ExtraDmgAmt = item->ExtraDmgAmt; + ibs.SpellShield = item->SpellShield; + ibs.Avoidance = item->Avoidance; + ibs.Accuracy = item->Accuracy; + ibs.FactionAmt1 = item->FactionAmt1; + ibs.FactionMod1 = item->FactionMod1; + ibs.FactionAmt2 = item->FactionAmt2; + ibs.FactionMod2 = item->FactionMod2; + ibs.FactionAmt3 = item->FactionAmt3; + ibs.FactionMod3 = item->FactionMod3; + ibs.FactionAmt4 = item->FactionAmt4; + ibs.FactionMod4 = item->FactionMod4; + + ss.write((const char*)&ibs, sizeof(Underfoot::structs::ItemBodyStruct)); + + //charm text + if (strlen(item->CharmFile) > 0) + { + ss.write((const char*)item->CharmFile, strlen(item->CharmFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + Underfoot::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); + + isbs.augtype = item->AugType; + isbs.augrestrict = item->AugRestrict; + + for (int x = 0; x < 5; ++x) + { + isbs.augslots[x].type = item->AugSlotType[x]; + isbs.augslots[x].visible = item->AugSlotVisible[x]; + isbs.augslots[x].unknown = item->AugSlotUnk2[x]; + } + + isbs.ldonpoint_type = item->PointType; + isbs.ldontheme = item->LDoNTheme; + isbs.ldonprice = item->LDoNPrice; + isbs.ldonsellbackrate = item->LDoNSellBackRate; + isbs.ldonsold = item->LDoNSold; + + isbs.bagtype = item->BagType; + isbs.bagslots = item->BagSlots; + isbs.bagsize = item->BagSize; + isbs.wreduction = item->BagWR; + + isbs.book = item->Book; + isbs.booktype = item->BookType; + + ss.write((const char*)&isbs, sizeof(Underfoot::structs::ItemSecondaryBodyStruct)); + + if (strlen(item->Filename) > 0) + { + ss.write((const char*)item->Filename, strlen(item->Filename)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + Underfoot::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); + + itbs.loregroup = item->LoreGroup; + itbs.artifact = item->ArtifactFlag; + itbs.summonedflag = item->SummonedFlag; + itbs.favor = item->Favor; + itbs.fvnodrop = item->FVNoDrop; + itbs.dotshield = item->DotShielding; + itbs.atk = item->Attack; + itbs.haste = item->Haste; + itbs.damage_shield = item->DamageShield; + itbs.guildfavor = item->GuildFavor; + itbs.augdistil = item->AugDistiller; + itbs.no_pet = item->NoPet; + + itbs.potion_belt_enabled = item->PotionBelt; + itbs.potion_belt_slots = item->PotionBeltSlots; + itbs.stacksize = stackable ? item->StackSize : 0; + itbs.no_transfer = item->NoTransfer; + itbs.expendablearrow = item->ExpendableArrow; + + ss.write((const char*)&itbs, sizeof(Underfoot::structs::ItemTertiaryBodyStruct)); + + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 0; + + Underfoot::structs::ClickEffectStruct ices; + memset(&ices, 0, sizeof(Underfoot::structs::ClickEffectStruct)); + + ices.effect = item->Click.Effect; + ices.level2 = item->Click.Level2; + ices.type = item->Click.Type; + ices.level = item->Click.Level; + ices.max_charges = item->MaxCharges; + ices.cast_time = item->CastTime; + ices.recast = item->RecastDelay; + ices.recast_type = item->RecastType; + + ss.write((const char*)&ices, sizeof(Underfoot::structs::ClickEffectStruct)); + + if (strlen(item->ClickName) > 0) + { + ss.write((const char*)item->ClickName, strlen(item->ClickName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // clickunk7 + + Underfoot::structs::ProcEffectStruct ipes; + memset(&ipes, 0, sizeof(Underfoot::structs::ProcEffectStruct)); + + ipes.effect = item->Proc.Effect; + ipes.level2 = item->Proc.Level2; + ipes.type = item->Proc.Type; + ipes.level = item->Proc.Level; + ipes.procrate = item->ProcRate; + + ss.write((const char*)&ipes, sizeof(Underfoot::structs::ProcEffectStruct)); + + if (strlen(item->ProcName) > 0) + { + ss.write((const char*)item->ProcName, strlen(item->ProcName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown5 + + Underfoot::structs::WornEffectStruct iwes; + memset(&iwes, 0, sizeof(Underfoot::structs::WornEffectStruct)); + + iwes.effect = item->Worn.Effect; + iwes.level2 = item->Worn.Level2; + iwes.type = item->Worn.Type; + iwes.level = item->Worn.Level; + + ss.write((const char*)&iwes, sizeof(Underfoot::structs::WornEffectStruct)); + + if (strlen(item->WornName) > 0) + { + ss.write((const char*)item->WornName, strlen(item->WornName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + Underfoot::structs::WornEffectStruct ifes; + memset(&ifes, 0, sizeof(Underfoot::structs::WornEffectStruct)); + + ifes.effect = item->Focus.Effect; + ifes.level2 = item->Focus.Level2; + ifes.type = item->Focus.Type; + ifes.level = item->Focus.Level; + + ss.write((const char*)&ifes, sizeof(Underfoot::structs::WornEffectStruct)); + + if (strlen(item->FocusName) > 0) + { + ss.write((const char*)item->FocusName, strlen(item->FocusName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + Underfoot::structs::WornEffectStruct ises; + memset(&ises, 0, sizeof(Underfoot::structs::WornEffectStruct)); + + ises.effect = item->Scroll.Effect; + ises.level2 = item->Scroll.Level2; + ises.type = item->Scroll.Type; + ises.level = item->Scroll.Level; + + ss.write((const char*)&ises, sizeof(Underfoot::structs::WornEffectStruct)); + + if (strlen(item->ScrollName) > 0) + { + ss.write((const char*)item->ScrollName, strlen(item->ScrollName)); + ss.write((const char*)&null_term, sizeof(uint8)); + } + else + { + ss.write((const char*)&null_term, sizeof(uint8)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + // Bard Effect? + Underfoot::structs::WornEffectStruct ibes; + memset(&ibes, 0, sizeof(Underfoot::structs::WornEffectStruct)); + + ibes.effect = item->Bard.Effect; + ibes.level2 = item->Bard.Level2; + ibes.type = item->Bard.Type; + ibes.level = item->Bard.Level; + //ibes.unknown6 = 0xffffffff; + + ss.write((const char*)&ibes, sizeof(Underfoot::structs::WornEffectStruct)); + + /* + if(strlen(item->BardName) > 0) + { ss.write((const char*)item->BardName, strlen(item->BardName)); ss.write((const char*)&null_term, sizeof(uint8)); - } - else */ + } + else */ ss.write((const char*)&null_term, sizeof(uint8)); - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects - Underfoot::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); + Underfoot::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); - iqbs.scriptfileid = item->ScriptFileID; - iqbs.quest_item = item->QuestItemFlag; - iqbs.unknown15 = 0; + iqbs.scriptfileid = item->ScriptFileID; + iqbs.quest_item = item->QuestItemFlag; + iqbs.unknown15 = 0; - iqbs.Purity = item->Purity; - iqbs.BackstabDmg = item->BackstabDmg; - iqbs.DSMitigation = item->DSMitigation; - iqbs.HeroicStr = item->HeroicStr; - iqbs.HeroicInt = item->HeroicInt; - iqbs.HeroicWis = item->HeroicWis; - iqbs.HeroicAgi = item->HeroicAgi; - iqbs.HeroicDex = item->HeroicDex; - iqbs.HeroicSta = item->HeroicSta; - iqbs.HeroicCha = item->HeroicCha; - iqbs.HeroicMR = item->HeroicMR; - iqbs.HeroicFR = item->HeroicFR; - iqbs.HeroicCR = item->HeroicCR; - iqbs.HeroicDR = item->HeroicDR; - iqbs.HeroicPR = item->HeroicPR; - iqbs.HeroicSVCorrup = item->HeroicSVCorrup; - iqbs.HealAmt = item->HealAmt; - iqbs.SpellDmg = item->SpellDmg; - iqbs.clairvoyance = item->Clairvoyance; + iqbs.Purity = item->Purity; + iqbs.BackstabDmg = item->BackstabDmg; + iqbs.DSMitigation = item->DSMitigation; + iqbs.HeroicStr = item->HeroicStr; + iqbs.HeroicInt = item->HeroicInt; + iqbs.HeroicWis = item->HeroicWis; + iqbs.HeroicAgi = item->HeroicAgi; + iqbs.HeroicDex = item->HeroicDex; + iqbs.HeroicSta = item->HeroicSta; + iqbs.HeroicCha = item->HeroicCha; + iqbs.HeroicMR = item->HeroicMR; + iqbs.HeroicFR = item->HeroicFR; + iqbs.HeroicCR = item->HeroicCR; + iqbs.HeroicDR = item->HeroicDR; + iqbs.HeroicPR = item->HeroicPR; + iqbs.HeroicSVCorrup = item->HeroicSVCorrup; + iqbs.HealAmt = item->HealAmt; + iqbs.SpellDmg = item->SpellDmg; + iqbs.clairvoyance = item->Clairvoyance; - iqbs.subitem_count = 0; + iqbs.subitem_count = 0; - char *SubSerializations[10]; // + char *SubSerializations[10]; // - uint32 SubLengths[10]; + uint32 SubLengths[10]; - for(int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { + for (int x = SUB_BEGIN; x < EmuConstants::ITEM_CONTAINER_SIZE; ++x) { - SubSerializations[x] = nullptr; + SubSerializations[x] = nullptr; - const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); + const ItemInst* subitem = ((const ItemInst*)inst)->GetItem(x); - if(subitem) { + if (subitem) { - int SubSlotNumber; + int SubSlotNumber; - iqbs.subitem_count++; + iqbs.subitem_count++; - if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? - //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); - SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); - else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) - //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); - else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) - //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); - SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); - else - SubSlotNumber = slot_id_in; // ??????? + if (slot_id_in >= EmuConstants::GENERAL_BEGIN && slot_id_in <= EmuConstants::GENERAL_END) // (< 30) - no cursor? + //SubSlotNumber = (((slot_id_in + 3) * 10) + x + 1); + SubSlotNumber = (((slot_id_in + 3) * EmuConstants::ITEM_CONTAINER_SIZE) + x + 1); + else if (slot_id_in >= EmuConstants::BANK_BEGIN && slot_id_in <= EmuConstants::BANK_END) + //SubSlotNumber = (((slot_id_in - 2000) * 10) + 2030 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::BANK_BAGS_BEGIN + x); + else if (slot_id_in >= EmuConstants::SHARED_BANK_BEGIN && slot_id_in <= EmuConstants::SHARED_BANK_END) + //SubSlotNumber = (((slot_id_in - 2500) * 10) + 2530 + x + 1); + SubSlotNumber = (((slot_id_in - EmuConstants::SHARED_BANK_BEGIN) * EmuConstants::ITEM_CONTAINER_SIZE) + EmuConstants::SHARED_BANK_BAGS_BEGIN + x); + else + SubSlotNumber = slot_id_in; // ??????? - /* - // TEST CODE: - SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); - */ + /* + // TEST CODE: + SubSlotNumber = Inventory::CalcSlotID(slot_id_in, x); + */ - SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + SubSerializations[x] = SerializeItem(subitem, SubSlotNumber, &SubLengths[x], depth + 1); + } } + + ss.write((const char*)&iqbs, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); + + for (int x = 0; x < 10; ++x) { + + if (SubSerializations[x]) { + + ss.write((const char*)&x, sizeof(uint32)); + + ss.write(SubSerializations[x], SubLengths[x]); + + safe_delete_array(SubSerializations[x]); + } + } + + char* item_serial = new char[ss.tellp()]; + memset(item_serial, 0, ss.tellp()); + memcpy(item_serial, ss.str().c_str(), ss.tellp()); + + *length = ss.tellp(); + return item_serial; } - ss.write((const char*)&iqbs, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); + static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot) + { + uint32 UnderfootSlot = 0; - for(int x = 0; x < 10; ++x) { + if (ServerSlot >= MainAmmo && ServerSlot <= 53) // Cursor/Ammo/Power Source and Normal Inventory Slots + UnderfootSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::GENERAL_BAGS_BEGIN && ServerSlot <= EmuConstants::CURSOR_BAG_END) + UnderfootSlot = ServerSlot + 11; + else if (ServerSlot >= EmuConstants::BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::BANK_BAGS_END) + UnderfootSlot = ServerSlot + 1; + else if (ServerSlot >= EmuConstants::SHARED_BANK_BAGS_BEGIN && ServerSlot <= EmuConstants::SHARED_BANK_BAGS_END) + UnderfootSlot = ServerSlot + 1; + else if (ServerSlot == MainPowerSource) + UnderfootSlot = slots::MainPowerSource; + else + UnderfootSlot = ServerSlot; - if(SubSerializations[x]) { - - ss.write((const char*)&x, sizeof(uint32)); - - ss.write(SubSerializations[x], SubLengths[x]); - - safe_delete_array(SubSerializations[x]); - } + return UnderfootSlot; } - char* item_serial = new char[ss.tellp()]; - memset(item_serial, 0, ss.tellp()); - memcpy(item_serial, ss.str().c_str(), ss.tellp()); + static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse) + { + //uint32 UnderfootCorpse; + return (ServerCorpse + 1); + } - *length = ss.tellp(); - return item_serial; + static inline uint32 UnderfootToServerSlot(uint32 UnderfootSlot) + { + uint32 ServerSlot = 0; + + if (UnderfootSlot >= slots::MainAmmo && UnderfootSlot <= consts::CORPSE_END) // Cursor/Ammo/Power Source and Normal Inventory Slots + ServerSlot = UnderfootSlot - 1; + else if (UnderfootSlot >= consts::GENERAL_BAGS_BEGIN && UnderfootSlot <= consts::CURSOR_BAG_END) + ServerSlot = UnderfootSlot - 11; + else if (UnderfootSlot >= consts::BANK_BAGS_BEGIN && UnderfootSlot <= consts::BANK_BAGS_END) + ServerSlot = UnderfootSlot - 1; + else if (UnderfootSlot >= consts::SHARED_BANK_BAGS_BEGIN && UnderfootSlot <= consts::SHARED_BANK_BAGS_END) + ServerSlot = UnderfootSlot - 1; + else if (UnderfootSlot == slots::MainPowerSource) + ServerSlot = MainPowerSource; + else + ServerSlot = UnderfootSlot; + + return ServerSlot; + } + + static inline uint32 UnderfootToServerCorpseSlot(uint32 UnderfootCorpse) + { + //uint32 ServerCorpse; + return (UnderfootCorpse - 1); + } } - -DECODE(OP_AltCurrencySellSelection) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySelectItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySelectItem_Struct, structs::AltCurrencySelectItem_Struct); - IN(merchant_entity_id); - emu->slot_id = UnderfootToServerSlot(eq->slot_id); - FINISH_DIRECT_DECODE(); -} - -DECODE(OP_AltCurrencySell) -{ - DECODE_LENGTH_EXACT(structs::AltCurrencySellItem_Struct); - SETUP_DIRECT_DECODE(AltCurrencySellItem_Struct, structs::AltCurrencySellItem_Struct); - IN(merchant_entity_id); - emu->slot_id = UnderfootToServerSlot(eq->slot_id); - IN(charges); - IN(cost); - FINISH_DIRECT_DECODE(); -} - -} //end namespace Underfoot +// end namespace Underfoot diff --git a/common/patches/underfoot_constants.h b/common/patches/underfoot_constants.h index d34eaeb5b..b89a4f255 100644 --- a/common/patches/underfoot_constants.h +++ b/common/patches/underfoot_constants.h @@ -22,7 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #ifndef Underfoot_CONSTANTS_H_ #define Underfoot_CONSTANTS_H_ -#include "../common/types.h" +#include "../types.h" namespace Underfoot { namespace maps { @@ -181,6 +181,7 @@ namespace Underfoot { namespace limits { static const bool ALLOWS_EMPTY_BAG_IN_BAG = false; + static const bool ALLOWS_CLICK_CAST_FROM_BAG = false; static const bool COIN_HAS_WEIGHT = false; } diff --git a/common/patches/underfoot_ops.h b/common/patches/underfoot_ops.h index 7aa9caf50..38cfb79e3 100644 --- a/common/patches/underfoot_ops.h +++ b/common/patches/underfoot_ops.h @@ -1,128 +1,127 @@ - -//list of packets we need to encode on the way out: - -E(OP_SendCharInfo) -E(OP_ZoneServerInfo) -E(OP_SendAATable) -E(OP_PlayerProfile) -E(OP_ZoneEntry) -E(OP_CharInventory) -E(OP_NewZone) -E(OP_SpawnDoor) -E(OP_GroundSpawn) -E(OP_SendZonepoints) -E(OP_NewSpawn) -E(OP_ZoneSpawns) -E(OP_ItemLinkResponse) -E(OP_ItemPacket) -E(OP_GuildMemberList) -E(OP_Illusion) -E(OP_ManaChange) -E(OP_ClientUpdate) -E(OP_LeadershipExpUpdate) -E(OP_ExpansionInfo) -E(OP_LogServer) -E(OP_Damage) -E(OP_Buff) +// out-going packets that require an ENCODE translation: E(OP_Action) -E(OP_Consider) -E(OP_CancelTrade) -E(OP_ShopPlayerSell) -E(OP_DeleteItem) -E(OP_ItemVerifyReply) -E(OP_DeleteCharge) -E(OP_MoveItem) -E(OP_OpenNewTasksWindow) -E(OP_BazaarSearch) -E(OP_Trader) -E(OP_TraderBuy) -E(OP_LootItem) -E(OP_TributeItem) -E(OP_SomeItemPacketMaybe) -E(OP_ReadBook) -E(OP_Stun) -E(OP_ZonePlayerToBind) E(OP_AdventureMerchantSell) -E(OP_RaidUpdate) -E(OP_RaidJoin) -E(OP_VetRewardsAvaliable) -E(OP_InspectRequest) -E(OP_GroupInvite) -E(OP_GroupFollow) -E(OP_GroupFollow2) -E(OP_GroupUpdate) -E(OP_GroupCancelInvite) -E(OP_WhoAllResponse) -E(OP_Track) -E(OP_ShopPlayerBuy) -E(OP_PetBuffWindow) -E(OP_OnLevelMessage) -E(OP_Barter) +E(OP_AltCurrency) +E(OP_AltCurrencySell) E(OP_ApplyPoison) +E(OP_Barter) +E(OP_BazaarSearch) +E(OP_Buff) +E(OP_BuffCreate) +E(OP_CancelTrade) E(OP_ChannelMessage) -E(OP_GuildsList) +E(OP_CharInventory) +E(OP_ClientUpdate) +E(OP_Consider) +E(OP_Damage) +E(OP_DeleteCharge) +E(OP_DeleteItem) +E(OP_DisciplineUpdate) +E(OP_DzCompass) E(OP_DzExpeditionEndsWarning) E(OP_DzExpeditionInfo) -E(OP_DzCompass) -E(OP_DzMemberList) E(OP_DzExpeditionList) -E(OP_DzLeaderStatus) E(OP_DzJoinExpeditionConfirm) -E(OP_TargetBuffs) -E(OP_BuffCreate) -E(OP_SpawnAppearance) -E(OP_RespondAA) -E(OP_DisciplineUpdate) -E(OP_AltCurrencySell) -E(OP_AltCurrency) -E(OP_WearChange) +E(OP_DzLeaderStatus) +E(OP_DzMemberList) +E(OP_ExpansionInfo) +E(OP_GroundSpawn) +E(OP_GroupCancelInvite) +E(OP_GroupFollow) +E(OP_GroupFollow2) +E(OP_GroupInvite) +E(OP_GroupUpdate) +E(OP_GuildMemberList) +E(OP_GuildsList) +E(OP_Illusion) +E(OP_InspectBuffs) +E(OP_InspectRequest) +E(OP_ItemLinkResponse) +E(OP_ItemPacket) +E(OP_ItemVerifyReply) +E(OP_LeadershipExpUpdate) +E(OP_LogServer) +E(OP_LootItem) +E(OP_ManaChange) E(OP_MercenaryDataResponse) E(OP_MercenaryDataUpdate) -//list of packets we need to decode on the way in: -D(OP_SetServerFilter) -D(OP_CharacterCreate) -D(OP_ItemLinkClick) -D(OP_ConsiderCorpse) -D(OP_Consider) -D(OP_ClientUpdate) -D(OP_MoveItem) -D(OP_WhoAllRequest) +E(OP_MoveItem) +E(OP_NewSpawn) +E(OP_NewZone) +E(OP_OnLevelMessage) +E(OP_OpenNewTasksWindow) +E(OP_PetBuffWindow) +E(OP_PlayerProfile) +E(OP_RaidJoin) +E(OP_RaidUpdate) +E(OP_ReadBook) +E(OP_RespondAA) +E(OP_SendAATable) +E(OP_SendCharInfo) +E(OP_SendZonepoints) +E(OP_ShopPlayerBuy) +E(OP_ShopPlayerSell) +E(OP_SomeItemPacketMaybe) +E(OP_SpawnAppearance) +E(OP_SpawnDoor) +E(OP_Stun) +E(OP_TargetBuffs) +E(OP_Track) +E(OP_Trader) +E(OP_TraderBuy) +E(OP_TributeItem) +E(OP_VetRewardsAvaliable) +E(OP_WearChange) +E(OP_WhoAllResponse) +E(OP_ZoneEntry) +E(OP_ZonePlayerToBind) +E(OP_ZoneServerInfo) +E(OP_ZoneSpawns) +// incoming packets that require a DECODE translation: +D(OP_AdventureMerchantSell) +D(OP_AltCurrencySell) +D(OP_AltCurrencySellSelection) +D(OP_ApplyPoison) +D(OP_AugmentInfo) +D(OP_AugmentItem) +D(OP_BazaarSearch) D(OP_Buff) -D(OP_ShopPlayerSell) -D(OP_Consume) +D(OP_BuffRemoveRequest) D(OP_CastSpell) -D(OP_Save) -D(OP_ItemVerifyRequest) -D(OP_GroupInvite) -D(OP_GroupInvite2) +D(OP_ChannelMessage) +D(OP_CharacterCreate) +D(OP_ClientUpdate) +D(OP_Consider) +D(OP_ConsiderCorpse) +D(OP_Consume) +D(OP_Damage) +D(OP_DeleteItem) +D(OP_EnvDamage) +D(OP_FaceChange) +D(OP_FindPersonRequest) +D(OP_GroupCancelInvite) +D(OP_GroupDisband) D(OP_GroupFollow) D(OP_GroupFollow2) -D(OP_GroupDisband) -D(OP_GroupCancelInvite) -D(OP_FindPersonRequest) -D(OP_TraderBuy) -D(OP_LootItem) -D(OP_TributeItem) -D(OP_ReadBook) -D(OP_AugmentInfo) -D(OP_FaceChange) -D(OP_AdventureMerchantSell) -D(OP_TradeSkillCombine) -D(OP_RaidInvite) +D(OP_GroupInvite) +D(OP_GroupInvite2) D(OP_InspectRequest) -D(OP_WearChange) -D(OP_ShopPlayerBuy) -D(OP_BazaarSearch) +D(OP_ItemLinkClick) +D(OP_ItemVerifyRequest) D(OP_LoadSpellSet) -D(OP_ApplyPoison) -D(OP_Damage) -D(OP_EnvDamage) -D(OP_ChannelMessage) -D(OP_DeleteItem) -D(OP_AugmentItem) +D(OP_LootItem) +D(OP_MoveItem) D(OP_PetCommands) -D(OP_BuffRemoveRequest) -D(OP_AltCurrencySellSelection) -D(OP_AltCurrencySell) +D(OP_RaidInvite) +D(OP_ReadBook) +D(OP_Save) +D(OP_SetServerFilter) +D(OP_ShopPlayerBuy) +D(OP_ShopPlayerSell) +D(OP_TraderBuy) +D(OP_TradeSkillCombine) +D(OP_TributeItem) +D(OP_WearChange) +D(OP_WhoAllRequest) #undef E #undef D diff --git a/common/patches/underfoot_structs.h b/common/patches/underfoot_structs.h index ee7286dab..eec3270f5 100644 --- a/common/patches/underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -519,7 +519,7 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint32 cs_unknown[5]; + uint32 cs_unknown[5]; }; /* @@ -564,7 +564,7 @@ struct SpellBuffFade_Struct_Underfoot { /*008*/ float unknown008; /*012*/ uint32 spellid; /*016*/ uint32 duration; -/*020*/ uint32 unknown016; +/*020*/ uint32 num_hits; /*024*/ uint32 playerId; // Global player ID? /*028*/ uint32 unknown020; /*032*/ uint8 unknown0028[48]; @@ -589,6 +589,25 @@ struct SpellBuffFade_Struct { /*036*/ }; +#if 0 +struct BuffIconEntry_Struct { +/*000*/ uint32 buff_slot; +/*004*/ uint32 spell_id; +/*008*/ uint32 tics_remaining; +/*012*/ uint32 num_hits; +// char name[0]; caster name is also here sometimes +// uint8 unknownend; 1 when single, 0 when all opposite of all_buffs? +}; + +struct BuffIcon_Struct { +/*000*/ uint32 entity_id; +/*004*/ uint32 unknown004; +/*008*/ uint8 all_buffs; // 1 when updating all buffs, 0 when doing one +/*009*/ uint16 count; +/*011*/ BuffIconEntry_Struct entires[0]; +}; +#endif + struct BuffRemoveRequest_Struct { /*00*/ uint32 SlotID; @@ -735,14 +754,62 @@ struct PotionBelt_Struct { static const uint32 MAX_GROUP_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_RAID_LEADERSHIP_AA_ARRAY = 16; static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY); -struct LeadershipAA_Struct { - uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; -}; struct GroupLeadershipAA_Struct { - uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 groupAAMarkNPC; + uint32 groupAANPCHealth; + uint32 groupAADelegateMainAssist; + uint32 groupAADelegateMarkNPC; + uint32 groupAA4; + uint32 groupAA5; + uint32 groupAAInspectBuffs; + uint32 groupAA7; + uint32 groupAASpellAwareness; + uint32 groupAAOffenseEnhancement; + uint32 groupAAManaEnhancement; + uint32 groupAAHealthEnhancement; + uint32 groupAAHealthRegeneration; + uint32 groupAAFindPathToPC; + uint32 groupAAHealthOfTargetsTarget; + uint32 groupAA15; + }; + uint32 ranks[MAX_GROUP_LEADERSHIP_AA_ARRAY]; + }; }; + struct RaidLeadershipAA_Struct { - uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + union { + struct { + uint32 raidAAMarkNPC; + uint32 raidAANPCHealth; + uint32 raidAADelegateMainAssist; + uint32 raidAADelegateMarkNPC; + uint32 raidAA4; + uint32 raidAA5; + uint32 raidAA6; + uint32 raidAASpellAwareness; + uint32 raidAAOffenseEnhancement; + uint32 raidAAManaEnhancement; + uint32 raidAAHealthEnhancement; + uint32 raidAAHealthRegeneration; + uint32 raidAAFindPathToPC; + uint32 raidAAHealthOfTargetsTarget; + uint32 raidAA14; + uint32 raidAA15; + }; + uint32 ranks[MAX_RAID_LEADERSHIP_AA_ARRAY]; + }; +}; + +struct LeadershipAA_Struct { + union { + struct { + GroupLeadershipAA_Struct group; + RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; }; /** @@ -766,7 +833,7 @@ struct BindStruct { ** OpCode: 0x006a */ static const uint32 MAX_PP_LANGUAGE = 25; // -static const uint32 MAX_PP_SPELLBOOK = 480; // Confirmed 60 pages on Underfoot now +static const uint32 MAX_PP_SPELLBOOK = 720; // Confirmed 60 pages on Underfoot now static const uint32 MAX_PP_MEMSPELL = 10; //was 9 now 10 on Underfoot static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 @@ -879,7 +946,6 @@ struct PlayerProfile_Struct /*04216*/ uint8 face; // Player face - Actually uint32? /*04217*/ uint8 unknown04217[147]; // was [175] /*04364*/ uint32 spell_book[MAX_PP_SPELLBOOK]; // List of the Spells in spellbook 720 = 90 pages [2880] was [1920] -/*06284*/ uint8 unknown06284[960]; // Spacer for the end of the book for now (pages 60 to 90) /*07244*/ uint32 mem_spells[MAX_PP_MEMSPELL]; // List of spells memorized /*07284*/ uint8 unknown07284[28]; //#### uint8 unknown04396[32]; in Titanium ####[28] /*07312*/ uint32 platinum; // Platinum Pieces on player @@ -1506,11 +1572,11 @@ struct BulkItemPacket_Struct struct Consume_Struct { -/*0000*/ uint32 slot; -/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click -/*0008*/ uint8 c_unknown1[4]; -/*0012*/ uint8 type; // 0x01=Food 0x02=Water -/*0013*/ uint8 unknown13[3]; +/*0000*/ uint32 slot; +/*0004*/ uint32 auto_consumed; // 0xffffffff when auto eating e7030000 when right click +/*0008*/ uint8 c_unknown1[4]; +/*0012*/ uint8 type; // 0x01=Food 0x02=Water +/*0013*/ uint8 unknown13[3]; /*0016*/ }; @@ -1540,17 +1606,17 @@ struct ItemProperties_Struct { }; struct DeleteItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; struct MoveItem_Struct { -/*0000*/ uint32 from_slot; -/*0004*/ uint32 to_slot; -/*0008*/ uint32 number_in_stack; +/*0000*/ uint32 from_slot; +/*0004*/ uint32 to_slot; +/*0008*/ uint32 number_in_stack; /*0012*/ }; @@ -2100,6 +2166,14 @@ struct GroupFollow_Struct { // Underfoot Follow Struct /*0152*/ }; +struct InspectBuffs_Struct { +/*000*/ uint32 spell_id[BUFF_COUNT]; +/*100*/ uint32 filler100[5]; // BUFF_COUNT is really 30... +/*120*/ uint32 tics_remaining[BUFF_COUNT]; +/*220*/ uint32 filler220[5]; // BUFF_COUNT is really 30... +}; + + struct LFG_Struct { /*000*/ uint32 unknown000; /*004*/ uint32 value; // 0x00 = off 0x01 = on @@ -3130,10 +3204,10 @@ struct TributeInfo_Struct { }; struct TributeItem_Struct { - uint32 slot; - uint32 quantity; - uint32 tribute_master_id; - int32 tribute_points; + uint32 slot; + uint32 quantity; + uint32 tribute_master_id; + int32 tribute_points; }; struct TributePoint_Struct { @@ -3169,7 +3243,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3605,6 +3679,21 @@ struct RaidAddMember_Struct { /*139*/ uint8 flags[5]; //no idea if these are needed... }; +struct RaidMOTD_Struct { +/*000*/ RaidGeneral_Struct general; // leader_name and action only used +/*140*/ char motd[0]; // max size 1024, but reply is variable +}; + +struct RaidLeadershipUpdate_Struct { +/*000*/ uint32 action; +/*004*/ char player_name[64]; +/*068*/ uint32 Unknown068; +/*072*/ char leader_name[64]; +/*136*/ GroupLeadershipAA_Struct group; //unneeded +/*200*/ RaidLeadershipAA_Struct raid; +/*264*/ char Unknown264[128]; +}; + struct RaidAdd_Struct { /*000*/ uint32 action; //=0 /*004*/ char player_name[64]; //should both be the player's name @@ -3736,7 +3825,7 @@ struct GMToggle_Struct { uint32 toggle; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; //was 1 /*0???*/ uint8 paddingXXX[3]; // always 0's @@ -4161,9 +4250,9 @@ struct ItemQuaternaryBodyStruct struct AugmentInfo_Struct { -/*000*/ uint32 itemid; // id of the solvent needed -/*004*/ uint8 window; // window to display the information in -/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint8 window; // window to display the information in +/*005*/ uint8 unknown005[71]; // total packet length 76, all the rest were always 00 /*076*/ }; diff --git a/common/ruletypes.h b/common/ruletypes.h index c61590ac4..528770ecb 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -171,6 +171,7 @@ RULE_INT ( World, PVPSettings, 0) // Sets the PVP settings for the server, 1 = R RULE_BOOL (World, IsGMPetitionWindowEnabled, false) RULE_INT (World, FVNoDropFlag, 0) // Sets the Firiona Vie settings on the client. If set to 2, the flag will be set for GMs only, allowing trading of no-drop items. RULE_BOOL (World, IPLimitDisconnectAll, false) +RULE_INT (World, TellQueueSize, 20) RULE_CATEGORY_END() RULE_CATEGORY( Zone ) @@ -200,6 +201,8 @@ RULE_INT ( Zone, EbonCrystalItemID, 40902) RULE_INT ( Zone, RadiantCrystalItemID, 40903) RULE_BOOL ( Zone, LevelBasedEXPMods, false) // Allows you to use the level_exp_mods table in consideration to your players EXP hits RULE_INT ( Zone, WeatherTimer, 600) // Weather timer when no duration is available +RULE_BOOL ( Zone, EnableLoggedOffReplenishments, true) +RULE_INT ( Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours RULE_CATEGORY_END() RULE_CATEGORY( Map ) @@ -320,6 +323,8 @@ RULE_INT ( Spells, AI_PursueDetrimentalChance, 90) // Chance while chasing targe RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) check when no spell is cast while idle. (min time in random) RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random) RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others. +RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false) +RULE_BOOL ( Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014 RULE_CATEGORY_END() diff --git a/common/servertalk.h b/common/servertalk.h index 5337b6b1d..f458c758e 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -83,6 +83,7 @@ #define ServerOP_QGlobalUpdate 0x0063 #define ServerOP_QGlobalDelete 0x0064 #define ServerOP_DepopPlayerCorpse 0x0065 +#define ServerOP_RequestTellQueue 0x0066 // client asks for it's tell queues #define ServerOP_RaidAdd 0x0100 //in use #define ServerOP_RaidRemove 0x0101 //in use @@ -103,6 +104,7 @@ #define ServerOP_GroupFollow 0x0110 #define ServerOP_GroupFollowAck 0x0111 #define ServerOP_GroupCancelInvite 0x0112 +#define ServerOP_RaidMOTD 0x0113 #define ServerOP_InstanceUpdateTime 0x014F #define ServerOP_AdventureRequest 0x0150 @@ -179,13 +181,15 @@ #define ServerOP_CZMessagePlayer 0x4008 #define ServerOP_ReloadWorld 0x4009 -#define ServerOP_QSPlayerLogTrades 0x4010 -#define ServerOP_QSPlayerLogHandins 0x4011 -#define ServerOP_QSPlayerLogNPCKills 0x4012 -#define ServerOP_QSPlayerLogDeletes 0x4013 -#define ServerOP_QSPlayerLogMoves 0x4014 +#define ServerOP_QSPlayerLogTrades 0x4010 +#define ServerOP_QSPlayerLogHandins 0x4011 +#define ServerOP_QSPlayerLogNPCKills 0x4012 +#define ServerOP_QSPlayerLogDeletes 0x4013 +#define ServerOP_QSPlayerLogMoves 0x4014 #define ServerOP_QSPlayerLogMerchantTransactions 0x4015 -#define ServerOP_QSSendQuery 0x4016 +#define ServerOP_QSSendQuery 0x4016 +#define ServerOP_CZSignalNPC 0x4017 +#define ServerOP_CZSetEntityVariableByNPCTypeID 0x4018 /* Query Serv Generic Packet Flag/Type Enumeration */ enum { QSG_LFGuild = 0 }; @@ -346,6 +350,7 @@ struct ServerChannelMessage_Struct { uint16 chan_num; uint32 guilddbid; uint16 language; + uint8 queued; // 0 = not queued, 1 = queued, 2 = queue full, 3 = offline char message[0]; }; @@ -850,6 +855,11 @@ struct ServerRaidMessage_Struct { char message[0]; }; +struct ServerRaidMOTD_Struct { + uint32 rid; + char motd[0]; +}; + struct ServerLFGMatchesRequest_Struct { uint32 FromID; uint8 QuerierLevel; @@ -1092,6 +1102,11 @@ struct CZClientSignal_Struct { uint32 data; }; +struct CZNPCSignal_Struct { + uint32 npctype_id; + uint32 data; +}; + struct CZClientSignalByName_Struct { char Name[64]; uint32 data; @@ -1233,10 +1248,20 @@ struct CZMessagePlayer_Struct { char Message[512]; }; +struct CZSetEntVarByNPCTypeID_Struct { + uint32 npctype_id; + char id[256]; + char m_var[256]; +}; + struct ReloadWorld_Struct{ uint32 Option; }; +struct ServerRequestTellQueue_Struct { + char name[64]; +}; + #pragma pack() #endif diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 10e3d2012..abb1124d5 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include "shareddb.h" #include "mysql.h" @@ -47,705 +48,659 @@ SharedDatabase::~SharedDatabase() { bool SharedDatabase::SetHideMe(uint32 account_id, uint8 hideme) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET hideme = %i where id = %i", hideme, account_id), errbuf)) { - std::cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("UPDATE account SET hideme = %i WHERE id = %i", hideme, account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetGMSpeed query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); return true; - } uint8 SharedDatabase::GetGMSpeed(uint32 account_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT gmspeed FROM account where id='%i'", account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint8 gmspeed = atoi(row[0]); - mysql_free_result(result); - return gmspeed; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - - std::cerr << "Error in GetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; + std::string query = StringFormat("SELECT gmspeed FROM account WHERE id = '%i'", account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetGMSpeed 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 SharedDatabase::SetGMSpeed(uint32 account_id, uint8 gmspeed) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET gmspeed = %i where id = %i", gmspeed, account_id), errbuf)) { - std::cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("UPDATE account SET gmspeed = %i WHERE id = %i", gmspeed, account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetGMSpeed query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); return true; - } uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { - uint32 EntitledTime = 0; - - const char *EntitledQuery = "select sum(ascii(substring(profile, 237, 1)) + (ascii(substring(profile, 238, 1)) * 256) +" - "(ascii(substring(profile, 239, 1)) * 65536) + (ascii(substring(profile, 240, 1)) * 16777216))" - "from character_ where account_id = %i"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, EntitledQuery, AccountID), errbuf, &result)) { - - if (mysql_num_rows(result) == 1) { - - row = mysql_fetch_row(result); - - EntitledTime = atoi(row[0]); - } - - mysql_free_result(result); + std::string query = StringFormat("SELECT `time_played` FROM `character_data` WHERE `account_id` = %u", AccountID); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + EntitledTime += atoi(row[0]); } - - safe_delete_array(query); - return EntitledTime; } bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) { - iter_queue it; - int i; - bool ret = true; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; // Delete cursor items - if ((ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid = %i AND ((slotid >= 8000 AND slotid <= 8999) OR slotid = %i OR (slotid >= %i AND slotid <= %i))", - char_id, MainCursor, EmuConstants::CURSOR_BAG_BEGIN, EmuConstants::CURSOR_BAG_END), errbuf))) { + std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i " + "AND ((slotid >= 8000 AND slotid <= 8999) " + "OR slotid = %i OR (slotid >= %i AND slotid <= %i) )", + char_id, MainCursor, + EmuConstants::CURSOR_BAG_BEGIN, EmuConstants::CURSOR_BAG_END); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cout << "Clearing cursor failed: " << results.ErrorMessage() << std::endl; + return false; + } - for (it = start, i = 8000; it != end; ++it, i++) { - ItemInst *inst = *it; - if (!(ret = SaveInventory(char_id, inst, (i == 8000) ? MainCursor : i))) - break; - } - } - else { - std::cout << "Clearing cursor failed: " << errbuf << std::endl; - } - safe_delete_array(query); + int i = 8000; + for(auto it = start; it != end; ++it, i++) { + ItemInst *inst = *it; + if (!SaveInventory(char_id,inst,(i == 8000) ? MainCursor : i)) + return false; + } - return ret; + return true; } bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; // Delete cursor items - if (!RunQuery(query, MakeAnyLenString(&query, - "SELECT itemid,charges FROM sharedbank " - "WHERE acctid=%d AND slotid=%d", - account_id, slot_id), errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error runing inventory verification query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT itemid, charges FROM sharedbank " + "WHERE acctid = %d AND slotid = %d", + account_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error runing inventory verification query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); //returning true is less harmful in the face of a query error - return(true); + return true; } - safe_delete_array(query); - row = mysql_fetch_row(result); - bool found = false; - if(row) { - uint32 id = atoi(row[0]); - uint16 charges = atoi(row[1]); + if (results.RowCount() == 0) + return false; - uint16 expect_charges = 0; - if(inst->GetCharges() >= 0) - expect_charges = inst->GetCharges(); - else - expect_charges = 0x7FFF; + auto row = results.begin(); - if(id == inst->GetItem()->ID && charges == expect_charges) - found = true; - } - mysql_free_result(result); - return(found); + uint32 id = atoi(row[0]); + uint16 charges = atoi(row[1]); + + uint16 expect_charges = 0; + + if(inst->GetCharges() >= 0) + expect_charges = inst->GetCharges(); + else + expect_charges = 0x7FFF; + + if(id != inst->GetItem()->ID || charges != expect_charges) + return false; + + return true; } bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - bool ret = false; - uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; + + // If we never save tribute slots..how are we to ever benefit from them!!? The client + // object is destroyed upon zoning - including its inventory object..and if tributes + // don't exist in the database, then they will never be loaded when the new client + // object is created in the new zone object... Something to consider... -U + // + // (we could add them to the 'NoRent' checks and dispose of after 30 minutes offline) //never save tribute slots: if(slot_id >= EmuConstants::TRIBUTE_BEGIN && slot_id <= EmuConstants::TRIBUTE_END) - return(true); + return true; - if (inst && inst->IsType(ItemClassCommon)) { + if (slot_id >= EmuConstants::SHARED_BANK_BEGIN && slot_id <= EmuConstants::SHARED_BANK_BAGS_END) { + // Shared bank inventory + if (!inst) + return DeleteSharedBankSlot(char_id, slot_id); + else + return UpdateSharedBankSlot(char_id, inst, slot_id); + } + else if (!inst) // All other inventory + return DeleteInventorySlot(char_id, slot_id); + + return UpdateInventorySlot(char_id, inst, slot_id); +} + +bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const ItemInst* inst, int16 slot_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->GetItem(i); augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : NO_ITEM; } - } - if (slot_id >= EmuConstants::SHARED_BANK_BEGIN && slot_id <= EmuConstants::SHARED_BANK_BAGS_END) { // Shared bank inventory - if (!inst) { - // Delete item - uint32 account_id = GetAccountIDByChar(char_id); - uint32 len_query = MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", - account_id, slot_id); + uint16 charges = 0; + if(inst->GetCharges() >= 0) + charges = inst->GetCharges(); + else + charges = 0x7FFF; - ret = RunQuery(query, len_query, errbuf); + // Update/Insert item + std::string query = StringFormat("REPLACE INTO inventory " + "(charid, slotid, itemid, charges, instnodrop, custom_data, color, " + "augslot1, augslot2, augslot3, augslot4, augslot5) " + "VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, " + "%lu, %lu, %lu, %lu, %lu)", + (unsigned long)char_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, + (unsigned long)charges, (unsigned long)(inst->IsInstNoDrop()? 1: 0), + inst->GetCustomDataString().c_str(), (unsigned long)inst->GetColor(), + (unsigned long)augslot[0], (unsigned long)augslot[1], (unsigned long)augslot[2], + (unsigned long)augslot[3],(unsigned long)augslot[4]); + auto results = QueryDatabase(query); - // Delete bag slots, if need be - if (ret && Inventory::SupportsContainers(slot_id)) { - safe_delete_array(query); - int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid>=%i AND slotid<%i", - account_id, base_slot_id, (base_slot_id+10)), errbuf); - } - - // @merth: need to delete augments here - } - else { - // Update/Insert item - uint32 account_id = GetAccountIDByChar(char_id); - uint16 charges = 0; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - - uint32 len_query = MakeAnyLenString(&query, - "REPLACE INTO sharedbank " - " (acctid,slotid,itemid,charges,custom_data," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,'%s'," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)account_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, - inst->GetCustomDataString().c_str(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]); - - - ret = RunQuery(query, len_query, errbuf); - } - } - else { // All other inventory - if (!inst) { - // Delete item - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid=%i", - char_id, slot_id), errbuf); - - // Delete bag slots, if need be - if (ret && Inventory::SupportsContainers(slot_id)) { - safe_delete_array(query); - int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid>=%i AND slotid<%i", - char_id, base_slot_id, (base_slot_id+10)), errbuf); - } - - // @merth: need to delete augments here - } - else { - uint16 charges = 0; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - // Update/Insert item - uint32 len_query = MakeAnyLenString(&query, - "REPLACE INTO inventory " - " (charid,slotid,itemid,charges,instnodrop,custom_data,color," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,%lu,'%s',%lu," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)char_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, - (unsigned long)(inst->IsInstNoDrop() ? 1:0),inst->GetCustomDataString().c_str(),(unsigned long)inst->GetColor(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4] ); - - ret = RunQuery(query, len_query, errbuf); - } - } - - if (!ret) - LogFile->write(EQEMuLog::Error, "SaveInventory query '%s': %s", query, errbuf); - safe_delete_array(query); - - // Save bag contents, if slot supports bag contents - if (inst && inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) { + // Save bag contents, if slot supports bag contents + if (inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) for (uint8 idx = SUB_BEGIN; idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { const ItemInst* baginst = inst->GetItem(idx); SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); } - } - // @merth: need to save augments here + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "UpdateInventorySlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } - return ret; -} - -int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT sharedplat FROM account WHERE id='%i'", account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint32 shared_platinum = atoi(row[0]); - mysql_free_result(result); - return shared_platinum; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in GetSharedPlatinum query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return 0; -} - -bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id), errbuf)) { - std::cerr << "Error in SetSharedPlatinum query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - safe_delete_array(query); return true; } -bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) +bool SharedDatabase::UpdateSharedBankSlot(uint32 char_id, const ItemInst* inst, int16 slot_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->GetItem(i); + augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : NO_ITEM; + } + +// Update/Insert item + uint32 account_id = GetAccountIDByChar(char_id); + uint16 charges = 0; + if(inst->GetCharges() >= 0) + charges = inst->GetCharges(); + else + charges = 0x7FFF; + + std::string query = StringFormat("REPLACE INTO sharedbank " + "(acctid, slotid, itemid, charges, custom_data, " + "augslot1, augslot2, augslot3, augslot4, augslot5) " + "VALUES( %lu, %lu, %lu, %lu, '%s', " + "%lu, %lu, %lu, %lu, %lu)", + (unsigned long)account_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, + (unsigned long)charges, inst->GetCustomDataString().c_str(), (unsigned long)augslot[0], + (unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]); + auto results = QueryDatabase(query); + + // Save bag contents, if slot supports bag contents + if (inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) + for (uint8 idx = SUB_BEGIN; idx < EmuConstants::ITEM_CONTAINER_SIZE; idx++) { + const ItemInst* baginst = inst->GetItem(idx); + SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); + } + + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "UpdateSharedBankSlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + return true; +} + +bool SharedDatabase::DeleteInventorySlot(uint32 char_id, int16 slot_id) { + + // Delete item + std::string query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid = %i", char_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteInventorySlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // Delete bag slots, if need be + if (!Inventory::SupportsContainers(slot_id)) + return true; + + int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); + query = StringFormat("DELETE FROM inventory WHERE charid = %i AND slotid >= %i AND slotid < %i", + char_id, base_slot_id, (base_slot_id+10)); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteInventorySlot, bags query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // @merth: need to delete augments here + return true; +} + +bool SharedDatabase::DeleteSharedBankSlot(uint32 char_id, int16 slot_id) { + + // Delete item + uint32 account_id = GetAccountIDByChar(char_id); + std::string query = StringFormat("DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", account_id, slot_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteSharedBankSlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // Delete bag slots, if need be + if (!Inventory::SupportsContainers(slot_id)) + return true; + + int16 base_slot_id = Inventory::CalcSlotId(slot_id, SUB_BEGIN); + query = StringFormat("DELETE FROM sharedbank WHERE acctid = %i " + "AND slotid >= %i AND slotid < %i", + account_id, base_slot_id, (base_slot_id+10)); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "DeleteSharedBankSlot, bags query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + // @merth: need to delete augments here + return true; +} + + +int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT sharedplat FROM account WHERE id = '%i'", account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetSharedPlatinum query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; + return false; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); +} + +bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) { + std::string query = StringFormat("UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetSharedPlatinum query '" << query << "' " << results.ErrorMessage() << std::endl; + return false; + } + + return true; +} + +bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) { + const Item_Struct* myitem; - RunQuery - ( - query, - MakeAnyLenString - ( - &query, - "SELECT itemid, item_charges, slot FROM starting_items " - "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " - "(deityid = %i or deityid=0) AND (zoneid = %i or zoneid = 0) AND " - "gm <= %i ORDER BY id", - si_race, si_class, si_deity, si_current_zone, admin_level - ), - errbuf, - &result - ); - safe_delete_array(query); + std::string query = StringFormat("SELECT itemid, item_charges, slot FROM starting_items " + "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " + "(deityid = %i or deityid = 0) AND (zoneid = %i or zoneid = 0) AND " + "gm <= %i ORDER BY id", + si_race, si_class, si_deity, si_current_zone, admin_level); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; - while((row = mysql_fetch_row(result))) { - int itemid = atoi(row[0]); - int charges = atoi(row[1]); - int slot = atoi(row[2]); + + for (auto row = results.begin(); row != results.end(); ++row) { + int32 itemid = atoi(row[0]); + int32 charges = atoi(row[1]); + int32 slot = atoi(row[2]); myitem = GetItem(itemid); + if(!myitem) continue; + ItemInst* myinst = CreateBaseItem(myitem, charges); + if(slot < 0) - slot = inv->FindFreeSlot(0,0); + slot = inv->FindFreeSlot(0, 0); + inv->PutItem(slot, *myinst); safe_delete(myinst); } - if(result) mysql_free_result(result); - return true; } // Retrieve shared bank inventory based on either account or character bool SharedDatabase::GetSharedBank(uint32 id, Inventory* inv, bool is_charid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 len_query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool ret = false; + std::string query; - if (is_charid) { - len_query = MakeAnyLenString(&query, - "SELECT sb.slotid,sb.itemid,sb.charges,sb.augslot1,sb.augslot2,sb.augslot3,sb.augslot4,sb.augslot5,sb.custom_data from sharedbank sb " - "INNER JOIN character_ ch ON ch.account_id=sb.acctid " - "WHERE ch.id=%i", id); - } - else { - len_query = MakeAnyLenString(&query, - "SELECT slotid,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5,custom_data from sharedbank WHERE acctid=%i", id); - } + if (is_charid) + query = StringFormat("SELECT sb.slotid, sb.itemid, sb.charges, " + "sb.augslot1, sb.augslot2, sb.augslot3, " + "sb.augslot4, sb.augslot5, sb.custom_data " + "FROM sharedbank sb INNER JOIN character_data ch " + "ON ch.account_id=sb.acctid WHERE ch.id = %i", id); + else + query = StringFormat("SELECT slotid, itemid, charges, " + "augslot1, augslot2, augslot3, " + "augslot4, augslot5, custom_data " + "FROM sharedbank WHERE acctid=%i", id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", results.ErrorMessage().c_str()); + return false; + } - if (RunQuery(query, len_query, errbuf, &result)) { - while ((row = mysql_fetch_row(result))) { - int16 slot_id = (int16)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]); - const Item_Struct* item = GetItem(item_id); + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = (int16)atoi(row[0]); + uint32 item_id = (uint32)atoi(row[1]); + int8 charges = (int8)atoi(row[2]); - if (item) { - int16 put_slot_id = INVALID_INDEX; + 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 = CreateBaseItem(item, charges); - if (item->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } - if(row[8]) { - std::string data_str(row[8]); - std::string id; - std::string value; - bool use_id = true; + const Item_Struct* item = GetItem(item_id); - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } - - put_slot_id = inv->PutItem(slot_id, *inst); - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in shared bank inventory: %s=%i, item_id=%i, slot_id=%i", - ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); - - if (is_charid) - SaveInventory(id, nullptr, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, + if (!item) { + LogFile->write(EQEMuLog::Error, "Warning: %s %i has an invalid item_id %i in inventory slot %i", ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); - } - } + continue; + } - mysql_free_result(result); - ret = true; - } - else { - LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", errbuf); + int16 put_slot_id = INVALID_INDEX; + + ItemInst* inst = CreateBaseItem(item, charges); + if (item->ItemClass == ItemClassCommon) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + if (aug[i]) { + inst->PutAugment(this, i, aug[i]); + } + } + } + + if(!row[8]) + continue; + + std::string data_str(row[8]); + std::string idAsString; + std::string value; + bool use_id = true; + + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } + use_id = !use_id; + continue; + } + + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); + else + value.push_back(v); + } + + put_slot_id = inv->PutItem(slot_id, *inst); + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id != INVALID_INDEX) + continue; + + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in shared bank inventory: %s=%i, item_id=%i, slot_id=%i", + ((is_charid==true)? "charid": "acctid"), id, item_id, slot_id); + + if (is_charid) + SaveInventory(id, nullptr, slot_id); } - safe_delete_array(query); - return ret; + return true; } // Overloaded: Retrieve character inventory based on character id bool SharedDatabase::GetInventory(uint32 char_id, Inventory* inv) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," - "instnodrop,custom_data FROM inventory WHERE charid=%i ORDER BY slotid", char_id), errbuf, &result)) { + std::string query = StringFormat("SELECT slotid, itemid, charges, color, augslot1, " + "augslot2, augslot3, augslot4, augslot5, instnodrop, custom_data " + "FROM inventory WHERE charid = %i ORDER BY slotid", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); + return false; + } - while ((row = mysql_fetch_row(result))) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - uint16 charges = atoi(row[2]); - uint32 color = atoul(row[3]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoul(row[4]); - aug[1] = (uint32)atoul(row[5]); - aug[2] = (uint32)atoul(row[6]); - aug[3] = (uint32)atoul(row[7]); - aug[4] = (uint32)atoul(row[8]); - bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + uint32 item_id = atoi(row[1]); + uint16 charges = atoi(row[2]); + uint32 color = atoul(row[3]); - const Item_Struct* item = GetItem(item_id); + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - if (item) { - int16 put_slot_id = INVALID_INDEX; + aug[0] = (uint32)atoul(row[4]); + aug[1] = (uint32)atoul(row[5]); + aug[2] = (uint32)atoul(row[6]); + aug[3] = (uint32)atoul(row[7]); + aug[4] = (uint32)atoul(row[8]); - ItemInst* inst = CreateBaseItem(item, charges); + bool instnodrop = (row[9] && (uint16)atoi(row[9]))? true: false; - if(row[10]) { - std::string data_str(row[10]); - std::string id; - std::string value; - bool use_id = true; + const Item_Struct* item = GetItem(item_id); - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } + if (!item) { + LogFile->write(EQEMuLog::Error,"Warning: charid %i has an invalid item_id %i in inventory slot %i", char_id, item_id, slot_id); + continue; + } - if (instnodrop || (((slot_id >= EmuConstants::EQUIPMENT_BEGIN && slot_id <= EmuConstants::EQUIPMENT_END) || slot_id == MainPowerSource) && inst->GetItem()->Attuneable)) - inst->SetInstNoDrop(true); - if (color > 0) - inst->SetColor(color); - if(charges==0x7FFF) - inst->SetCharges(-1); - else - inst->SetCharges(charges); + int16 put_slot_id = INVALID_INDEX; - if (item->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } + ItemInst* inst = CreateBaseItem(item, charges); - if (slot_id >= 8000 && slot_id <= 8999) { - put_slot_id = inv->PushCursor(*inst); - } - // Admins: please report any occurrences of this error - else if (slot_id >= 3111 && slot_id <= 3179) { - LogFile->write(EQEMuLog::Error, - "Warning: Defunct location for item in inventory: charid=%i, item_id=%i, slot_id=%i .. pushing to cursor...", - char_id, item_id, slot_id); - put_slot_id = inv->PushCursor(*inst); - } - else { - put_slot_id = inv->PutItem(slot_id, *inst); - } + if(row[10]) { + std::string data_str(row[10]); + std::string idAsString; + std::string value; + bool use_id = true; - safe_delete(inst); + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i", - char_id, item_id, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: charid %i has an invalid item_id %i in inventory slot %i", - char_id, item_id, slot_id); - } - } - mysql_free_result(result); + use_id = !use_id; + continue; + } - // Retrieve shared inventory - ret = GetSharedBank(char_id, inv, true); - } - else { - LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); - LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); - } + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); + else + value.push_back(v); + } + } - safe_delete_array(query); - return ret; + if (instnodrop || (((slot_id >= EmuConstants::EQUIPMENT_BEGIN && slot_id <= EmuConstants::EQUIPMENT_END) || slot_id == MainPowerSource) && inst->GetItem()->Attuneable)) + inst->SetInstNoDrop(true); + + if (color > 0) + inst->SetColor(color); + + if(charges==0x7FFF) + inst->SetCharges(-1); + else + inst->SetCharges(charges); + + if (item->ItemClass == ItemClassCommon) + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) + if (aug[i]) + inst->PutAugment(this, i, aug[i]); + + if (slot_id >= 8000 && slot_id <= 8999) + put_slot_id = inv->PushCursor(*inst); + else if (slot_id >= 3111 && slot_id <= 3179) { + // Admins: please report any occurrences of this error + LogFile->write(EQEMuLog::Error, "Warning: Defunct location for item in inventory: charid=%i, item_id=%i, slot_id=%i .. pushing to cursor...", char_id, item_id, slot_id); + put_slot_id = inv->PushCursor(*inst); + } else + put_slot_id = inv->PutItem(slot_id, *inst); + + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id == INVALID_INDEX) { + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i",char_id, item_id, slot_id); + } + } + + // Retrieve shared inventory + return GetSharedBank(char_id, inv, true); } // Overloaded: Retrieve character inventory based on account_id and character name bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," - "instnodrop,custom_data FROM inventory INNER JOIN character_ ch ON ch.id=charid WHERE ch.name='%s' AND ch.account_id=%i ORDER BY slotid", - name, account_id), errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - int8 charges = atoi(row[2]); - uint32 color = atoul(row[3]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoi(row[4]); - aug[1] = (uint32)atoi(row[5]); - aug[2] = (uint32)atoi(row[6]); - aug[3] = (uint32)atoi(row[7]); - aug[4] = (uint32)atoi(row[8]); - bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - const Item_Struct* item = GetItem(item_id); - int16 put_slot_id = INVALID_INDEX; - if(!item) - continue; - - ItemInst* inst = CreateBaseItem(item, charges); - inst->SetInstNoDrop(instnodrop); - - if(row[10]) { - std::string data_str(row[10]); - std::string id; - std::string value; - bool use_id = true; - - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } - - if (color > 0) - inst->SetColor(color); - inst->SetCharges(charges); - - if (item->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } - if (slot_id>=8000 && slot_id <= 8999) - put_slot_id = inv->PushCursor(*inst); - else - put_slot_id = inv->PutItem(slot_id, *inst); - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: name=%s, acctid=%i, item_id=%i, slot_id=%i", - name, account_id, item_id, slot_id); - } - } - mysql_free_result(result); - - // Retrieve shared inventory - ret = GetSharedBank(account_id, inv, false); - } - else { - LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); + std::string query = StringFormat("SELECT slotid, itemid, charges, color, augslot1, " + "augslot2, augslot3, augslot4, augslot5, instnodrop, custom_data " + "FROM inventory INNER JOIN character_data ch " + "ON ch.id = charid WHERE ch.name = '%s' AND ch.account_id = %i ORDER BY slotid", + name, account_id); + auto results = QueryDatabase(query); + if (!results.Success()){ + LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); + return false; } - safe_delete_array(query); - return ret; + + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + uint32 item_id = atoi(row[1]); + int8 charges = atoi(row[2]); + uint32 color = atoul(row[3]); + + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoi(row[4]); + aug[1] = (uint32)atoi(row[5]); + aug[2] = (uint32)atoi(row[6]); + aug[3] = (uint32)atoi(row[7]); + aug[4] = (uint32)atoi(row[8]); + + bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; + const Item_Struct* item = GetItem(item_id); + int16 put_slot_id = INVALID_INDEX; + if(!item) + continue; + + ItemInst* inst = CreateBaseItem(item, charges); + inst->SetInstNoDrop(instnodrop); + + if(row[10]) { + std::string data_str(row[10]); + std::string idAsString; + std::string value; + bool use_id = true; + + for(int i = 0; i < data_str.length(); ++i) { + if(data_str[i] == '^') { + if(!use_id) { + inst->SetCustomData(idAsString, value); + idAsString.clear(); + value.clear(); + } + + use_id = !use_id; + continue; + } + + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); + else + value.push_back(v); + + } + } + + if (color > 0) + inst->SetColor(color); + + inst->SetCharges(charges); + + if (item->ItemClass == ItemClassCommon) + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) + if (aug[i]) + inst->PutAugment(this, i, aug[i]); + + if (slot_id>=8000 && slot_id <= 8999) + put_slot_id = inv->PushCursor(*inst); + else + put_slot_id = inv->PutItem(slot_id, *inst); + + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id == INVALID_INDEX) + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: name=%s, acctid=%i, item_id=%i, slot_id=%i", name, account_id, item_id, slot_id); + + } + + // Retrieve shared inventory + return GetSharedBank(account_id, inv, false); } void SharedDatabase::GetItemsCount(int32 &item_count, uint32 &max_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; item_count = -1; max_id = 0; - char query[] = "SELECT MAX(id), count(*) FROM items"; - if (RunQuery(query, static_cast(strlen(query)), errbuf, &result)) { - row = mysql_fetch_row(result); - if (row != nullptr && row[1] != 0) { - item_count = atoi(row[1]); - if(row[0]) - max_id = atoi(row[0]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetItemsCount '%s': '%s'", query, errbuf); + const std::string query = "SELECT MAX(id), count(*) FROM items"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetItemsCount '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + return; } + + if (results.RowCount() == 0) + return; + + auto row = results.begin(); + + if(row[0]) + max_id = atoi(row[0]); + + if (row[1]) + item_count = atoi(row[1]); } bool SharedDatabase::LoadItems() { @@ -781,9 +736,6 @@ bool SharedDatabase::LoadItems() { void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id) { EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, items, max_item_id); - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; char ndbuffer[4]; bool disableNoRent = false; @@ -811,220 +763,229 @@ void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_ } } - char query[] = "select source," + Item_Struct item; + + const std::string query = "SELECT source," #define F(x) "`"#x"`," #include "item_fieldlist.h" #undef F - "updated" - " from items order by id"; - Item_Struct item; - if(RunQuery(query, sizeof(query), errbuf, &result)) { - while((row = mysql_fetch_row(result))) { - memset(&item, 0, sizeof(Item_Struct)); + "updated FROM items ORDER BY id"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "LoadItems '%s', %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - item.ItemClass = (uint8)atoi(row[ItemField::itemclass]); - strcpy(item.Name,row[ItemField::name]); - strcpy(item.Lore,row[ItemField::lore]); - strcpy(item.IDFile,row[ItemField::idfile]); - item.ID = (uint32)atoul(row[ItemField::id]); - item.Weight = (uint8)atoi(row[ItemField::weight]); - item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]); - item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]); - item.Size = (uint8)atoi(row[ItemField::size]); - item.Slots = (uint32)atoul(row[ItemField::slots]); - item.Price = (uint32)atoul(row[ItemField::price]); - item.Icon = (uint32)atoul(row[ItemField::icon]); - item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0); - item.Tradeskills = (atoi(row[ItemField::tradeskills])==0) ? false : true; - item.CR = (int8)atoi(row[ItemField::cr]); - item.DR = (int8)atoi(row[ItemField::dr]); - item.PR = (int8)atoi(row[ItemField::pr]); - item.MR = (int8)atoi(row[ItemField::mr]); - item.FR = (int8)atoi(row[ItemField::fr]); - item.AStr = (int8)atoi(row[ItemField::astr]); - item.ASta = (int8)atoi(row[ItemField::asta]); - item.AAgi = (int8)atoi(row[ItemField::aagi]); - item.ADex = (int8)atoi(row[ItemField::adex]); - item.ACha = (int8)atoi(row[ItemField::acha]); - item.AInt = (int8)atoi(row[ItemField::aint]); - item.AWis = (int8)atoi(row[ItemField::awis]); - item.HP = (int32)atoul(row[ItemField::hp]); - item.Mana = (int32)atoul(row[ItemField::mana]); - item.AC = (int32)atoul(row[ItemField::ac]); - item.Deity = (uint32)atoul(row[ItemField::deity]); - item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]); - //item.Unk033 = (int32)atoul(row[ItemField::UNK033]); - item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]); - item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]); - item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]); - item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]); - item.Magic = (atoi(row[ItemField::magic])==0) ? false : true; - item.CastTime_ = (int32)atoul(row[ItemField::casttime_]); - item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]); - item.BardType = (uint32)atoul(row[ItemField::bardtype]); - item.BardValue = (int32)atoul(row[ItemField::bardvalue]); - item.Light = (int8)atoi(row[ItemField::light]); - item.Delay = (uint8)atoi(row[ItemField::delay]); - item.RecLevel = (uint8)atoi(row[ItemField::reclevel]); - item.RecSkill = (uint8)atoi(row[ItemField::recskill]); - item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]); - item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]); - item.Range = (uint8)atoi(row[ItemField::range]); - item.Damage = (uint32)atoi(row[ItemField::damage]); - item.Color = (uint32)atoul(row[ItemField::color]); - item.Classes = (uint32)atoul(row[ItemField::classes]); - item.Races = (uint32)atoul(row[ItemField::races]); - //item.Unk054 = (uint32)atoul(row[ItemField::UNK054]); - item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]); - item.ItemType = (uint8)atoi(row[ItemField::itemtype]); - item.Material = (uint8)atoi(row[ItemField::material]); - item.SellRate = (float)atof(row[ItemField::sellrate]); - //item.Unk059 = (uint32)atoul(row[ItemField::UNK059]); - item.CastTime = (uint32)atoul(row[ItemField::casttime]); - item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]); - item.ProcRate = (int32)atoi(row[ItemField::procrate]); - item.CombatEffects = (int8)atoi(row[ItemField::combateffects]); - item.Shielding = (int8)atoi(row[ItemField::shielding]); - item.StunResist = (int8)atoi(row[ItemField::stunresist]); - item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]); - item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]); - item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]); - item.SpellShield = (int8)atoi(row[ItemField::spellshield]); - item.Avoidance = (int8)atoi(row[ItemField::avoidance]); - item.Accuracy = (int8)atoi(row[ItemField::accuracy]); - item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]); - item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]); - item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]); - item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]); - item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]); - item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]); - item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]); - item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]); - item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]); - strcpy(item.CharmFile,row[ItemField::charmfile]); - item.AugType = (uint32)atoul(row[ItemField::augtype]); - item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]); - item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]); - item.AugSlotUnk2[0] = 0; - item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]); - item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]); - item.AugSlotUnk2[1] = 0; - item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]); - item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]); - item.AugSlotUnk2[2] = 0; - item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]); - item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]); - item.AugSlotUnk2[3] = 0; - item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]); - item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]); - item.AugSlotUnk2[4] = 0; - item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]); - item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]); - item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]); - item.BagType = (uint8)atoi(row[ItemField::bagtype]); - item.BagSlots = (uint8)atoi(row[ItemField::bagslots]); - item.BagSize = (uint8)atoi(row[ItemField::bagsize]); - item.BagWR = (uint8)atoi(row[ItemField::bagwr]); - item.Book = (uint8)atoi(row[ItemField::book]); - item.BookType = (uint32)atoul(row[ItemField::booktype]); - strcpy(item.Filename,row[ItemField::filename]); - item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]); - item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]); - item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]); - item.LoreFlag = item.LoreGroup!=0; - item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag])==0) ? false : true; - item.ArtifactFlag = (atoi(row[ItemField::artifactflag])==0) ? false : true; - item.SummonedFlag = (atoi(row[ItemField::summonedflag])==0) ? false : true; - item.Favor = (uint32)atoul(row[ItemField::favor]); - item.FVNoDrop = (atoi(row[ItemField::fvnodrop])==0) ? false : true; - item.Endur = (uint32)atoul(row[ItemField::endur]); - item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]); - item.Attack = (uint32)atoul(row[ItemField::attack]); - item.Regen = (uint32)atoul(row[ItemField::regen]); - item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]); - item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]); - item.Haste = (uint32)atoul(row[ItemField::haste]); - item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); - item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); - item.RecastType = (uint32)atoul(row[ItemField::recasttype]); - item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); - item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); - item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true; - item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true; - item.PointType = (uint32)atoul(row[ItemField::pointtype]); - item.PotionBelt = (atoi(row[ItemField::potionbelt])==0) ? false : true; - item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots])==0) ? false : true; - item.StackSize = (uint16)atoi(row[ItemField::stacksize]); - item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer])==0) ? false : true; - item.Stackable = (atoi(row[ItemField::stackable])==0) ? false : true; - item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]); - item.Click.Type = (uint8)atoul(row[ItemField::clicktype]); - item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]); - item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]); - strcpy(item.CharmFile,row[ItemField::charmfile]); - item.Proc.Effect = (uint16)atoul(row[ItemField::proceffect]); - item.Proc.Type = (uint8)atoul(row[ItemField::proctype]); - item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]); - item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]); - item.Worn.Effect = (uint16)atoul(row[ItemField::worneffect]); - item.Worn.Type = (uint8)atoul(row[ItemField::worntype]); - item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]); - item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]); - item.Focus.Effect = (uint16)atoul(row[ItemField::focuseffect]); - item.Focus.Type = (uint8)atoul(row[ItemField::focustype]); - item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]); - item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]); - item.Scroll.Effect = (uint16)atoul(row[ItemField::scrolleffect]); - item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]); - item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]); - item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]); - item.Bard.Effect = (uint16)atoul(row[ItemField::bardeffect]); - item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]); - item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]); - item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]); - item.QuestItemFlag = (atoi(row[ItemField::questitemflag])==0) ? false : true; - item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]); - item.Purity = (uint32)atoul(row[ItemField::purity]); - item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]); - item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]); - item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]); - item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]); - item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]); - item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]); - item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]); - item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]); - item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]); - item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]); - item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]); - item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]); - item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]); - item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]); - item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]); - item.HealAmt = (int32)atoi(row[ItemField::healamt]); - item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]); - item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]); - item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]); - item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]); - item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]); - strcpy(item.ClickName,row[ItemField::clickname]); - strcpy(item.ProcName,row[ItemField::procname]); - strcpy(item.WornName,row[ItemField::wornname]); - strcpy(item.FocusName,row[ItemField::focusname]); - strcpy(item.ScrollName,row[ItemField::scrollname]); + for(auto row = results.begin(); row != results.end(); ++row) { + memset(&item, 0, sizeof(Item_Struct)); - try { - hash.insert(item.ID, item); - } catch(std::exception &ex) { - LogFile->write(EQEMuLog::Error, "Database::LoadItems: %s", ex.what()); - break; - } - } + item.ItemClass = (uint8)atoi(row[ItemField::itemclass]); + strcpy(item.Name,row[ItemField::name]); + strcpy(item.Lore,row[ItemField::lore]); + strcpy(item.IDFile,row[ItemField::idfile]); + + item.ID = (uint32)atoul(row[ItemField::id]); + item.Weight = (uint8)atoi(row[ItemField::weight]); + item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]); + item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]); + item.Size = (uint8)atoi(row[ItemField::size]); + item.Slots = (uint32)atoul(row[ItemField::slots]); + item.Price = (uint32)atoul(row[ItemField::price]); + item.Icon = (uint32)atoul(row[ItemField::icon]); + item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0); + item.Tradeskills = (atoi(row[ItemField::tradeskills])==0) ? false : true; + item.CR = (int8)atoi(row[ItemField::cr]); + item.DR = (int8)atoi(row[ItemField::dr]); + item.PR = (int8)atoi(row[ItemField::pr]); + item.MR = (int8)atoi(row[ItemField::mr]); + item.FR = (int8)atoi(row[ItemField::fr]); + item.AStr = (int8)atoi(row[ItemField::astr]); + item.ASta = (int8)atoi(row[ItemField::asta]); + item.AAgi = (int8)atoi(row[ItemField::aagi]); + item.ADex = (int8)atoi(row[ItemField::adex]); + item.ACha = (int8)atoi(row[ItemField::acha]); + item.AInt = (int8)atoi(row[ItemField::aint]); + item.AWis = (int8)atoi(row[ItemField::awis]); + item.HP = (int32)atoul(row[ItemField::hp]); + item.Mana = (int32)atoul(row[ItemField::mana]); + item.AC = (int32)atoul(row[ItemField::ac]); + item.Deity = (uint32)atoul(row[ItemField::deity]); + item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]); + + item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]); + item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]); + item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]); + item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]); + item.Magic = (atoi(row[ItemField::magic])==0) ? false : true; + item.CastTime_ = (int32)atoul(row[ItemField::casttime_]); + item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]); + item.BardType = (uint32)atoul(row[ItemField::bardtype]); + item.BardValue = (int32)atoul(row[ItemField::bardvalue]); + item.Light = (int8)atoi(row[ItemField::light]); + item.Delay = (uint8)atoi(row[ItemField::delay]); + item.RecLevel = (uint8)atoi(row[ItemField::reclevel]); + item.RecSkill = (uint8)atoi(row[ItemField::recskill]); + item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]); + item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]); + item.Range = (uint8)atoi(row[ItemField::range]); + item.Damage = (uint32)atoi(row[ItemField::damage]); + item.Color = (uint32)atoul(row[ItemField::color]); + item.Classes = (uint32)atoul(row[ItemField::classes]); + item.Races = (uint32)atoul(row[ItemField::races]); + + item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]); + item.ItemType = (uint8)atoi(row[ItemField::itemtype]); + item.Material = (uint8)atoi(row[ItemField::material]); + item.SellRate = (float)atof(row[ItemField::sellrate]); + + item.CastTime = (uint32)atoul(row[ItemField::casttime]); + item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]); + item.ProcRate = (int32)atoi(row[ItemField::procrate]); + item.CombatEffects = (int8)atoi(row[ItemField::combateffects]); + item.Shielding = (int8)atoi(row[ItemField::shielding]); + item.StunResist = (int8)atoi(row[ItemField::stunresist]); + item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]); + item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]); + item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]); + item.SpellShield = (int8)atoi(row[ItemField::spellshield]); + item.Avoidance = (int8)atoi(row[ItemField::avoidance]); + item.Accuracy = (int8)atoi(row[ItemField::accuracy]); + item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]); + item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]); + item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]); + item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]); + item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]); + item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]); + item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]); + item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]); + item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]); + + strcpy(item.CharmFile,row[ItemField::charmfile]); + + item.AugType = (uint32)atoul(row[ItemField::augtype]); + item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]); + item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]); + item.AugSlotUnk2[0] = 0; + item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]); + item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]); + item.AugSlotUnk2[1] = 0; + item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]); + item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]); + item.AugSlotUnk2[2] = 0; + item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]); + item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]); + item.AugSlotUnk2[3] = 0; + item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]); + item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]); + item.AugSlotUnk2[4] = 0; + + item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]); + item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]); + item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]); + item.BagType = (uint8)atoi(row[ItemField::bagtype]); + item.BagSlots = (uint8)atoi(row[ItemField::bagslots]); + item.BagSize = (uint8)atoi(row[ItemField::bagsize]); + item.BagWR = (uint8)atoi(row[ItemField::bagwr]); + item.Book = (uint8)atoi(row[ItemField::book]); + item.BookType = (uint32)atoul(row[ItemField::booktype]); + + strcpy(item.Filename,row[ItemField::filename]); + + item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]); + item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]); + item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]); + item.LoreFlag = item.LoreGroup!=0; + item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag])==0) ? false : true; + item.ArtifactFlag = (atoi(row[ItemField::artifactflag])==0) ? false : true; + item.SummonedFlag = (atoi(row[ItemField::summonedflag])==0) ? false : true; + item.Favor = (uint32)atoul(row[ItemField::favor]); + item.FVNoDrop = (atoi(row[ItemField::fvnodrop])==0) ? false : true; + item.Endur = (uint32)atoul(row[ItemField::endur]); + item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]); + item.Attack = (uint32)atoul(row[ItemField::attack]); + item.Regen = (uint32)atoul(row[ItemField::regen]); + item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]); + item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]); + item.Haste = (uint32)atoul(row[ItemField::haste]); + item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); + item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); + item.RecastType = (uint32)atoul(row[ItemField::recasttype]); + item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); + item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); + item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true; + item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true; + item.PointType = (uint32)atoul(row[ItemField::pointtype]); + item.PotionBelt = (atoi(row[ItemField::potionbelt])==0) ? false : true; + item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots])==0) ? false : true; + item.StackSize = (uint16)atoi(row[ItemField::stacksize]); + item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer])==0) ? false : true; + item.Stackable = (atoi(row[ItemField::stackable])==0) ? false : true; + item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]); + item.Click.Type = (uint8)atoul(row[ItemField::clicktype]); + item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]); + item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]); + + strcpy(item.CharmFile,row[ItemField::charmfile]); + + item.Proc.Effect = (uint16)atoul(row[ItemField::proceffect]); + item.Proc.Type = (uint8)atoul(row[ItemField::proctype]); + item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]); + item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]); + item.Worn.Effect = (uint16)atoul(row[ItemField::worneffect]); + item.Worn.Type = (uint8)atoul(row[ItemField::worntype]); + item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]); + item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]); + item.Focus.Effect = (uint16)atoul(row[ItemField::focuseffect]); + item.Focus.Type = (uint8)atoul(row[ItemField::focustype]); + item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]); + item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]); + item.Scroll.Effect = (uint16)atoul(row[ItemField::scrolleffect]); + item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]); + item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]); + item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]); + item.Bard.Effect = (uint16)atoul(row[ItemField::bardeffect]); + item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]); + item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]); + item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]); + item.QuestItemFlag = (atoi(row[ItemField::questitemflag])==0) ? false : true; + item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]); + item.Purity = (uint32)atoul(row[ItemField::purity]); + item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]); + item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]); + item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]); + item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]); + item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]); + item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]); + item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]); + item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]); + item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]); + item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]); + item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]); + item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]); + item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]); + item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]); + item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]); + item.HealAmt = (int32)atoi(row[ItemField::healamt]); + item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]); + item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]); + item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]); + item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]); + item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]); + + strcpy(item.ClickName,row[ItemField::clickname]); + strcpy(item.ProcName,row[ItemField::procname]); + strcpy(item.WornName,row[ItemField::wornname]); + strcpy(item.FocusName,row[ItemField::focusname]); + strcpy(item.ScrollName,row[ItemField::scrollname]); + + try { + hash.insert(item.ID, item); + } catch(std::exception &ex) { + LogFile->write(EQEMuLog::Error, "Database::LoadItems: %s", ex.what()); + break; + } + } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "LoadItems '%s', %s", query, errbuf); - } } const Item_Struct* SharedDatabase::GetItem(uint32 id) { @@ -1061,54 +1022,48 @@ const Item_Struct* SharedDatabase::IterateItems(uint32* id) { std::string SharedDatabase::GetBook(const char *txtfile) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; char txtfile2[20]; std::string txtout; - strcpy(txtfile2,txtfile); - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT txtfile FROM books where name='%s'", txtfile2), errbuf, &result)) { - std::cerr << "Error in GetBook query '" << query << "' " << errbuf << std::endl; - if (query != 0) - safe_delete_array(query); + strcpy(txtfile2, txtfile); + + std::string query = StringFormat("SELECT txtfile FROM books WHERE name = '%s'", txtfile2); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetBook query '" << query << "' " << results.ErrorMessage() << std::endl; txtout.assign(" ",1); return txtout; } - else { - safe_delete_array(query); - if (mysql_num_rows(result) == 0) { - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "No book to send, (%s)", txtfile); - txtout.assign(" ",1); - return txtout; - } - else { - row = mysql_fetch_row(result); - txtout.assign(row[0],strlen(row[0])); - mysql_free_result(result); - return txtout; - } - } + + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "No book to send, (%s)", txtfile); + txtout.assign(" ",1); + return txtout; + } + + auto row = results.begin(); + txtout.assign(row[0],strlen(row[0])); + + return txtout; } void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { list_count = 0; max_lists = 0; - const char *query = "SELECT COUNT(*), MAX(id) FROM npc_faction"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - list_count = static_cast(atoul(row[0])); - max_lists = static_cast(atoul(row[1])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); + const std::string query = "SELECT COUNT(*), MAX(id) FROM npc_faction"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; } + + if (results.RowCount() == 0) + return; + + auto row = results.begin(); + + list_count = static_cast(atoul(row[0])); + max_lists = static_cast(atoul(row[1])); } const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { @@ -1125,57 +1080,52 @@ const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { void SharedDatabase::LoadNPCFactionLists(void *data, uint32 size, uint32 list_count, uint32 max_lists) { EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, list_count, max_lists); - const char *query = "SELECT npc_faction.id, npc_faction.primaryfaction, npc_faction.ignore_primary_assist, " - "npc_faction_entries.faction_id, npc_faction_entries.value, npc_faction_entries.npc_value, npc_faction_entries.temp " - "FROM npc_faction LEFT JOIN npc_faction_entries ON npc_faction.id = npc_faction_entries.npc_faction_id ORDER BY " - "npc_faction.id;"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; NPCFactionList faction; - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, faction); - } + const std::string query = "SELECT npc_faction.id, npc_faction.primaryfaction, npc_faction.ignore_primary_assist, " + "npc_faction_entries.faction_id, npc_faction_entries.value, npc_faction_entries.npc_value, " + "npc_faction_entries.temp FROM npc_faction LEFT JOIN npc_faction_entries " + "ON npc_faction.id = npc_faction_entries.npc_faction_id ORDER BY npc_faction.id;"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - memset(&faction, 0, sizeof(faction)); - current_entry = 0; - current_id = id; - faction.id = id; - faction.primaryfaction = static_cast(atoul(row[1])); - faction.assistprimaryfaction = (atoi(row[2]) == 0); - } + uint32 current_id = 0; + uint32 current_entry = 0; - if(!row[3]) { + for(auto row = results.begin(); row != results.end(); ++row) { + uint32 id = static_cast(atoul(row[0])); + if(id != current_id) { + if(current_id != 0) { + hash.insert(current_id, faction); + } + + memset(&faction, 0, sizeof(faction)); + current_entry = 0; + current_id = id; + faction.id = id; + faction.primaryfaction = static_cast(atoul(row[1])); + faction.assistprimaryfaction = (atoi(row[2]) == 0); + } + + if(!row[3]) + continue; + + if(current_entry >= MAX_NPC_FACTIONS) continue; - } - if(current_entry >= MAX_NPC_FACTIONS) { - continue; - } + faction.factionid[current_entry] = static_cast(atoul(row[3])); + faction.factionvalue[current_entry] = static_cast(atoi(row[4])); + faction.factionnpcvalue[current_entry] = static_cast(atoi(row[5])); + faction.factiontemp[current_entry] = static_cast(atoi(row[6])); + ++current_entry; + } - faction.factionid[current_entry] = static_cast(atoul(row[3])); - faction.factionvalue[current_entry] = static_cast(atoi(row[4])); - faction.factionnpcvalue[current_entry] = static_cast(atoi(row[5])); - faction.factiontemp[current_entry] = static_cast(atoi(row[6])); - ++current_entry; - } + if(current_id != 0) + hash.insert(current_id, faction); - if(current_id != 0) { - hash.insert(current_id, faction); - } - - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); -} } bool SharedDatabase::LoadNPCFactionLists() { @@ -1211,104 +1161,6 @@ bool SharedDatabase::LoadNPCFactionLists() { return true; } -// Get the player profile and inventory for the given account "account_id" and -// character name "name". Return true if the character was found, otherwise false. -// False will also be returned if there is a database error. -bool SharedDatabase::GetPlayerProfile(uint32 account_id, char* name, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, char* current_zone, uint32 *current_instance) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile,zonename,x,y,z,extprofile,instanceid FROM character_ WHERE account_id=%i AND name='%s'", account_id, name), errbuf, &result)) { - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(PlayerProfile_Struct)) { - memcpy(pp, row[0], sizeof(PlayerProfile_Struct)); - - if (current_zone) - strcpy(current_zone, row[1]); - pp->zone_id = GetZoneID(row[1]); - pp->x = atof(row[2]); - pp->y = atof(row[3]); - pp->z = atof(row[4]); - pp->zoneInstance = atoi(row[6]); - if (pp->x == -1 && pp->y == -1 && pp->z == -1) - GetSafePoints(pp->zone_id, GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z); - - if(current_instance) - *current_instance = pp->zoneInstance; - - if(ext) { - //SetExtendedProfile handles any conversion - SetExtendedProfile(ext, row[5], lengths[5]); - } - - // Retrieve character inventory - ret = GetInventory(account_id, name, inv); - } - else { - LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetPlayerProfile. Found: %i, Expected: %i", - lengths[0], sizeof(PlayerProfile_Struct)); - } - } - - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "GetPlayerProfile query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - -bool SharedDatabase::SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 affected_rows = 0; - bool ret = false; - - if (RunQuery(query, SetPlayerProfile_MQ(&query, account_id, charid, pp, inv, ext, current_zone, current_instance, MaxXTargets), errbuf, 0, &affected_rows)) { - ret = (affected_rows != 0); - } - - if (!ret) { - LogFile->write(EQEMuLog::Error, "SetPlayerProfile query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - -// Generate SQL for updating player profile -uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - *query = new char[396 + sizeof(PlayerProfile_Struct)*2 + sizeof(ExtendedProfile_Struct)*2 + 4]; - char* end = *query; - if (!current_zone) - current_zone = pp->zone_id; - - if (!current_instance) - current_instance = pp->zoneInstance; - - if(strlen(pp->name) == 0) // Sanity check in case pp never loaded - return false; - - end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, profile=\'", pp->name, GetZoneName(current_zone), current_zone, current_instance, pp->x, pp->y, pp->z); - end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end,"\', extprofile=\'"); - end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end,"\',class=%d,level=%d,xtargets=%u WHERE id=%u", pp->class_, pp->level, MaxXTargets, charid); - - return (uint32) (end - (*query)); -} - - - // Create appropriate ItemInst class ItemInst* SharedDatabase::CreateItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) { @@ -1362,72 +1214,52 @@ ItemInst* SharedDatabase::CreateBaseItem(const Item_Struct* item, int16 charges) } int32 SharedDatabase::DeleteStalePlayerCorpses() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if(RuleB(Zone, EnableShadowrest)) { - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried = 1 WHERE IsBurried=0 and " - "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", - (RuleI(Character, CorpseDecayTimeMS) / 1000)), errbuf, 0, &affected_rows)) - { - safe_delete_array(query); + std::string query = StringFormat("UPDATE player_corpses SET IsBurried = 1 WHERE IsBurried = 0 AND " + "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d AND NOT timeofdeath = 0", + (RuleI(Character, CorpseDecayTimeMS) / 1000)); + auto results = QueryDatabase(query); + if (!results.Success()) return -1; - } - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses where (UNIX_TIMESTAMP() - " - "UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", (RuleI(Character, CorpseDecayTimeMS) / 1000)), - errbuf, 0, &affected_rows)) - { - safe_delete_array(query); - return -1; - } + + return results.RowsAffected(); } - safe_delete_array(query); - return affected_rows; + std::string query = StringFormat("DELETE FROM player_corpses WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d " + "AND NOT timeofdeath = 0", (RuleI(Character, CorpseDecayTimeMS) / 1000)); + auto results = QueryDatabase(query); + if (!results.Success()) + return -1; + + return results.RowsAffected(); } int32 SharedDatabase::DeleteStalePlayerBackups() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - // 1209600 seconds = 2 weeks - if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses_backup where (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > 1209600"), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - return -1; - } - safe_delete_array(query); + const std::string query = "DELETE FROM player_corpses_backup WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > 1209600"; + auto results = QueryDatabase(query); + if (!results.Success()) + return -1; - return affected_rows; + return results.RowsAffected(); } bool SharedDatabase::GetCommandSettings(std::map &commands) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - strcpy(query, "SELECT command,access from commands"); - commands.clear(); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - commands[row[0]]=atoi(row[1]); - } - mysql_free_result(result); - return true; - } else { - std::cerr << "Error in GetCommands query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + const std::string query = "SELECT command, access FROM commands"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetCommands query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - return false; + commands.clear(); + + for (auto row = results.begin(); row != results.end(); ++row) + commands[row[0]]=atoi(row[1]); + + return true; } bool SharedDatabase::LoadSkillCaps() { @@ -1462,31 +1294,25 @@ void SharedDatabase::LoadSkillCaps(void *data) { uint32 level_count = HARD_LEVEL_CAP + 1; uint16 *skill_caps_table = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, - "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level"), - errbuf, &result)) { - safe_delete_array(query); - - while((row = mysql_fetch_row(result))) { - uint8 skillID = atoi(row[0]); - uint8 class_ = atoi(row[1]) - 1; - uint8 level = atoi(row[2]); - uint16 cap = atoi(row[3]); - if(skillID >= skill_count || class_ >= class_count || level >= level_count) - continue; - - uint32 index = (((class_ * skill_count) + skillID) * level_count) + level; - skill_caps_table[index] = cap; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error loading skill caps from database: %s", errbuf); - safe_delete_array(query); + const std::string query = "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error loading skill caps from database: %s", results.ErrorMessage().c_str()); + return; } + + for(auto row = results.begin(); row != results.end(); ++row) { + uint8 skillID = atoi(row[0]); + uint8 class_ = atoi(row[1]) - 1; + uint8 level = atoi(row[2]); + uint16 cap = atoi(row[3]); + + if(skillID >= skill_count || class_ >= class_count || level >= level_count) + continue; + + uint32 index = (((class_ * skill_count) + skillID) * level_count) + level; + skill_caps_table[index] = cap; + } } uint16 SharedDatabase::GetSkillCap(uint8 Class_, SkillUseTypes Skill, uint8 Level) { @@ -1569,32 +1395,20 @@ uint8 SharedDatabase::GetTrainLevel(uint8 Class_, SkillUseTypes Skill, uint8 Lev void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID) { - const char *DSQuery = "SELECT `spellid`, `type` from `damageshieldtypes` WHERE `spellid` > 0 " - "AND `spellid` <= %i"; + std::string query = StringFormat("SELECT `spellid`, `type` FROM `damageshieldtypes` WHERE `spellid` > 0 " + "AND `spellid` <= %i", iMaxSpellID); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadDamageShieldTypes: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - const char *ERR_MYSQLERROR = "Error in LoadDamageShieldTypes: %s %s"; + for(auto row = results.begin(); row != results.end(); ++row) { + int spellID = atoi(row[0]); + if((spellID > 0) && (spellID <= iMaxSpellID)) + sp[spellID].DamageShieldType = atoi(row[1]); + } - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query,MakeAnyLenString(&query,DSQuery,iMaxSpellID),errbuf,&result)) { - - while((row = mysql_fetch_row(result))) { - - int SpellID = atoi(row[0]); - if((SpellID > 0) && (SpellID <= iMaxSpellID)) { - sp[SpellID].DamageShieldType = atoi(row[1]); - } - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); - } } const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { @@ -1602,207 +1416,191 @@ const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { } int SharedDatabase::GetMaxSpellID() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - int32 ret = 0; - if(RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM spells_new"), - errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - ret = atoi(row[0]); - mysql_free_result(result); - } else { - _log(SPELLS__LOAD_ERR, "Error in GetMaxSpellID query '%s' %s", query, errbuf); - safe_delete_array(query); - ret = -1; - } - return ret; + std::string query = "SELECT MAX(id) FROM spells_new"; + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(SPELLS__LOAD_ERR, "Error in GetMaxSpellID query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return -1; + } + + auto row = results.begin(); + + return atoi(row[0]); } void SharedDatabase::LoadSpells(void *data, int max_spells) { SPDat_Spell_Struct *sp = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, - "SELECT * FROM spells_new ORDER BY id ASC"), - errbuf, &result)) { - safe_delete_array(query); + const std::string query = "SELECT * FROM spells_new ORDER BY id ASC"; + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(SPELLS__LOAD_ERR, "Error in LoadSpells query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - int tempid = 0; - int counter = 0; + if(results.ColumnCount() <= SPELL_LOAD_FIELD_COUNT) { + _log(SPELLS__LOAD_ERR, "Fatal error loading spells: Spell field count < SPELL_LOAD_FIELD_COUNT(%u)", SPELL_LOAD_FIELD_COUNT); + return; + } - if(result && mysql_field_count(getMySQL()) <= SPELL_LOAD_FIELD_COUNT) { - _log(SPELLS__LOAD_ERR, "Fatal error loading spells: Spell field count < SPELL_LOAD_FIELD_COUNT(%u)", SPELL_LOAD_FIELD_COUNT); - mysql_free_result(result); - return; - } + int tempid = 0; + int counter = 0; - while (row = mysql_fetch_row(result)) { - tempid = atoi(row[0]); - if(tempid >= max_spells) { - _log(SPELLS__LOAD_ERR, "Non fatal error: spell.id >= max_spells, ignoring."); - continue; - } + for (auto row = results.begin(); row != results.end(); ++row) { + tempid = atoi(row[0]); + if(tempid >= max_spells) { + _log(SPELLS__LOAD_ERR, "Non fatal error: spell.id >= max_spells, ignoring."); + continue; + } - ++counter; - sp[tempid].id = tempid; - strn0cpy(sp[tempid].name, row[1], sizeof(sp[tempid].name)); - strn0cpy(sp[tempid].player_1, row[2], sizeof(sp[tempid].player_1)); - strn0cpy(sp[tempid].teleport_zone, row[3], sizeof(sp[tempid].teleport_zone)); - strn0cpy(sp[tempid].you_cast, row[4], sizeof(sp[tempid].you_cast)); - strn0cpy(sp[tempid].other_casts, row[5], sizeof(sp[tempid].other_casts)); - strn0cpy(sp[tempid].cast_on_you, row[6], sizeof(sp[tempid].cast_on_you)); - strn0cpy(sp[tempid].cast_on_other, row[7], sizeof(sp[tempid].cast_on_other)); - strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades)); + ++counter; + sp[tempid].id = tempid; + strn0cpy(sp[tempid].name, row[1], sizeof(sp[tempid].name)); + strn0cpy(sp[tempid].player_1, row[2], sizeof(sp[tempid].player_1)); + strn0cpy(sp[tempid].teleport_zone, row[3], sizeof(sp[tempid].teleport_zone)); + strn0cpy(sp[tempid].you_cast, row[4], sizeof(sp[tempid].you_cast)); + strn0cpy(sp[tempid].other_casts, row[5], sizeof(sp[tempid].other_casts)); + strn0cpy(sp[tempid].cast_on_you, row[6], sizeof(sp[tempid].cast_on_you)); + strn0cpy(sp[tempid].cast_on_other, row[7], sizeof(sp[tempid].cast_on_other)); + strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades)); - sp[tempid].range=static_cast(atof(row[9])); - sp[tempid].aoerange=static_cast(atof(row[10])); - sp[tempid].pushback=static_cast(atof(row[11])); - sp[tempid].pushup=static_cast(atof(row[12])); - sp[tempid].cast_time=atoi(row[13]); - sp[tempid].recovery_time=atoi(row[14]); - sp[tempid].recast_time=atoi(row[15]); - sp[tempid].buffdurationformula=atoi(row[16]); - sp[tempid].buffduration=atoi(row[17]); - sp[tempid].AEDuration=atoi(row[18]); - sp[tempid].mana=atoi(row[19]); + sp[tempid].range=static_cast(atof(row[9])); + sp[tempid].aoerange=static_cast(atof(row[10])); + sp[tempid].pushback=static_cast(atof(row[11])); + sp[tempid].pushup=static_cast(atof(row[12])); + sp[tempid].cast_time=atoi(row[13]); + sp[tempid].recovery_time=atoi(row[14]); + sp[tempid].recast_time=atoi(row[15]); + sp[tempid].buffdurationformula=atoi(row[16]); + sp[tempid].buffduration=atoi(row[17]); + sp[tempid].AEDuration=atoi(row[18]); + sp[tempid].mana=atoi(row[19]); - int y=0; - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value - for(y=0; y < EFFECT_COUNT; y++) - sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].max[y]=atoi(row[44+y]); + int y=0; + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value - for(y=0; y< 4;y++) - sp[tempid].components[y]=atoi(row[58+y]); + for(y=0; y < EFFECT_COUNT; y++) + sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value - for(y=0; y< 4;y++) - sp[tempid].component_counts[y]=atoi(row[62+y]); + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].max[y]=atoi(row[44+y]); - for(y=0; y< 4;y++) - sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); + for(y=0; y< 4;y++) + sp[tempid].components[y]=atoi(row[58+y]); - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].formula[y]=atoi(row[70+y]); + for(y=0; y< 4;y++) + sp[tempid].component_counts[y]=atoi(row[62+y]); - sp[tempid].goodEffect=atoi(row[83]); - sp[tempid].Activated=atoi(row[84]); - sp[tempid].resisttype=atoi(row[85]); + for(y=0; y< 4;y++) + sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].effectid[y]=atoi(row[86+y]); + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].formula[y]=atoi(row[70+y]); - sp[tempid].targettype = (SpellTargetType) atoi(row[98]); - sp[tempid].basediff=atoi(row[99]); - int tmp_skill = atoi(row[100]);; - if(tmp_skill < 0 || tmp_skill > HIGHEST_SKILL) - sp[tempid].skill = SkillBegging; /* not much better we can do. */ // can probably be changed to client-based 'SkillNone' once activated - else - sp[tempid].skill = (SkillUseTypes) tmp_skill; - sp[tempid].zonetype=atoi(row[101]); - sp[tempid].EnvironmentType=atoi(row[102]); - sp[tempid].TimeOfDay=atoi(row[103]); + sp[tempid].goodEffect=atoi(row[83]); + sp[tempid].Activated=atoi(row[84]); + sp[tempid].resisttype=atoi(row[85]); - for(y=0; y < PLAYER_CLASS_COUNT;y++) - sp[tempid].classes[y]=atoi(row[104+y]); + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].effectid[y]=atoi(row[86+y]); - sp[tempid].CastingAnim=atoi(row[120]); - sp[tempid].SpellAffectIndex=atoi(row[123]); - sp[tempid].disallow_sit=atoi(row[124]); + sp[tempid].targettype = (SpellTargetType) atoi(row[98]); + sp[tempid].basediff=atoi(row[99]); - for (y = 0; y < 16; y++) - sp[tempid].deities[y]=atoi(row[126+y]); + int tmp_skill = atoi(row[100]);; - sp[tempid].uninterruptable=atoi(row[146]) != 0; - sp[tempid].ResistDiff=atoi(row[147]); - sp[tempid].dot_stacking_exempt=atoi(row[148]); - sp[tempid].RecourseLink = atoi(row[150]); - sp[tempid].no_partial_resist = atoi(row[151]) != 0; + if(tmp_skill < 0 || tmp_skill > HIGHEST_SKILL) + sp[tempid].skill = SkillBegging; /* not much better we can do. */ // can probably be changed to client-based 'SkillNone' once activated + else + sp[tempid].skill = (SkillUseTypes) tmp_skill; - sp[tempid].short_buff_box = atoi(row[154]); - sp[tempid].descnum = atoi(row[155]); - sp[tempid].effectdescnum = atoi(row[157]); + sp[tempid].zonetype=atoi(row[101]); + sp[tempid].EnvironmentType=atoi(row[102]); + sp[tempid].TimeOfDay=atoi(row[103]); - sp[tempid].npc_no_los = atoi(row[159]) != 0; - sp[tempid].reflectable = atoi(row[161]) != 0; - sp[tempid].bonushate=atoi(row[162]); + for(y=0; y < PLAYER_CLASS_COUNT;y++) + sp[tempid].classes[y]=atoi(row[104+y]); - sp[tempid].EndurCost=atoi(row[166]); - sp[tempid].EndurTimerIndex=atoi(row[167]); - sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; - sp[tempid].HateAdded=atoi(row[173]); - sp[tempid].EndurUpkeep=atoi(row[174]); - sp[tempid].numhitstype = atoi(row[175]); - sp[tempid].numhits = atoi(row[176]); - sp[tempid].pvpresistbase=atoi(row[177]); - sp[tempid].pvpresistcalc=atoi(row[178]); - sp[tempid].pvpresistcap=atoi(row[179]); - sp[tempid].spell_category=atoi(row[180]); - sp[tempid].can_mgb=atoi(row[185]); - sp[tempid].dispel_flag = atoi(row[186]); - sp[tempid].MinResist = atoi(row[189]); - sp[tempid].MaxResist = atoi(row[190]); - sp[tempid].viral_targets = atoi(row[191]); - sp[tempid].viral_timer = atoi(row[192]); - sp[tempid].NimbusEffect = atoi(row[193]); - sp[tempid].directional_start = static_cast(atoi(row[194])); - sp[tempid].directional_end = static_cast(atoi(row[195])); - sp[tempid].not_extendable = atoi(row[197]) != 0; - sp[tempid].suspendable = atoi(row[200]) != 0; - sp[tempid].viral_range = atoi(row[201]); - sp[tempid].spellgroup=atoi(row[207]); - sp[tempid].rank = atoi(row[208]); - sp[tempid].powerful_flag=atoi(row[209]); - sp[tempid].CastRestriction = atoi(row[211]); - sp[tempid].AllowRest = atoi(row[212]) != 0; - sp[tempid].InCombat = atoi(row[213]) != 0; - sp[tempid].OutofCombat = atoi(row[214]) != 0; - sp[tempid].aemaxtargets = atoi(row[218]); - sp[tempid].maxtargets = atoi(row[219]); - sp[tempid].persistdeath = atoi(row[224]) != 0; - sp[tempid].min_dist = atof(row[227]); - sp[tempid].min_dist_mod = atof(row[228]); - sp[tempid].max_dist = atof(row[229]); - sp[tempid].max_dist_mod = atof(row[230]); - sp[tempid].min_range = static_cast(atoi(row[231])); - sp[tempid].DamageShieldType = 0; - } - mysql_free_result(result); + sp[tempid].CastingAnim=atoi(row[120]); + sp[tempid].SpellAffectIndex=atoi(row[123]); + sp[tempid].disallow_sit=atoi(row[124]); - LoadDamageShieldTypes(sp, max_spells); - } else { - _log(SPELLS__LOAD_ERR, "Error in LoadSpells query '%s' %s", query, errbuf); - safe_delete_array(query); - } + for (y = 0; y < 16; y++) + sp[tempid].deities[y]=atoi(row[126+y]); + + sp[tempid].uninterruptable=atoi(row[146]) != 0; + sp[tempid].ResistDiff=atoi(row[147]); + sp[tempid].dot_stacking_exempt=atoi(row[148]); + sp[tempid].RecourseLink = atoi(row[150]); + sp[tempid].no_partial_resist = atoi(row[151]) != 0; + + sp[tempid].short_buff_box = atoi(row[154]); + sp[tempid].descnum = atoi(row[155]); + sp[tempid].effectdescnum = atoi(row[157]); + + sp[tempid].npc_no_los = atoi(row[159]) != 0; + sp[tempid].reflectable = atoi(row[161]) != 0; + sp[tempid].bonushate=atoi(row[162]); + + sp[tempid].EndurCost=atoi(row[166]); + sp[tempid].EndurTimerIndex=atoi(row[167]); + sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0; + sp[tempid].HateAdded=atoi(row[173]); + sp[tempid].EndurUpkeep=atoi(row[174]); + sp[tempid].numhitstype = atoi(row[175]); + sp[tempid].numhits = atoi(row[176]); + sp[tempid].pvpresistbase=atoi(row[177]); + sp[tempid].pvpresistcalc=atoi(row[178]); + sp[tempid].pvpresistcap=atoi(row[179]); + sp[tempid].spell_category=atoi(row[180]); + sp[tempid].can_mgb=atoi(row[185]); + sp[tempid].dispel_flag = atoi(row[186]); + sp[tempid].MinResist = atoi(row[189]); + sp[tempid].MaxResist = atoi(row[190]); + sp[tempid].viral_targets = atoi(row[191]); + sp[tempid].viral_timer = atoi(row[192]); + sp[tempid].NimbusEffect = atoi(row[193]); + sp[tempid].directional_start = static_cast(atoi(row[194])); + sp[tempid].directional_end = static_cast(atoi(row[195])); + sp[tempid].not_extendable = atoi(row[197]) != 0; + sp[tempid].suspendable = atoi(row[200]) != 0; + sp[tempid].viral_range = atoi(row[201]); + sp[tempid].spellgroup=atoi(row[207]); + sp[tempid].rank = atoi(row[208]); + sp[tempid].powerful_flag=atoi(row[209]); + sp[tempid].CastRestriction = atoi(row[211]); + sp[tempid].AllowRest = atoi(row[212]) != 0; + sp[tempid].InCombat = atoi(row[213]) != 0; + sp[tempid].OutofCombat = atoi(row[214]) != 0; + sp[tempid].aemaxtargets = atoi(row[218]); + sp[tempid].maxtargets = atoi(row[219]); + sp[tempid].persistdeath = atoi(row[224]) != 0; + sp[tempid].min_dist = atof(row[227]); + sp[tempid].min_dist_mod = atof(row[228]); + sp[tempid].max_dist = atof(row[229]); + sp[tempid].max_dist_mod = atof(row[230]); + sp[tempid].min_range = static_cast(atoi(row[231])); + sp[tempid].DamageShieldType = 0; + } + + LoadDamageShieldTypes(sp, max_spells); } int SharedDatabase::GetMaxBaseDataLevel() { - char errbuf[MYSQL_ERRMSG_SIZE]; - const char *query = "SELECT MAX(level) FROM base_data"; - MYSQL_RES *result; - MYSQL_ROW row; - int32 ret = 0; - if(RunQuery(query, strlen(query), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) { - ret = atoi(row[0]); - mysql_free_result(result); - } else { - ret = -1; - mysql_free_result(result); - } - } else { - LogFile->write(EQEMuLog::Error, "Error in GetMaxBaseDataLevel query '%s' %s", query, errbuf); - ret = -1; + const std::string query = "SELECT MAX(level) FROM base_data"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetMaxBaseDataLevel query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return -1; } - return ret; + + if (results.RowCount() == 0) + return -1; + + auto row = results.begin(); + + return atoi(row[0]); } bool SharedDatabase::LoadBaseData() { @@ -1814,7 +1612,7 @@ bool SharedDatabase::LoadBaseData() { EQEmu::IPCMutex mutex("base_data"); mutex.Lock(); base_data_mmf = new EQEmu::MemoryMappedFile("shared/base_data"); - + int size = 16 * (GetMaxBaseDataLevel() + 1) * sizeof(BaseDataStruct); if(size == 0) { EQ_EXCEPT("SharedDatabase", "Base Data size is zero"); @@ -1835,53 +1633,52 @@ bool SharedDatabase::LoadBaseData() { void SharedDatabase::LoadBaseData(void *data, int max_level) { char *base_ptr = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - const char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query, strlen(query), errbuf, &result)) { - - int lvl = 0; - int cl = 0; - while (row = mysql_fetch_row(result)) { - lvl = atoi(row[0]); - cl = atoi(row[1]); - if(lvl <= 0) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level <= 0, ignoring."); - continue; - } - if(lvl >= max_level) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level >= max_level, ignoring."); - continue; - } - - if(cl <= 0) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.cl <= 0, ignoring."); - continue; - } - - if(cl > 16) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.class > 16, ignoring."); - continue; - } - - BaseDataStruct *bd = reinterpret_cast(base_ptr + (((16 * (lvl - 1)) + (cl - 1)) * sizeof(BaseDataStruct))); - bd->base_hp = atof(row[2]); - bd->base_mana = atof(row[3]); - bd->base_end = atof(row[4]); - bd->unk1 = atof(row[5]); - bd->unk2 = atof(row[6]); - bd->hp_factor = atof(row[7]); - bd->mana_factor = atof(row[8]); - bd->endurance_factor = atof(row[9]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadBaseData query '%s' %s", query, errbuf); - safe_delete_array(query); + const std::string query = "SELECT * FROM base_data ORDER BY level, class ASC"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadBaseData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return; } + + int lvl = 0; + int cl = 0; + + for (auto row = results.begin(); row != results.end(); ++row) { + lvl = atoi(row[0]); + cl = atoi(row[1]); + + if(lvl <= 0) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level <= 0, ignoring."); + continue; + } + + if(lvl >= max_level) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level >= max_level, ignoring."); + continue; + } + + if(cl <= 0) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.cl <= 0, ignoring."); + continue; + } + + if(cl > 16) { + LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.class > 16, ignoring."); + continue; + } + + BaseDataStruct *bd = reinterpret_cast(base_ptr + (((16 * (lvl - 1)) + (cl - 1)) * sizeof(BaseDataStruct))); + bd->base_hp = atof(row[2]); + bd->base_mana = atof(row[3]); + bd->base_end = atof(row[4]); + bd->unk1 = atof(row[5]); + bd->unk2 = atof(row[6]); + bd->hp_factor = atof(row[7]); + bd->mana_factor = atof(row[8]); + bd->endurance_factor = atof(row[9]); + } + } const BaseDataStruct* SharedDatabase::GetBaseData(int lvl, int cl) { @@ -1917,155 +1714,146 @@ void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot loot_table_count = 0; max_loot_table = 0; loot_table_entries = 0; - const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; + const std::string query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - loot_table_count = static_cast(atoul(row[0])); - max_loot_table = static_cast(atoul(row[1])); - loot_table_entries = static_cast(atoul(row[2])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } + if (results.RowCount() == 0) + return; + + auto row = results.begin(); + + loot_table_count = static_cast(atoul(row[0])); + max_loot_table = static_cast(atoul(row[1])); + loot_table_entries = static_cast(atoul(row[2])); } void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_drop, uint32 &loot_drop_entries) { loot_drop_count = 0; max_loot_drop = 0; loot_drop_entries = 0; - const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - loot_drop_count = static_cast(atoul(row[0])); - max_loot_drop = static_cast(atoul(row[1])); - loot_drop_entries = static_cast(atoul(row[2])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } + const std::string query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + if (results.RowCount() == 0) + return; + + auto row =results.begin(); + + loot_drop_count = static_cast(atoul(row[0])); + max_loot_drop = static_cast(atoul(row[1])); + loot_drop_entries = static_cast(atoul(row[2])); } void SharedDatabase::LoadLootTables(void *data, uint32 size) { EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); - const char *query = "SELECT loottable.id, loottable.mincash, loottable.maxcash, loottable.avgcoin," - " loottable_entries.lootdrop_id, loottable_entries.multiplier, loottable_entries.droplimit, " - "loottable_entries.mindrop, loottable_entries.probability FROM loottable LEFT JOIN loottable_entries" - " ON loottable.id = loottable_entries.loottable_id ORDER BY id"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; + uint8 loot_table[sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)]; LootTable_Struct *lt = reinterpret_cast(loot_table); - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + - (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - } + const std::string query = "SELECT loottable.id, loottable.mincash, loottable.maxcash, loottable.avgcoin, " + "loottable_entries.lootdrop_id, loottable_entries.multiplier, loottable_entries.droplimit, " + "loottable_entries.mindrop, loottable_entries.probability FROM loottable LEFT JOIN loottable_entries " + "ON loottable.id = loottable_entries.loottable_id ORDER BY id"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); - current_entry = 0; - current_id = id; - lt->mincash = static_cast(atoul(row[1])); - lt->maxcash = static_cast(atoul(row[2])); - lt->avgcoin = static_cast(atoul(row[3])); - } + uint32 current_id = 0; + uint32 current_entry = 0; - if(current_entry > 128) { - continue; - } + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = static_cast(atoul(row[0])); + if(id != current_id) { + if(current_id != 0) + hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - if(!row[4]) { - continue; - } + memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); + current_entry = 0; + current_id = id; + lt->mincash = static_cast(atoul(row[1])); + lt->maxcash = static_cast(atoul(row[2])); + lt->avgcoin = static_cast(atoul(row[3])); + } - lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); - lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); - lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); - lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); - lt->Entries[current_entry].probability = static_cast(atof(row[8])); + if(current_entry > 128) + continue; - ++(lt->NumEntries); - ++current_entry; - } - if(current_id != 0) { - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + - (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - } + if(!row[4]) + continue; + + lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); + lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); + lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); + lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); + lt->Entries[current_entry].probability = static_cast(atof(row[8])); + + ++(lt->NumEntries); + ++current_entry; + } + + if(current_id != 0) + hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } } void SharedDatabase::LoadLootDrops(void *data, uint32 size) { - EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); - const char *query = "SELECT lootdrop.id, lootdrop_entries.item_id, lootdrop_entries.item_charges, " - "lootdrop_entries.equip_item, lootdrop_entries.chance, lootdrop_entries.minlevel, " - "lootdrop_entries.maxlevel, lootdrop_entries.multiplier FROM lootdrop JOIN lootdrop_entries " - "ON lootdrop.id = lootdrop_entries.lootdrop_id ORDER BY lootdrop_id"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; + EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); uint8 loot_drop[sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)]; LootDrop_Struct *ld = reinterpret_cast(loot_drop); - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + - (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - } - memset(loot_drop, 0, sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)); - current_entry = 0; - current_id = id; - } + const std::string query = "SELECT lootdrop.id, lootdrop_entries.item_id, lootdrop_entries.item_charges, " + "lootdrop_entries.equip_item, lootdrop_entries.chance, lootdrop_entries.minlevel, " + "lootdrop_entries.maxlevel, lootdrop_entries.multiplier FROM lootdrop JOIN lootdrop_entries " + "ON lootdrop.id = lootdrop_entries.lootdrop_id ORDER BY lootdrop_id"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error getting loot drop info from database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); + } - if(current_entry >= 1260) { - continue; - } + uint32 current_id = 0; + uint32 current_entry = 0; - ld->Entries[current_entry].item_id = static_cast(atoul(row[1])); - ld->Entries[current_entry].item_charges = static_cast(atoi(row[2])); - ld->Entries[current_entry].equip_item = static_cast(atoi(row[3])); - ld->Entries[current_entry].chance = static_cast(atof(row[4])); - ld->Entries[current_entry].minlevel = static_cast(atoi(row[5])); - ld->Entries[current_entry].maxlevel = static_cast(atoi(row[6])); - ld->Entries[current_entry].multiplier = static_cast(atoi(row[7])); + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = static_cast(atoul(row[0])); + if(id != current_id) { + if(current_id != 0) + hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) +(sizeof(LootDropEntries_Struct) * ld->NumEntries))); - ++(ld->NumEntries); - ++current_entry; - } - if(current_id != 0) { - hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + - (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - } + memset(loot_drop, 0, sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)); + current_entry = 0; + current_id = id; + } + + if(current_entry >= 1260) + continue; + + ld->Entries[current_entry].item_id = static_cast(atoul(row[1])); + ld->Entries[current_entry].item_charges = static_cast(atoi(row[2])); + ld->Entries[current_entry].equip_item = static_cast(atoi(row[3])); + ld->Entries[current_entry].chance = static_cast(atof(row[4])); + ld->Entries[current_entry].minlevel = static_cast(atoi(row[5])); + ld->Entries[current_entry].maxlevel = static_cast(atoi(row[6])); + ld->Entries[current_entry].multiplier = static_cast(atoi(row[7])); + + ++(ld->NumEntries); + ++current_entry; + } + + if(current_id != 0) + hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot drop info from database: %s, %s", query, errbuf); - } } bool SharedDatabase::LoadLoot() { @@ -2120,74 +1908,44 @@ const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { return nullptr; } -void SharedDatabase::GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT inspectmessage FROM character_ WHERE name='%s'", playername), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - memcpy(message, row[0], sizeof(InspectMessage_Struct)); - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in GetPlayerInspectMessage query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); +void SharedDatabase::LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message) { + std::string query = StringFormat("SELECT `inspect_message` FROM `character_inspect_messages` WHERE `id` = %u LIMIT 1", character_id); + auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "SharedDatabase::LoadCharacterInspectMessage", query); + auto row = results.begin(); + memcpy(message, "", sizeof(InspectMessage_Struct)); + for (auto row = results.begin(); row != results.end(); ++row) { + memcpy(message, row[0], sizeof(InspectMessage_Struct)); } } -void SharedDatabase::SetPlayerInspectMessage(char* playername, const InspectMessage_Struct* message) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - std::string msg = EscapeString(message->text); - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", msg.c_str(), playername), errbuf)) { - std::cerr << "Error in SetPlayerInspectMessage query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); +void SharedDatabase::SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message) { + std::string query = StringFormat("REPLACE INTO `character_inspect_messages` (id, inspect_message) VALUES (%u, '%s')", character_id, EscapeString(message->text).c_str()); + auto results = QueryDatabase(query); ThrowDBError(results.ErrorMessage(), "SharedDatabase::SaveCharacterInspectMessage", query); } void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT BotInspectMessage FROM bots WHERE BotID=%i", botid), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - memcpy(message, row[0], sizeof(InspectMessage_Struct)); - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in GetBotInspectMessage query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT BotInspectMessage FROM bots WHERE BotID = %i", botid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetBotInspectMessage query '" << query << "' " << results.ErrorMessage() << std::endl; + return; } + + if (results.RowCount() != 1) + return; + + auto row = results.begin(); + memcpy(message, row[0], sizeof(InspectMessage_Struct)); + } void SharedDatabase::SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - std::string msg = EscapeString(message->text); - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", msg.c_str(), botid), errbuf)) { - std::cerr << "Error in SetBotInspectMessage query '" << query << "' " << errbuf << std::endl; - } + std::string query = StringFormat("UPDATE bots SET BotInspectMessage = '%s' WHERE BotID = %i", msg.c_str(), botid); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in SetBotInspectMessage query '" << query << "' " << results.ErrorMessage() << std::endl; - safe_delete_array(query); } diff --git a/common/shareddb.h b/common/shareddb.h index 0fd72426c..14793f792 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -40,13 +40,10 @@ public: bool SetGMSpeed(uint32 account_id, uint8 gmspeed); uint8 GetGMSpeed(uint32 account_id); bool SetHideMe(uint32 account_id, uint8 hideme); - bool GetPlayerProfile(uint32 account_id, char* name, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, char* current_zone = 0, uint32 *current_instance = 0); - bool SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); - uint32 SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets); int32 DeleteStalePlayerCorpses(); int32 DeleteStalePlayerBackups(); - void GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message); - void SetPlayerInspectMessage(char* playername, const InspectMessage_Struct* message); + void LoadCharacterInspectMessage(uint32 character_id, InspectMessage_Struct* message); + void SaveCharacterInspectMessage(uint32 character_id, const InspectMessage_Struct* message); void GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message); void SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message); bool GetCommandSettings(std::map &commands); @@ -57,6 +54,10 @@ public: */ bool SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end); bool SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id); + bool DeleteSharedBankSlot(uint32 char_id, int16 slot_id); + bool DeleteInventorySlot(uint32 char_id, int16 slot_id); + bool UpdateInventorySlot(uint32 char_id, const ItemInst* inst, int16 slot_id); + bool UpdateSharedBankSlot(uint32 char_id, const ItemInst* inst, int16 slot_id); bool VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst); bool GetSharedBank(uint32 id, Inventory* inv, bool is_charid); int32 GetSharedPlatinum(uint32 account_id); diff --git a/common/skills.cpp b/common/skills.cpp new file mode 100644 index 000000000..24a32196e --- /dev/null +++ b/common/skills.cpp @@ -0,0 +1,56 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include "types.h" +#include "skills.h" + +bool EQEmu::IsTradeskill(SkillUseTypes skill) +{ + switch (skill) { + case SkillFishing: + case SkillMakePoison: + case SkillTinkering: + case SkillResearch: + case SkillAlchemy: + case SkillBaking: + case SkillTailoring: + case SkillBlacksmithing: + case SkillFletching: + case SkillBrewing: + case SkillPottery: + case SkillJewelryMaking: + return true; + default: + return false; + } +} + +bool EQEmu::IsSpecializedSkill(SkillUseTypes skill) +{ + // this could be a simple if, but if this is more portable if any IDs change (probably won't) + // or any other specialized are added (also unlikely) + switch (skill) { + case SkillSpecializeAbjure: + case SkillSpecializeAlteration: + case SkillSpecializeConjuration: + case SkillSpecializeDivination: + case SkillSpecializeEvocation: + return true; + default: + return false; + } +} diff --git a/common/skills.h b/common/skills.h index 88b05967f..b4064ab3f 100644 --- a/common/skills.h +++ b/common/skills.h @@ -260,4 +260,10 @@ typedef enum { #define HIGHEST_SKILL FRENZY */ +// for skill related helper functions +namespace EQEmu { + bool IsTradeskill(SkillUseTypes skill); + bool IsSpecializedSkill(SkillUseTypes skill); +} + #endif diff --git a/common/struct_strategy.cpp b/common/struct_strategy.cpp index 9507fd231..9b568b12e 100644 --- a/common/struct_strategy.cpp +++ b/common/struct_strategy.cpp @@ -17,6 +17,11 @@ StructStrategy::StructStrategy() { } void StructStrategy::Encode(EQApplicationPacket **p, EQStream *dest, bool ack_req) const { + if((*p)->GetOpcodeBypass() != 0) { + PassEncoder(p, dest, ack_req); + return; + } + EmuOpcode op = (*p)->GetOpcode(); Encoder proc = encoders[op]; proc(p, dest, ack_req); diff --git a/common/unix.h b/common/unix.h index c78d05cb2..d4155f946 100644 --- a/common/unix.h +++ b/common/unix.h @@ -18,9 +18,6 @@ #ifndef _WINDOWS #ifndef __UNIX_H__ #define __UNIX_H__ - #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP - #define PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP {0, 0, 0, PTHREAD_MUTEX_RECURSIVE_NP, __LOCK_INITIALIZER} - #endif #include typedef int SOCKET; diff --git a/luabind/luabind/detail/object_rep.hpp b/luabind/luabind/detail/object_rep.hpp index dafa6532a..93b3e39d6 100644 --- a/luabind/luabind/detail/object_rep.hpp +++ b/luabind/luabind/detail/object_rep.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace luabind { namespace detail { diff --git a/queryserv/database.cpp b/queryserv/database.cpp index f2dfebb56..4f7fdf10f 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -349,17 +349,16 @@ void Database::GeneralQueryReceive(ServerPacket *pack) { /* These are general queries passed from anywhere in zone instead of packing structures and breaking them down again and again */ - char *Query = nullptr; - Query = new char[pack->ReadUInt32() + 1]; - pack->ReadString(Query); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - 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); + char *queryBuffer = new char[pack->ReadUInt32() + 1]; + pack->ReadString(queryBuffer); + + std::string query(queryBuffer); + 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()); } - safe_delete_array(query); + safe_delete(pack); - safe_delete(Query); + safe_delete(queryBuffer); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f7972e98f..1c13ae26c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -8,18 +8,22 @@ SET(tests_sources SET(tests_headers atobool_test.h + data_verification_test.h fixed_memory_test.h fixed_memory_variable_test.h hextoi_32_64_test.h ipc_mutex_test.h memory_mapped_file_test.h string_util_test.h + skills_util_test.h ) ADD_EXECUTABLE(tests ${tests_sources} ${tests_headers}) TARGET_LINK_LIBRARIES(tests common cppunit) +INSTALL(TARGETS tests RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}) + IF(MSVC) SET_TARGET_PROPERTIES(tests PROPERTIES LINK_FLAGS_RELEASE "/OPT:REF /OPT:ICF") TARGET_LINK_LIBRARIES(tests "Ws2_32.lib") diff --git a/tests/data_verification_test.h b/tests/data_verification_test.h new file mode 100644 index 000000000..b9eb210fa --- /dev/null +++ b/tests/data_verification_test.h @@ -0,0 +1,108 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __EQEMU_TESTS_DATA_VERIFICATION_H +#define __EQEMU_TESTS_DATA_VERIFICATION_H + +#include "cppunit/cpptest.h" +#include "../common/data_verification.h" + +class DataVerificationTest : public Test::Suite { + typedef void(DataVerificationTest::*TestFunction)(void); +public: + DataVerificationTest() { + TEST_ADD(DataVerificationTest::Clamp); + TEST_ADD(DataVerificationTest::ClampUpper); + TEST_ADD(DataVerificationTest::ClampLower); + TEST_ADD(DataVerificationTest::ValueWithin); + } + + ~DataVerificationTest() { + } + + private: + void Clamp() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::Clamp(value_f, 0.0f, 1000.0f); + float vf2 = EQEmu::Clamp(value_f, 0.0f, 250.0f); + float vf3 = EQEmu::Clamp(value_f, 750.0f, 1000.0f); + + int vi1 = EQEmu::Clamp(value_i, 0, 1000); + int vi2 = EQEmu::Clamp(value_i, 0, 250); + int vi3 = EQEmu::Clamp(value_i, 750, 1000); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 250.0f); + TEST_ASSERT_EQUALS(vf3, 750.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 250); + TEST_ASSERT_EQUALS(vi3, 750); + } + + void ClampUpper() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::ClampUpper(value_f, 1000.0f); + float vf2 = EQEmu::ClampUpper(value_f, 250.0f); + + int vi1 = EQEmu::ClampUpper(value_i, 1000); + int vi2 = EQEmu::ClampUpper(value_i, 250); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 250.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 250); + } + + void ClampLower() { + float value_f = 500.0f; + int value_i = 500; + + float vf1 = EQEmu::ClampLower(value_f, 0.0f); + float vf2 = EQEmu::ClampLower(value_f, 750.0f); + + int vi1 = EQEmu::ClampLower(value_i, 0); + int vi2 = EQEmu::ClampLower(value_i, 750); + + TEST_ASSERT_EQUALS(vf1, 500.0f); + TEST_ASSERT_EQUALS(vf2, 750.0f); + + TEST_ASSERT_EQUALS(vi1, 500); + TEST_ASSERT_EQUALS(vi2, 750); + } + + void ValueWithin() { + float value_f = 500.0f; + int value_i = 500; + + TEST_ASSERT(EQEmu::ValueWithin(value_f, 0.0f, 1000.0f)); + TEST_ASSERT(!EQEmu::ValueWithin(value_f, 0.0f, 400.0f)); + TEST_ASSERT(!EQEmu::ValueWithin(value_f, 600.0f, 900.0f)); + + TEST_ASSERT(EQEmu::ValueWithin(value_i, 0, 1000)); + TEST_ASSERT(!EQEmu::ValueWithin(value_i, 0, 400)); + TEST_ASSERT(!EQEmu::ValueWithin(value_i, 600, 900)); + } +}; + +#endif diff --git a/tests/main.cpp b/tests/main.cpp index 285460da4..d64dfead4 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -27,6 +27,8 @@ #include "atobool_test.h" #include "hextoi_32_64_test.h" #include "string_util_test.h" +#include "data_verification_test.h" +#include "skills_util_test.h" int main() { try { @@ -40,6 +42,8 @@ int main() { tests.add(new atoboolTest()); tests.add(new hextoi_32_64_Test()); tests.add(new StringUtilTest()); + tests.add(new DataVerificationTest()); + tests.add(new SkillsUtilsTest()); tests.run(*output, true); } catch(...) { return -1; diff --git a/tests/skills_util_test.h b/tests/skills_util_test.h new file mode 100644 index 000000000..96507256e --- /dev/null +++ b/tests/skills_util_test.h @@ -0,0 +1,48 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef __EQEMU_TESTS_SKILLS_UTILS_H +#define __EQEMU_TESTS_SKILLS_UTILS_H + +#include "cppunit/cpptest.h" +#include "../common/skills.h" + +class SkillsUtilsTest: public Test::Suite { + typedef void(SkillsUtilsTest::*TestFunction)(void); +public: + SkillsUtilsTest() { + TEST_ADD(SkillsUtilsTest::IsTradeskill); + TEST_ADD(SkillsUtilsTest::IsSpecializedSkill); + } + + ~SkillsUtilsTest() { + } + + private: + void IsTradeskill() { + TEST_ASSERT(EQEmu::IsTradeskill(SkillPottery)); + TEST_ASSERT(!EQEmu::IsTradeskill(SkillParry)); + } + + void IsSpecializedSkill() { + TEST_ASSERT(EQEmu::IsSpecializedSkill(SkillSpecializeConjuration)); + TEST_ASSERT(!EQEmu::IsSpecializedSkill(SkillConjuration)) + } +}; + +#endif diff --git a/tests/string_util_test.h b/tests/string_util_test.h index 32efb5320..facb0cc72 100644 --- a/tests/string_util_test.h +++ b/tests/string_util_test.h @@ -23,7 +23,7 @@ #include "../common/string_util.h" class StringUtilTest : public Test::Suite { - typedef void(IPCMutexTest::*TestFunction)(void); + typedef void(StringUtilTest::*TestFunction)(void); public: StringUtilTest() { TEST_ADD(StringUtilTest::StringFormatTest); @@ -35,7 +35,7 @@ public: } private: - void StringFormatTest() { + void StringFormatTest() { const char* fmt = "Test: %c %d %4.2f"; char c = 'a'; int i = 2014; diff --git a/ucs/database.cpp b/ucs/database.cpp index a2f61c438..8130585d1 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -139,7 +139,7 @@ int Database::FindAccount(const char *characterName, Client *client) { client->ClearCharacters(); std::string query = StringFormat("SELECT `id`, `account_id`, `level` " - "FROM `character_` WHERE `name` = '%s' LIMIT 1", + "FROM `character_data` WHERE `name` = '%s' LIMIT 1", characterName); auto results = QueryDatabase(query); if (!results.Success()) { @@ -159,7 +159,7 @@ int Database::FindAccount(const char *characterName, Client *client) { _log(UCS__TRACE, "Account ID for %s is %i", characterName, accountID); - query = StringFormat("SELECT `id`, `name`, `level` FROM `character_` " + query = StringFormat("SELECT `id`, `name`, `level` FROM `character_data` " "WHERE `account_id` = %i AND `name` != '%s'", accountID, characterName); results = QueryDatabase(query); @@ -174,7 +174,7 @@ int Database::FindAccount(const char *characterName, Client *client) { bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::string MailKey) { - std::string query = StringFormat("SELECT `mailkey` FROM `character_` WHERE `name`='%s' LIMIT 1", + std::string query = StringFormat("SELECT `mailkey` FROM `character_data` WHERE `name`='%s' LIMIT 1", characterName.c_str()); auto results = QueryDatabase(query); if (!results.Success()) { @@ -202,7 +202,7 @@ bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::stri int Database::FindCharacter(const char *characterName) { char *safeCharName = RemoveApostrophes(characterName); - std::string query = StringFormat("SELECT `id` FROM `character_` WHERE `name`='%s' LIMIT 1", safeCharName); + std::string query = StringFormat("SELECT `id` FROM `character_data` WHERE `name`='%s' LIMIT 1", safeCharName); auto results = QueryDatabase(query); if (!results.Success()) { _log(UCS__ERROR, "FindCharacter failed. %s %s", query.c_str(), results.ErrorMessage().c_str()); @@ -523,9 +523,9 @@ void Database::ExpireMail() { time(nullptr) - RuleI(Mail, ExpireTrash)); results = QueryDatabase(query); if(results.Success()) - _log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); - else _log(UCS__INIT, "Expired %i trash messages.", results.RowsAffected()); + else + _log(UCS__ERROR, "Error expiring trash messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); } diff --git a/utils/patches/opcodes.conf b/utils/patches/opcodes.conf index ed2a685d7..562ba3347 100644 --- a/utils/patches/opcodes.conf +++ b/utils/patches/opcodes.conf @@ -147,7 +147,7 @@ OP_Begging=0x13e7 # ShowEQ 10/27/05 OP_InspectRequest=0x775d # ShowEQ 10/27/05 OP_Action2=0x0000 OP_BeginCast=0x3990 # ShowEQ 10/27/05 -OP_BuffFadeMsg=0x0b2d # ShowEQ 10/27/05 +OP_ColoredText=0x0b2d # ShowEQ 10/27/05 OP_Consent=0x1081 # ShowEQ 10/27/05 OP_LFGCommand=0x68ac # ShowEQ 10/27/05 OP_LFGGetMatchesRequest=0x022f # ShowEQ 10/27/05 diff --git a/utils/patches/patch_6.2.conf b/utils/patches/patch_6.2.conf index 25944a3b5..8eca3dd4e 100644 --- a/utils/patches/patch_6.2.conf +++ b/utils/patches/patch_6.2.conf @@ -150,7 +150,7 @@ OP_InspectRequest=0x2403 OP_Action2=0x0000 # ShowEQ 06/29/05 OP_BeginCast=0x3990 # ShowEQ 06/29/05 OP_WhoAllRequest=0x5cdd # ShowEQ 06/29/05 -OP_BuffFadeMsg=0x4bc6 # ShowEQ 06/29/05 +OP_ColoredText=0x4bc6 # ShowEQ 06/29/05 OP_Consent=0x1081 # ShowEQ 06/29/05 OP_LFGCommand=0x022f # ShowEQ 06/29/05 OP_LFGGetMatchesRequest=0x6f82 # ShowEQ 06/29/05 diff --git a/utils/patches/patch_RoF.conf b/utils/patches/patch_RoF.conf index 50bfd5580..97098946d 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -166,7 +166,7 @@ OP_InspectRequest=0x23f1 OP_InspectAnswer=0x5794 OP_InspectMessageUpdate=0x3064 OP_BeginCast=0x17ff -OP_BuffFadeMsg=0x41cb +OP_ColoredText=0x41cb OP_ConsentResponse=0x183d OP_MemorizeSpell=0x2fac OP_SwapSpell=0x4736 @@ -289,8 +289,10 @@ OP_LeadershipExpToggle=0x3ea6 OP_LeadershipExpUpdate=0x6922 OP_PurchaseLeadershipAA=0x1962 OP_UpdateLeadershipAA=0x56aa -OP_MarkNPC=0x2d9f +OP_MarkNPC=0x1a6c +OP_MarkRaidNPC=0x2d9f #unimplemented OP_ClearNPCMarks=0x0d2d +OP_ClearRaidNPCMarks=0x433a #unimplemented OP_DelegateAbility=0x7820 OP_SetGroupTarget=0x118a OP_Charm=0x7118 @@ -498,6 +500,8 @@ OP_GroupRoles=0x047c OP_GroupMakeLeader=0x4129 OP_DoGroupLeadershipAbility=0x17d7 OP_GroupLeadershipAAUpdate=0x6567 +OP_GroupMentor=0x56DB +OP_InspectBuffs=0x01f3 # LFG/LFP Opcodes OP_LFGCommand=0x4463 @@ -653,3 +657,5 @@ OP_RAWOutOfSession=0x0000 # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 + +OP_ItemRecastDelay=0x57ed diff --git a/utils/patches/patch_SoD.conf b/utils/patches/patch_SoD.conf index 642e0f4ab..338ee82e7 100644 --- a/utils/patches/patch_SoD.conf +++ b/utils/patches/patch_SoD.conf @@ -168,7 +168,7 @@ OP_GMLastName=0x3563 # C OP_InspectAnswer=0x4938 # C OP_Action2=0x7e4d # C OP_Damage? OP_BeginCast=0x0d5a # C -OP_BuffFadeMsg=0x569a # C +OP_ColoredText=0x569a # C OP_ConsentResponse=0x6e47 # C OP_MemorizeSpell=0x8543 # C OP_SwapSpell=0x3fd2 # C @@ -288,8 +288,10 @@ OP_LeadershipExpToggle=0x34c5 # C OP_LeadershipExpUpdate=0x69d0 # C OP_PurchaseLeadershipAA=0x07b3 # C OP_UpdateLeadershipAA=0x6948 # C -OP_MarkNPC=0x0d4b # C +OP_MarkNPC=0x695c # C +OP_MarkRaidNPC=0x0d4b # C OP_ClearNPCMarks=0x5033 # C +OP_ClearRaidNPCMarks=0x5f55 # C OP_DoGroupLeadershipAbility=0x540b # C OP_GroupLeadershipAAUpdate=0x0c33 OP_DelegateAbility=0x0322 # C @@ -484,6 +486,8 @@ OP_GroupDisbandOther=0x162d OP_GroupLeaderChange=0x7545 OP_GroupRoles=0x6b67 OP_GroupMakeLeader=0x6087 +OP_GroupMentor=0x1224 +OP_InspectBuffs=0x66bf # LFG/LFP Opcodes OP_LFGCommand=0x3288 # C OP_LFGGetMatchesRequest=0x5613 # C @@ -659,4 +663,6 @@ OP_RAWOutOfSession=0x0000 # # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs -OP_InitialHPUpdate=0x0000 # \ No newline at end of file +OP_InitialHPUpdate=0x0000 # + +OP_ItemRecastDelay=0x15c4 diff --git a/utils/patches/patch_SoF.conf b/utils/patches/patch_SoF.conf index 819096ed4..fc0c716e1 100644 --- a/utils/patches/patch_SoF.conf +++ b/utils/patches/patch_SoF.conf @@ -164,7 +164,7 @@ OP_GMLastName=0x0375 #/lastname OP_InspectAnswer=0x084F #SEQ 12/04/08 OP_Action2=0x0EF2 #SEQ 12/04/08 OP_BeginCast=0x5A50 #SEQ 12/04/08 -OP_BuffFadeMsg=0x3BC7 #SEQ 12/04/08 +OP_ColoredText=0x3BC7 #SEQ 12/04/08 OP_ConsentResponse=0x4D30 #SEQ 12/04/08 OP_MemorizeSpell=0x6A93 #SEQ 12/04/08 OP_SwapSpell=0x1418 #SEQ 12/04/08 @@ -281,8 +281,10 @@ OP_LeadershipExpToggle=0x24D4 #Xinu 02/20/09 OP_LeadershipExpUpdate=0x58b6 #Derision 2009 OP_PurchaseLeadershipAA=0x1408 #Derision 2009 OP_UpdateLeadershipAA=0x7abf #Derision 2009 -OP_MarkNPC=0x00c6 #Derision 2009 +OP_MarkNPC=0x4697 #Derision 2009 +OP_MarkRaidNPC=0x00c6 OP_ClearNPCMarks=0x2ff2 # +OP_ClearRaidNPCMarks=0x56a9 # OP_DoGroupLeadershipAbility=0x5a64 #Derision 2009 OP_DelegateAbility=0x57e3 #Derision 2009 OP_SetGroupTarget=0x1651 #Derision 2009 @@ -450,6 +452,8 @@ OP_GroupDelete=0x0000 # OP_CancelInvite=0x596C #Trevius 03/02/09 OP_GroupFollow2=0x59D4 #Xinu 02/20/09 OP_GroupInvite2=0x07F6 #Xinu 02/20/09 +OP_GroupMentor=0x9EF3 +OP_InspectBuffs=0x3547 #LFG/LFP Opcodes OP_LFGCommand=0x5D81 #Trevius 01/16/09 @@ -615,6 +619,8 @@ OP_RAWOutOfSession=0x0000 # OP_Some3ByteHPUpdate=0x0000 #initial HP update for mobs OP_InitialHPUpdate=0x0000 # +OP_ItemRecastDelay=0x0ada + # Opcodes from the client that are currently Unknowns: # 0x3E85 - Sent when Guild Management window is opened diff --git a/utils/patches/patch_Titanium.conf b/utils/patches/patch_Titanium.conf index 93f24e8db..841f77bf5 100644 --- a/utils/patches/patch_Titanium.conf +++ b/utils/patches/patch_Titanium.conf @@ -154,7 +154,7 @@ OP_InspectRequest=0x775d # ShowEQ 10/27/05 OP_InspectAnswer=0x2403 # ShowEQ 10/27/05 OP_Action2=0x0000 OP_BeginCast=0x3990 # ShowEQ 10/27/05 -OP_BuffFadeMsg=0x0b2d # ShowEQ 10/27/05 +OP_ColoredText=0x0b2d # ShowEQ 10/27/05 OP_Consent=0x1081 # ShowEQ 10/27/05 OP_ConsentDeny=0x4e8c # ShowEQ 10/27/05 OP_ConsentResponse=0x6380 # ShowEQ 10/27/05 @@ -420,6 +420,8 @@ OP_RaidJoin=0x1f21 # ShowEQ 10/27/05 OP_RaidInvite=0x5891 # ShowEQ 10/27/05 OP_RaidUpdate=0x1f21 # EQEmu 06/29/05 +OP_InspectBuffs=0x4FB6 + OP_ZoneComplete=0x0000 OP_ItemLinkText=0x0000 diff --git a/utils/patches/patch_Underfoot.conf b/utils/patches/patch_Underfoot.conf index cce73ce45..56838da65 100644 --- a/utils/patches/patch_Underfoot.conf +++ b/utils/patches/patch_Underfoot.conf @@ -168,7 +168,7 @@ OP_GMLastName=0x7bfb # C OP_InspectAnswer=0x0c2b # C OP_BeginCast=0x0d5a # C -OP_BuffFadeMsg=0x71bf # C +OP_ColoredText=0x71bf # C OP_ConsentResponse=0x0e87 # C OP_MemorizeSpell=0x3887 # C OP_SwapSpell=0x5805 # C @@ -292,8 +292,10 @@ OP_LeadershipExpToggle=0x5033 # C OP_LeadershipExpUpdate=0x074f # C OP_PurchaseLeadershipAA=0x5f55 # C OP_UpdateLeadershipAA=0x77ed # C -OP_MarkNPC=0x3ec7 # C +OP_MarkNPC=0x66bf +OP_MarkRaidNPC=0x3ec7 # C OP_ClearNPCMarks=0x5c29 # C +OP_ClearRaidNPCMarks=0x2af4 OP_DoGroupLeadershipAbility=0x0068 # C OP_GroupLeadershipAAUpdate=0x167b # C OP_DelegateAbility=0x6e58 # C @@ -487,6 +489,8 @@ OP_GroupDisbandOther=0x49f6 # C OP_GroupLeaderChange=0x0c33 # C OP_GroupRoles=0x116d # C OP_GroupMakeLeader=0x5851 +OP_GroupMentor=0x292f +OP_InspectBuffs=0x105b # LFG/LFP Opcodes OP_LFGCommand=0x2c38 # C @@ -654,3 +658,8 @@ OP_RAWOutOfSession=0x0000 # # we need to document the differences between these packets to make identifying them easier OP_Some3ByteHPUpdate=0x0000 # initial HP update for mobs OP_InitialHPUpdate=0x0000 # + +OP_ItemRecastDelay=0x82d7 + +# unhandled +OP_ShieldGroup=0x23a1 diff --git a/utils/sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql b/utils/sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql new file mode 100644 index 000000000..eb53c9ade --- /dev/null +++ b/utils/sql/git/bots/deprecated/2014_10_16_Lower_Case_View_Fix.sql @@ -0,0 +1,74 @@ +-- A fix for the auto-conversion view renaming issue + + +DROP VIEW IF EXISTS `vwbotcharactermobs`; +DROP VIEW IF EXISTS `vwbotgroups`; +DROP VIEW IF EXISTS `vwgroups`; +DROP VIEW IF EXISTS `vwguildmembers`; + + +CREATE VIEW `vwBotCharacterMobs` AS +SELECT _utf8'C' AS mobtype, +c.`id`, +c.`name`, +c.`class`, +c.`level`, +c.`last_login`, +c.`zone_id` +FROM `character_data` AS c +UNION ALL +SELECT _utf8'B' AS mobtype, +b.`BotID` AS id, +b.`Name` AS name, +b.`Class` AS class, +b.`BotLevel` AS level, +0 AS timelaston, +0 AS zoneid +FROM bots AS b; + +CREATE VIEW `vwGroups` AS +SELECT g.`groupid` AS groupid, +GetMobType(g.`name`) AS mobtype, +g.`name` AS name, +g.`charid` AS mobid, +IFNULL(c.`level`, b.`BotLevel`) AS level +FROM `group_id` AS g +LEFT JOIN `character_data` AS c ON g.`name` = c.`name` +LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; + +CREATE VIEW `vwBotGroups` AS +SELECT g.`BotGroupId`, +g.`BotGroupName`, +g.`BotGroupLeaderBotId`, +b.`Name` AS BotGroupLeaderName, +b.`BotOwnerCharacterId`, +c.`name` AS BotOwnerCharacterName +FROM `botgroup` AS g +JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` +JOIN `character_data` AS c ON b.`BotOwnerCharacterID` = c.`id` +ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; + +CREATE VIEW `vwGuildMembers` AS +SELECT 'C' AS mobtype, +cm.`char_id`, +cm.`guild_id`, +cm.`rank`, +cm.`tribute_enable`, +cm.`total_tribute`, +cm.`last_tribute`, +cm.`banker`, +cm.`public_note`, +cm.`alt` +FROM `guild_members` AS cm +UNION ALL +SELECT 'B' AS mobtype, +bm.`char_id`, +bm.`guild_id`, +bm.`rank`, +bm.`tribute_enable`, +bm.`total_tribute`, +bm.`last_tribute`, +bm.`banker`, +bm.`public_note`, +bm.`alt` +FROM `botguildmembers` AS bm; diff --git a/utils/sql/git/bots/deprecated/load_bots_old.sql b/utils/sql/git/bots/deprecated/load_bots_old.sql new file mode 100644 index 000000000..44d1c1b90 --- /dev/null +++ b/utils/sql/git/bots/deprecated/load_bots_old.sql @@ -0,0 +1,280 @@ +-- 'load_bots_old' sql script file +-- current as of 10/15/2014 +-- +-- Use this file on databases where the player profile blob has not been converted +-- +-- Note: This file assumes a database free of bot remnants. If you have a prior +-- bot installation and wish to reload the default schema and entries, then +-- source 'drop_bots.sql' before sourcing this file. + + +ALTER TABLE `guild_members` DROP PRIMARY KEY; +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`, `ismerc`); + +UPDATE `spawn2` SET `enabled` = 1 WHERE `id` IN (59297,59298); + +-- old command kept for reference (`commands` now only has 2 columns - `command` and `access`) +-- INSERT INTO `commands` VALUES ('bot', '0', 'Type \"#bot help\" to the see the list of available commands for bots.'); +INSERT INTO `commands` VALUES ('bot', '0'); + +INSERT INTO `rule_values` VALUES + ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'), + ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'), + ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'), + ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'), + ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'), + ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'), + ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'), + ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); + +CREATE TABLE `bots` ( + `BotID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotOwnerCharacterID` INT(10) UNSIGNED NOT NULL, + `BotSpellsID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NOT NULL, + `LastName` VARCHAR(32) DEFAULT NULL, + `BotLevel` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0', + `Race` SMALLINT(5) NOT NULL DEFAULT '0', + `Class` TINYINT(2) NOT NULL DEFAULT '0', + `Gender` TINYINT(2) NOT NULL DEFAULT '0', + `Size` FLOAT NOT NULL DEFAULT '0', + `Face` INT(10) NOT NULL DEFAULT '1', + `LuclinHairStyle` INT(10) NOT NULL DEFAULT '1', + `LuclinHairColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor2` INT(10) NOT NULL DEFAULT '1', + `LuclinBeardColor` INT(10) NOT NULL DEFAULT '1', + `LuclinBeard` INT(10) NOT NULL DEFAULT '0', + `DrakkinHeritage` INT(10) NOT NULL DEFAULT '0', + `DrakkinTattoo` INT(10) NOT NULL DEFAULT '0', + `DrakkinDetails` INT(10) NOT NULL DEFAULT '0', + `HP` INTEGER NOT NULL DEFAULT '0', + `Mana` INTEGER NOT NULL DEFAULT '0', + `MR` SMALLINT(5) NOT NULL DEFAULT '0', + `CR` SMALLINT(5) NOT NULL DEFAULT '0', + `DR` SMALLINT(5) NOT NULL DEFAULT '0', + `FR` SMALLINT(5) NOT NULL DEFAULT '0', + `PR` SMALLINT(5) NOT NULL DEFAULT '0', + `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', + `AC` SMALLINT(5) NOT NULL DEFAULT '0', + `STR` MEDIUMINT(8) NOT NULL DEFAULT '75', + `STA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `DEX` MEDIUMINT(8) NOT NULL DEFAULT '75', + `AGI` MEDIUMINT(8) NOT NULL DEFAULT '75', + `_INT` MEDIUMINT(8) NOT NULL DEFAULT '80', + `WIS` MEDIUMINT(8) NOT NULL DEFAULT '75', + `CHA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `ATK` MEDIUMINT(9) NOT NULL DEFAULT '0', + `BotCreateDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `LastSpawnDate` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `TotalPlayTime` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `LastZoneId` SMALLINT(6) NOT NULL DEFAULT '0', + `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', + PRIMARY KEY (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botstances` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `StanceID` TINYINT UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_botstances_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); + +CREATE TABLE `bottimers` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TimerID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Value` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_bottimers_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); + +CREATE TABLE `botbuffs` ( + `BotBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DurationFormula` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TicsRemaining` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `PoisonCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `DiseaseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CurseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `HitCount` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MeleeRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MagicRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DeathSaveSuccessChance` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterAARank` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Persistent` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`BotBuffId`), + KEY `FK_botbuff_1` (`BotId`), + CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botinventory` ( + `BotInventoryID` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `SlotID` INTEGER SIGNED NOT NULL DEFAULT '0', + `ItemID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `charges` TINYINT(3) UNSIGNED DEFAULT 0, + `color` INTEGER UNSIGNED NOT NULL DEFAULT 0, + `augslot1` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot2` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot3` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot4` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot5` MEDIUMINT(7) UNSIGNED DEFAULT 0, + `instnodrop` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`BotInventoryID`), + KEY `FK_botinventory_1` (`BotID`), + CONSTRAINT `FK_botinventory_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botpets` ( + `BotPetsId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `PetId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NULL, + `Mana` INTEGER NOT NULL DEFAULT '0', + `HitPoints` INTEGER NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetsId`), + KEY `FK_botpets_1` (`BotId`), + CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), + CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botpetbuffs` ( + `BotPetBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Duration` INT(11) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetBuffId`), + KEY `FK_botpetbuffs_1` (`BotPetsId`), + CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botpetinventory` ( + `BotPetInventoryId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `ItemId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetInventoryId`), + KEY `FK_botpetinventory_1` (`BotPetsId`), + CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botgroup` ( + `BotGroupId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupLeaderBotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotGroupName` VARCHAR(64) NOT NULL, + PRIMARY KEY (`BotGroupId`), + KEY `FK_botgroup_1` (`BotGroupLeaderBotId`), + CONSTRAINT `FK_botgroup_1` FOREIGN KEY (`BotGroupLeaderBotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botgroupmembers` ( + `BotGroupMemberId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotGroupMemberId`), + KEY `FK_botgroupmembers_1` (`BotGroupId`), + CONSTRAINT `FK_botgroupmembers_1` FOREIGN KEY (`BotGroupId`) REFERENCES `botgroup` (`BotGroupId`), + KEY `FK_botgroupmembers_2` (`BotId`), + CONSTRAINT `FK_botgroupmembers_2` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botguildmembers` ( + `char_id` INT(11) NOT NULL DEFAULT '0', + `guild_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', + `rank` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `tribute_enable` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `total_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `last_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `banker` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `public_note` TEXT NULL, + `alt` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB; + +DELIMITER // + +CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1) +BEGIN + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (SELECT COUNT(*) FROM `character_` WHERE `name` = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (SELECT COUNT(*) FROM `bots` WHERE `Name` = mobname) > 0 THEN + SET Result = 'B'; + END IF; + +RETURN Result; +END// + +DELIMITER ; + +CREATE VIEW `vwBotCharacterMobs` AS +SELECT _utf8'C' AS mobtype, +c.`id`, +c.`name`, +c.`class`, +c.`level`, +c.`timelaston`, +c.`zoneid` +FROM `character_` AS c +UNION ALL +SELECT _utf8'B' AS mobtype, +b.`BotID` AS id, +b.`Name` AS name, +b.`Class` AS class, +b.`BotLevel` AS level, +0 AS timelaston, +0 AS zoneid +FROM bots AS b; + +CREATE VIEW `vwGroups` AS +SELECT g.`groupid` AS groupid, +GetMobType(g.`name`) AS mobtype, +g.`name` AS name, +g.`charid` AS mobid, +IFNULL(c.`level`, b.`BotLevel`) AS level +FROM `group_id` AS g +LEFT JOIN `character_` AS c ON g.`name` = c.`name` +LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; + +CREATE VIEW `vwBotGroups` AS +SELECT g.`BotGroupId`, +g.`BotGroupName`, +g.`BotGroupLeaderBotId`, +b.`Name` AS BotGroupLeaderName, +b.`BotOwnerCharacterId`, +c.`name` AS BotOwnerCharacterName +FROM `botgroup` AS g +JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` +JOIN `character_` AS c ON b.`BotOwnerCharacterID` = c.`id` +ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; + +CREATE VIEW `vwGuildMembers` AS +SELECT 'C' AS mobtype, +cm.`char_id`, +cm.`guild_id`, +cm.`rank`, +cm.`tribute_enable`, +cm.`total_tribute`, +cm.`last_tribute`, +cm.`banker`, +cm.`public_note`, +cm.`alt` +FROM `guild_members` AS cm +UNION ALL +SELECT 'B' AS mobtype, +bm.`char_id`, +bm.`guild_id`, +bm.`rank`, +bm.`tribute_enable`, +bm.`total_tribute`, +bm.`last_tribute`, +bm.`banker`, +bm.`public_note`, +bm.`alt` +FROM `botguildmembers` AS bm; diff --git a/utils/sql/git/bots/drop_bots.sql b/utils/sql/git/bots/drop_bots.sql index d02425d73..df8ca4fa8 100644 --- a/utils/sql/git/bots/drop_bots.sql +++ b/utils/sql/git/bots/drop_bots.sql @@ -1,21 +1,41 @@ -DROP TABLE IF EXISTS `botbuffs`; +-- 'drop_bots' sql script file +-- current as of 10/15/2014 +-- +-- Note: This file will revert all changes made by either 'load_bots' sql file. +-- There may still be remnants of bot activity in tables `guild_members` and +-- `group_id`. If these entries are causing issues, you may need to manually +-- remove them. +-- +-- If this script fails due to a 'SQL Error (1068): Multiple primary key defined' +-- error, run this query: ALTER TABLE `guild_members` DROP PRIMARY KEY; +-- and it should remove the key so this script will process in its entirety. + + +ALTER TABLE `guild_members` ADD PRIMARY KEY (`char_id`); +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY (`groupid`, `charid`, `ismerc`); + +UPDATE `spawn2` SET `enabled` = 0 WHERE `id` IN (59297,59298); + +DELETE FROM `commands` WHERE `command` = 'bot'; +DELETE FROM `rule_values` WHERE `rule_name` LIKE 'Bots%'; + +DROP VIEW IF EXISTS `vwBotCharacterMobs`; +DROP VIEW IF EXISTS `vwBotGroups`; +DROP VIEW IF EXISTS `vwGroups`; +DROP VIEW IF EXISTS `vwGuildMembers`; + +DROP FUNCTION IF EXISTS `GetMobType`; + +DROP TABLE IF EXISTS `botguildmembers`; +DROP TABLE IF EXISTS `botgroupmembers`; +-- this table is not a part of 'load_bots.sql' +DROP TABLE IF EXISTS `botgroups`; +DROP TABLE IF EXISTS `botgroup`; DROP TABLE IF EXISTS `botpetinventory`; DROP TABLE IF EXISTS `botpetbuffs`; DROP TABLE IF EXISTS `botpets`; -DROP TABLE IF EXISTS `botgroupmembers`; -DROP TABLE IF EXISTS `botgroup`; -DROP TABLE IF EXISTS `botgroups`; DROP TABLE IF EXISTS `botinventory`; -DROP TABLE IF EXISTS `botguildmembers`; -DROP TABLE IF EXISTS `botstances`; +DROP TABLE IF EXISTS `botbuffs`; DROP TABLE IF EXISTS `bottimers`; +DROP TABLE IF EXISTS `botstances`; DROP TABLE IF EXISTS `bots`; -DROP VIEW IF EXISTS `vwGuildMembers`; -DROP VIEW IF EXISTS `vwBotCharacterMobs`; -DROP VIEW IF EXISTS `vwBotGroups`; - -delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; - -delete from commands where command = 'bot'; - -update spawn2 set enabled = 0 where id in (59297,59298); \ No newline at end of file diff --git a/utils/sql/git/bots/load_bots.sql b/utils/sql/git/bots/load_bots.sql index d5a358d69..9957b882a 100644 --- a/utils/sql/git/bots/load_bots.sql +++ b/utils/sql/git/bots/load_bots.sql @@ -1,307 +1,280 @@ --- This is pretty much a straight copy of the original files with fixes applied. --- HeidiSQL does not like sub-directory references, so this should now run from there. --- The 'headers' are left in place for reference only. +-- 'load_bots' sql script file +-- current as of 10/15/2014 +-- +-- Use this file on databases where the player profile blob has been converted. +-- +-- Note: This file assumes a database free of bot remnants. If you have a prior +-- bot installation and wish to reload the default schema and entries, then +-- source 'drop_bots.sql' before sourcing this file. --- FILE: --- source player_tables/botguildmembers.sql; -CREATE TABLE IF NOT EXISTS `botguildmembers` ( - `char_id` int(11) NOT NULL default '0', - `guild_id` mediumint(8) unsigned NOT NULL default '0', - `rank` tinyint(3) unsigned NOT NULL default '0', - `tribute_enable` tinyint(3) unsigned NOT NULL default '0', - `total_tribute` int(10) unsigned NOT NULL default '0', - `last_tribute` int(10) unsigned NOT NULL default '0', - `banker` tinyint(3) unsigned NOT NULL default '0', - `public_note` text NULL, - `alt` tinyint(3) unsigned NOT NULL default '0', - PRIMARY KEY (`char_id`) -) ENGINE=InnoDB; --- FILE: --- source player_tables/bots.sql; -update spawn2 set enabled = 1 where id in (59297,59298); +ALTER TABLE `guild_members` DROP PRIMARY KEY; +ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`, `ismerc`); -INSERT INTO rule_values VALUES ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'); -INSERT INTO rule_values VALUES ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'); -INSERT INTO rule_values VALUES ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'); -INSERT INTO rule_values VALUES ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'); +UPDATE `spawn2` SET `enabled` = 1 WHERE `id` IN (59297,59298); --- this is a hack fix to maintain the original file process -delete from rule_values where rule_name like 'Bots%' and ruleset_id = 1; -INSERT INTO rule_values VALUES ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'); - --- field count has changed +-- old command kept for reference (`commands` now only has 2 columns - `command` and `access`) -- INSERT INTO `commands` VALUES ('bot', '0', 'Type \"#bot help\" to the see the list of available commands for bots.'); INSERT INTO `commands` VALUES ('bot', '0'); -CREATE TABLE bots ( - `BotID` int(10) unsigned NOT NULL AUTO_INCREMENT, - `BotOwnerCharacterID` int(10) unsigned NOT NULL, - `BotSpellsID` int(10) unsigned NOT NULL DEFAULT '0', - `Name` varchar(64) NOT NULL, - `LastName` varchar(32) DEFAULT NULL, - `BotLevel` tinyint(2) unsigned NOT NULL DEFAULT '0', - `Race` smallint(5) NOT NULL DEFAULT '0', - `Class` tinyint(2) NOT NULL DEFAULT '0', - `Gender` tinyint(2) NOT NULL DEFAULT '0', - `Size` float NOT NULL DEFAULT '0', - `Face` int(10) NOT NULL DEFAULT '1', - `LuclinHairStyle` int(10) NOT NULL DEFAULT '1', - `LuclinHairColor` int(10) NOT NULL DEFAULT '1', - `LuclinEyeColor` int(10) NOT NULL DEFAULT '1', - `LuclinEyeColor2` int(10) NOT NULL DEFAULT '1', - `LuclinBeardColor` int(10) NOT NULL DEFAULT '1', - `LuclinBeard` int(10) NOT NULL DEFAULT '0', - `DrakkinHeritage` int(10) NOT NULL DEFAULT '0', - `DrakkinTattoo` int(10) NOT NULL DEFAULT '0', - `DrakkinDetails` int(10) NOT NULL DEFAULT '0', - `HP` INTEGER NOT NULL DEFAULT '0', - `Mana` INTEGER NOT NULL DEFAULT '0', - `MR` smallint(5) NOT NULL DEFAULT '0', - `CR` smallint(5) NOT NULL DEFAULT '0', - `DR` smallint(5) NOT NULL DEFAULT '0', - `FR` smallint(5) NOT NULL DEFAULT '0', - `PR` smallint(5) NOT NULL DEFAULT '0', - `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', - `AC` smallint(5) NOT NULL DEFAULT '0', - `STR` mediumint(8) NOT NULL DEFAULT '75', - `STA` mediumint(8) NOT NULL DEFAULT '75', - `DEX` mediumint(8) NOT NULL DEFAULT '75', - `AGI` mediumint(8) NOT NULL DEFAULT '75', - `_INT` mediumint(8) NOT NULL DEFAULT '80', - `WIS` mediumint(8) NOT NULL DEFAULT '75', - `CHA` mediumint(8) NOT NULL DEFAULT '75', - `ATK` mediumint(9) NOT NULL DEFAULT '0', - `BotCreateDate` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `LastSpawnDate` datetime NOT NULL DEFAULT '0000-00-00 00:00:00', - `TotalPlayTime` int(10) unsigned NOT NULL DEFAULT '0', - `LastZoneId` smallint(6) NOT NULL DEFAULT '0', - `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', - PRIMARY KEY (`BotID`) +INSERT INTO `rule_values` VALUES + ('1', 'Bots:BotAAExpansion', '8', 'The expansion through which bots will obtain AAs'), + ('1', 'Bots:BotFinishBuffing', 'false', 'Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat.'), + ('1', 'Bots:BotGroupBuffing', 'false', 'Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB.'), + ('1', 'Bots:BotManaRegen', '3.0', 'Adjust mana regen for bots, 1 is fast and higher numbers slow it down 3 is about the same as players.'), + ('1', 'Bots:BotQuest', 'false', 'Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl'), + ('1', 'Bots:BotSpellQuest', 'false', 'Anita Thrall\'s (Anita_Thrall.pl) Bot Spell Scriber quests.'), + ('1', 'Bots:CreateBotCount', '150', 'Number of bots that each account can create'), + ('1', 'Bots:SpawnBotCount', '71', 'Number of bots a character can have spawned at one time, You + 71 bots is a 12 group raid'); + +CREATE TABLE `bots` ( + `BotID` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotOwnerCharacterID` INT(10) UNSIGNED NOT NULL, + `BotSpellsID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NOT NULL, + `LastName` VARCHAR(32) DEFAULT NULL, + `BotLevel` TINYINT(2) UNSIGNED NOT NULL DEFAULT '0', + `Race` SMALLINT(5) NOT NULL DEFAULT '0', + `Class` TINYINT(2) NOT NULL DEFAULT '0', + `Gender` TINYINT(2) NOT NULL DEFAULT '0', + `Size` FLOAT NOT NULL DEFAULT '0', + `Face` INT(10) NOT NULL DEFAULT '1', + `LuclinHairStyle` INT(10) NOT NULL DEFAULT '1', + `LuclinHairColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor` INT(10) NOT NULL DEFAULT '1', + `LuclinEyeColor2` INT(10) NOT NULL DEFAULT '1', + `LuclinBeardColor` INT(10) NOT NULL DEFAULT '1', + `LuclinBeard` INT(10) NOT NULL DEFAULT '0', + `DrakkinHeritage` INT(10) NOT NULL DEFAULT '0', + `DrakkinTattoo` INT(10) NOT NULL DEFAULT '0', + `DrakkinDetails` INT(10) NOT NULL DEFAULT '0', + `HP` INTEGER NOT NULL DEFAULT '0', + `Mana` INTEGER NOT NULL DEFAULT '0', + `MR` SMALLINT(5) NOT NULL DEFAULT '0', + `CR` SMALLINT(5) NOT NULL DEFAULT '0', + `DR` SMALLINT(5) NOT NULL DEFAULT '0', + `FR` SMALLINT(5) NOT NULL DEFAULT '0', + `PR` SMALLINT(5) NOT NULL DEFAULT '0', + `Corrup` SMALLINT(5) NOT NULL DEFAULT '0', + `AC` SMALLINT(5) NOT NULL DEFAULT '0', + `STR` MEDIUMINT(8) NOT NULL DEFAULT '75', + `STA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `DEX` MEDIUMINT(8) NOT NULL DEFAULT '75', + `AGI` MEDIUMINT(8) NOT NULL DEFAULT '75', + `_INT` MEDIUMINT(8) NOT NULL DEFAULT '80', + `WIS` MEDIUMINT(8) NOT NULL DEFAULT '75', + `CHA` MEDIUMINT(8) NOT NULL DEFAULT '75', + `ATK` MEDIUMINT(9) NOT NULL DEFAULT '0', + `BotCreateDate` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `LastSpawnDate` DATETIME NOT NULL DEFAULT '0000-00-00 00:00:00', + `TotalPlayTime` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `LastZoneId` SMALLINT(6) NOT NULL DEFAULT '0', + `BotInspectMessage` VARCHAR(256) NOT NULL DEFAULT '', + PRIMARY KEY (`BotID`) ) ENGINE=InnoDB; -ALTER TABLE `group_id` DROP PRIMARY KEY, ADD PRIMARY KEY USING BTREE(`groupid`, `charid`, `name`); -ALTER TABLE `guild_members` DROP PRIMARY KEY; +CREATE TABLE `botstances` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `StanceID` TINYINT UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_botstances_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); -DROP VIEW IF EXISTS `vwGuildMembers`; -CREATE VIEW `vwGuildMembers` AS - select 'C' as mobtype, -cm.char_id, -cm.guild_id, -cm.rank, -cm.tribute_enable, -cm.total_tribute, -cm.last_tribute, -cm.banker, -cm.public_note, -cm.alt -from guild_members as cm -union all -select 'B' as mobtype, -bm.char_id, -bm.guild_id, -bm.rank, -bm.tribute_enable, -bm.total_tribute, -bm.last_tribute, -bm.banker, -bm.public_note, -bm.alt -from botguildmembers as bm; +CREATE TABLE `bottimers` ( + `BotID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TimerID` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Value` INT(10) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotID`), + CONSTRAINT `FK_bottimers_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) +); -DROP VIEW IF EXISTS `vwBotCharacterMobs`; -CREATE VIEW `vwBotCharacterMobs` AS - select 'C' as mobtype, -c.id, -c.name, -c.class, -c.level, -c.timelaston, -c.zoneid -from character_ as c -union all -select 'B' as mobtype, -b.BotID as id, -b.Name as name, -b.Class as class, -b.BotLevel as level, -0 as timelaston, -0 as zoneid -from bots as b; - --- FILE: --- source player_tables/botpetstatepersists.sql; -DROP TABLE IF EXISTS `botpetinventory`; -DROP TABLE IF EXISTS `botpetbuffs`; -DROP TABLE IF EXISTS `botpets`; - -CREATE TABLE IF NOT EXISTS `botpets` ( - `BotPetsId` integer unsigned NOT NULL AUTO_INCREMENT, - `PetId` integer unsigned NOT NULL DEFAULT '0', - `BotId` integer unsigned NOT NULL DEFAULT '0', - `Name` varchar(64) NULL, - `Mana` integer NOT NULL DEFAULT '0', - `HitPoints` integer NOT NULL DEFAULT '0', - PRIMARY KEY (`BotPetsId`), - KEY `FK_botpets_1` (`BotId`), - CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), - CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) +CREATE TABLE `botbuffs` ( + `BotBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DurationFormula` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `TicsRemaining` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `PoisonCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `DiseaseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CurseCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', + `HitCount` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MeleeRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `MagicRune` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `DeathSaveSuccessChance` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterAARank` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Persistent` TINYINT(1) NOT NULL DEFAULT '0', + PRIMARY KEY (`BotBuffId`), + KEY `FK_botbuff_1` (`BotId`), + CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; -CREATE TABLE IF NOT EXISTS `botpetbuffs` ( - `BotPetBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, - `BotPetsId` int(10) unsigned NOT NULL DEFAULT '0', - `SpellId` int(10) unsigned NOT NULL DEFAULT '0', - `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', - `Duration` int(11) unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`BotPetBuffId`), - KEY `FK_botpetbuffs_1` (`BotPetsId`), - CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; - -CREATE TABLE IF NOT EXISTS `botpetinventory` ( - `BotPetInventoryId` integer unsigned NOT NULL AUTO_INCREMENT, - `BotPetsId` integer unsigned NOT NULL DEFAULT '0', - `ItemId` integer unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`BotPetInventoryId`), - KEY `FK_botpetinventory_1` (`BotPetsId`), - CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) -) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; - --- FILE: --- source player_tables/botinventory.sql; -CREATE TABLE IF NOT EXISTS botinventory ( - BotInventoryID integer unsigned NOT NULL auto_increment, - BotID integer unsigned NOT NULL DEFAULT '0', - SlotID integer signed NOT NULL DEFAULT '0', - ItemID integer unsigned NOT NULL DEFAULT '0', - charges tinyint(3) unsigned DEFAULT 0, - color integer unsigned NOT NULL DEFAULT 0, - augslot1 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot2 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot3 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot4 mediumint(7) unsigned NOT NULL DEFAULT 0, - augslot5 mediumint(7) unsigned DEFAULT 0, - instnodrop tinyint(1) unsigned NOT NULL DEFAULT 0, - PRIMARY KEY (BotInventoryID), - KEY FK_botinventory_1 (BotID), - CONSTRAINT FK_botinventory_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) +CREATE TABLE `botinventory` ( + `BotInventoryID` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `SlotID` INTEGER SIGNED NOT NULL DEFAULT '0', + `ItemID` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `charges` TINYINT(3) UNSIGNED DEFAULT 0, + `color` INTEGER UNSIGNED NOT NULL DEFAULT 0, + `augslot1` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot2` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot3` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot4` MEDIUMINT(7) UNSIGNED NOT NULL DEFAULT 0, + `augslot5` MEDIUMINT(7) UNSIGNED DEFAULT 0, + `instnodrop` TINYINT(1) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY (`BotInventoryID`), + KEY `FK_botinventory_1` (`BotID`), + CONSTRAINT `FK_botinventory_1` FOREIGN KEY (`BotID`) REFERENCES `bots` (`BotID`) ) ENGINE=InnoDB; --- FILE: --- source player_tables/botbuffs.sql; -DROP TABLE IF EXISTS `botbuffs`; -CREATE TABLE `botbuffs` ( - `BotBuffId` int(10) unsigned NOT NULL AUTO_INCREMENT, - `BotId` int(10) unsigned NOT NULL DEFAULT '0', - `SpellId` int(10) unsigned NOT NULL DEFAULT '0', - `CasterLevel` int(10) unsigned NOT NULL DEFAULT '0', - `DurationFormula` int(10) unsigned NOT NULL DEFAULT '0', - `TicsRemaining` int(11) unsigned NOT NULL DEFAULT '0', - `PoisonCounters` int(11) unsigned NOT NULL DEFAULT '0', - `DiseaseCounters` int(11) unsigned NOT NULL DEFAULT '0', - `CurseCounters` int(11) unsigned NOT NULL DEFAULT '0', - `CorruptionCounters` INT(11) UNSIGNED NOT NULL DEFAULT '0', - `HitCount` int(10) unsigned NOT NULL DEFAULT '0', - `MeleeRune` int(10) unsigned NOT NULL DEFAULT '0', - `MagicRune` int(10) unsigned NOT NULL DEFAULT '0', - `DeathSaveSuccessChance` int(10) unsigned NOT NULL DEFAULT '0', - `CasterAARank` int(10) unsigned NOT NULL DEFAULT '0', - `Persistent` tinyint(1) NOT NULL DEFAULT '0', - PRIMARY KEY (`BotBuffId`), - KEY `FK_botbuff_1` (`BotId`), - CONSTRAINT `FK_botbuff_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +CREATE TABLE `botpets` ( + `BotPetsId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `PetId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `Name` VARCHAR(64) NULL, + `Mana` INTEGER NOT NULL DEFAULT '0', + `HitPoints` INTEGER NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetsId`), + KEY `FK_botpets_1` (`BotId`), + CONSTRAINT `FK_botpets_1` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`), + CONSTRAINT `U_botpets_1` UNIQUE (`BotId`) ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; --- FILE: --- source player_tables/botadventuring.sql; -DELIMITER $$ +CREATE TABLE `botpetbuffs` ( + `BotPetBuffId` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `SpellId` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `CasterLevel` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `Duration` INT(11) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetBuffId`), + KEY `FK_botpetbuffs_1` (`BotPetsId`), + CONSTRAINT `FK_botpetbuffs_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botpetinventory` ( + `BotPetInventoryId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotPetsId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `ItemId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotPetInventoryId`), + KEY `FK_botpetinventory_1` (`BotPetsId`), + CONSTRAINT `FK_botpetinventory_1` FOREIGN KEY (`BotPetsId`) REFERENCES `botpets` (`BotPetsID`) +) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1; + +CREATE TABLE `botgroup` ( + `BotGroupId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupLeaderBotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotGroupName` VARCHAR(64) NOT NULL, + PRIMARY KEY (`BotGroupId`), + KEY `FK_botgroup_1` (`BotGroupLeaderBotId`), + CONSTRAINT `FK_botgroup_1` FOREIGN KEY (`BotGroupLeaderBotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botgroupmembers` ( + `BotGroupMemberId` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, + `BotGroupId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + `BotId` INTEGER UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`BotGroupMemberId`), + KEY `FK_botgroupmembers_1` (`BotGroupId`), + CONSTRAINT `FK_botgroupmembers_1` FOREIGN KEY (`BotGroupId`) REFERENCES `botgroup` (`BotGroupId`), + KEY `FK_botgroupmembers_2` (`BotId`), + CONSTRAINT `FK_botgroupmembers_2` FOREIGN KEY (`BotId`) REFERENCES `bots` (`BotID`) +) ENGINE=InnoDB; + +CREATE TABLE `botguildmembers` ( + `char_id` INT(11) NOT NULL DEFAULT '0', + `guild_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', + `rank` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `tribute_enable` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `total_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `last_tribute` INT(10) UNSIGNED NOT NULL DEFAULT '0', + `banker` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `public_note` TEXT NULL, + `alt` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + PRIMARY KEY (`char_id`) +) ENGINE=InnoDB; + +DELIMITER \\ -DROP FUNCTION IF EXISTS `GetMobType` $$ CREATE FUNCTION `GetMobType` (mobname VARCHAR(64)) RETURNS CHAR(1) BEGIN - DECLARE Result CHAR(1); - - SET Result = NULL; - - IF (select count(*) from character_ where name = mobname) > 0 THEN - SET Result = 'C'; - ELSEIF (select count(*) from bots where Name = mobname) > 0 THEN - SET Result = 'B'; - END IF; - - RETURN Result; -END $$ + DECLARE Result CHAR(1); + + SET Result = NULL; + + IF (SELECT COUNT(*) FROM `character_data` WHERE `name` = mobname) > 0 THEN + SET Result = 'C'; + ELSEIF (SELECT COUNT(*) FROM `bots` WHERE `Name` = mobname) > 0 THEN + SET Result = 'B'; + END IF; + + RETURN Result; +END\\ DELIMITER ; -DROP VIEW IF EXISTS `vwGroups`; +CREATE VIEW `vwBotCharacterMobs` AS +SELECT _utf8'C' AS mobtype, +c.`id`, +c.`name`, +c.`class`, +c.`level`, +c.`last_login`, +c.`zone_id` +FROM `character_data` AS c +UNION ALL +SELECT _utf8'B' AS mobtype, +b.`BotID` AS id, +b.`Name` AS name, +b.`Class` AS class, +b.`BotLevel` AS level, +0 AS timelaston, +0 AS zoneid +FROM bots AS b; + CREATE VIEW `vwGroups` AS - select g.groupid as groupid, -GetMobType(g.name) as mobtype, -g.name as name, -g.charid as mobid, -ifnull(c.level, b.BotLevel) as level -from group_id as g -left join character_ as c on g.name = c.name -left join bots as b on g.name = b.Name; +SELECT g.`groupid` AS groupid, +GetMobType(g.`name`) AS mobtype, +g.`name` AS name, +g.`charid` AS mobid, +IFNULL(c.`level`, b.`BotLevel`) AS level +FROM `group_id` AS g +LEFT JOIN `character_data` AS c ON g.`name` = c.`name` +LEFT JOIN `bots` AS b ON g.`name` = b.`Name`; --- FILE: --- source player_tables/botgroups.sql; -DROP TABLE IF EXISTS `botgroupmembers`; -DROP TABLE IF EXISTS `botgroup`; - -CREATE TABLE IF NOT EXISTS `botgroup` ( - `BotGroupId` integer unsigned NOT NULL AUTO_INCREMENT, - `BotGroupLeaderBotId` integer unsigned NOT NULL DEFAULT '0', - `BotGroupName` varchar(64) NOT NULL, - PRIMARY KEY (`BotGroupId`), - KEY FK_botgroup_1 (BotGroupLeaderBotId), - CONSTRAINT FK_botgroup_1 FOREIGN KEY (BotGroupLeaderBotId) REFERENCES bots (BotID) -) ENGINE=InnoDB; - -CREATE TABLE IF NOT EXISTS `botgroupmembers` ( - `BotGroupMemberId` integer unsigned NOT NULL AUTO_INCREMENT, - `BotGroupId` integer unsigned NOT NULL DEFAULT '0', - `BotId` integer unsigned NOT NULL DEFAULT '0', - PRIMARY KEY (`BotGroupMemberId`), - KEY FK_botgroupmembers_1 (BotGroupId), - CONSTRAINT FK_botgroupmembers_1 FOREIGN KEY (BotGroupId) REFERENCES botgroup (BotGroupId), - KEY FK_botgroupmembers_2 (BotId), - CONSTRAINT FK_botgroupmembers_2 FOREIGN KEY (BotId) REFERENCES bots (BotID) -) ENGINE=InnoDB; - -DROP VIEW IF EXISTS `vwBotGroups`; CREATE VIEW `vwBotGroups` AS -select g.BotGroupId, -g.BotGroupName, -g.BotGroupLeaderBotId, -b.Name as BotGroupLeaderName, -b.BotOwnerCharacterId, -c.name as BotOwnerCharacterName -from botgroup as g -join bots as b on g.BotGroupLeaderBotId = b.BotID -join character_ as c on b.BotOwnerCharacterID = c.id -order by b.BotOwnerCharacterId, g.BotGroupName; +SELECT g.`BotGroupId`, +g.`BotGroupName`, +g.`BotGroupLeaderBotId`, +b.`Name` AS BotGroupLeaderName, +b.`BotOwnerCharacterId`, +c.`name` AS BotOwnerCharacterName +FROM `botgroup` AS g +JOIN `bots` AS b ON g.`BotGroupLeaderBotId` = b.`BotID` +JOIN `character_data` AS c ON b.`BotOwnerCharacterID` = c.`id` +ORDER BY b.`BotOwnerCharacterId`, g.`BotGroupName`; --- FILE: --- source player_tables/botstances.sql; -CREATE TABLE botstances ( - BotID int(10) unsigned NOT NULL default '0', - StanceID tinyint unsigned NOT NULL default '0', - PRIMARY KEY (BotID), - CONSTRAINT FK_botstances_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) -); - --- FILE: --- source player_tables/bottimers.sql; -CREATE TABLE bottimers ( -BotID int(10) unsigned NOT NULL default '0', -TimerID int(10) unsigned NOT NULL default '0', -Value int(10) unsigned NOT NULL default '0', -PRIMARY KEY (BotID), -CONSTRAINT FK_bottimers_1 FOREIGN KEY (BotID) REFERENCES bots (BotID) -) +CREATE VIEW `vwGuildMembers` AS +SELECT 'C' AS mobtype, +cm.`char_id`, +cm.`guild_id`, +cm.`rank`, +cm.`tribute_enable`, +cm.`total_tribute`, +cm.`last_tribute`, +cm.`banker`, +cm.`public_note`, +cm.`alt` +FROM `guild_members` AS cm +UNION ALL +SELECT 'B' AS mobtype, +bm.`char_id`, +bm.`guild_id`, +bm.`rank`, +bm.`tribute_enable`, +bm.`total_tribute`, +bm.`last_tribute`, +bm.`banker`, +bm.`public_note`, +bm.`alt` +FROM `botguildmembers` AS bm; diff --git a/utils/sql/git/optional/2014_09_18_TellQueueRule.sql b/utils/sql/git/optional/2014_09_18_TellQueueRule.sql new file mode 100644 index 000000000..2a9b43611 --- /dev/null +++ b/utils/sql/git/optional/2014_09_18_TellQueueRule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'World:TellQueueSize', '20', 'Maximum tell queue size.'); diff --git a/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql b/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql new file mode 100644 index 000000000..de6eb7d85 --- /dev/null +++ b/utils/sql/git/optional/2014_09_20_SHDProCIDOffByOne.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SHDProcIDOffByOne', 'true', 'SHD procs are off by 1. Set true for pre-UF spell files, false for UF+.'); diff --git a/utils/sql/git/optional/2014_09_28_NewHundredHandsEffect.sql b/utils/sql/git/optional/2014_09_28_NewHundredHandsEffect.sql new file mode 100644 index 000000000..2c5acb205 --- /dev/null +++ b/utils/sql/git/optional/2014_09_28_NewHundredHandsEffect.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:Jun182014HundredHandsRevamp', 'false', 'Set this to true if your spell file is from after June 18, 2014.'); diff --git a/utils/sql/git/optional/2014_10_02_AAFixes.sql b/utils/sql/git/optional/2014_10_02_AAFixes.sql new file mode 100644 index 000000000..1d6242b9f --- /dev/null +++ b/utils/sql/git/optional/2014_10_02_AAFixes.sql @@ -0,0 +1,13 @@ +-- Fix Salvage +UPDATE `aa_effects` SET `effectid` = '331' WHERE `aaid` IN (997, 998, 999); +-- Rapid Strikes missing entries +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('818', '1', '279', '17', '0'); +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('819', '1', '279', '19', '0'); +-- Secondary Forte fixes client side display issues +INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('691', '1', '248', '100', '0'); +-- Packrat this is what live uses +UPDATE `aa_effects` SET `base1` = '3' WHERE `aaid` = 678; +UPDATE `aa_effects` SET `base1` = '6' WHERE `aaid` = 679; +UPDATE `aa_effects` SET `base1` = '9' WHERE `aaid` = 680; +UPDATE `aa_effects` SET `base1` = '12' WHERE `aaid` = 681; +UPDATE `aa_effects` SET `base1` = '15' WHERE `aaid` = 682; diff --git a/utils/sql/git/optional/2014_10_08_LoggedOffReplenishments.sql b/utils/sql/git/optional/2014_10_08_LoggedOffReplenishments.sql new file mode 100644 index 000000000..cd553784c --- /dev/null +++ b/utils/sql/git/optional/2014_10_08_LoggedOffReplenishments.sql @@ -0,0 +1,2 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Zone:EnableLoggedOffReplenishments', 'true', 'Replenish mana/hp/end if logged off for MinOfflineTimeToReplenishments'); +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Zone:MinOfflineTimeToReplenishments', '21600', 'Minimum logged off time to trigger replenish mana/hp/end'); diff --git a/utils/sql/git/required/2014_09_18_tellqueuesclean.sql b/utils/sql/git/required/2014_09_18_tellqueuesclean.sql new file mode 100644 index 000000000..04d9b38d1 --- /dev/null +++ b/utils/sql/git/required/2014_09_18_tellqueuesclean.sql @@ -0,0 +1 @@ +DROP TABLE `tellque`; diff --git a/utils/sql/git/required/2014_09_20_ban_messages.sql b/utils/sql/git/required/2014_09_20_ban_messages.sql new file mode 100644 index 000000000..4612f9962 --- /dev/null +++ b/utils/sql/git/required/2014_09_20_ban_messages.sql @@ -0,0 +1 @@ +ALTER TABLE `account` ADD COLUMN `ban_reason` TEXT NULL DEFAULT NULL AFTER `expansion`, ADD COLUMN `suspend_reason` TEXT NULL DEFAULT NULL AFTER `ban_reason`; diff --git a/utils/sql/git/required/2014_10_11_RaidMOTD.sql b/utils/sql/git/required/2014_10_11_RaidMOTD.sql new file mode 100644 index 000000000..39fccd311 --- /dev/null +++ b/utils/sql/git/required/2014_10_11_RaidMOTD.sql @@ -0,0 +1 @@ +ALTER TABLE `raid_details` ADD `motd` varchar(1024); diff --git a/utils/sql/git/required/2014_10_13_RaidLeadership.sql b/utils/sql/git/required/2014_10_13_RaidLeadership.sql new file mode 100644 index 000000000..972e09ed5 --- /dev/null +++ b/utils/sql/git/required/2014_10_13_RaidLeadership.sql @@ -0,0 +1,9 @@ +CREATE TABLE `raid_leaders` ( + `gid` int(4) unsigned NOT NULL, + `rid` int(4) unsigned NOT NULL, + `marknpc` varchar(64) NOT NULL, + `maintank` varchar(64) NOT NULL, + `assist` varchar(64) NOT NULL, + `puller` varchar(64) NOT NULL, + `leadershipaa` tinyblob NOT NULL +); diff --git a/utils/sql/git/required/2014_10_18_group_mentor.sql b/utils/sql/git/required/2014_10_18_group_mentor.sql new file mode 100644 index 000000000..bd139f076 --- /dev/null +++ b/utils/sql/git/required/2014_10_18_group_mentor.sql @@ -0,0 +1,2 @@ +ALTER TABLE `group_leaders` ADD `mentoree` VARCHAR(64) NOT NULL; +ALTER TABLE `group_leaders` ADD `mentor_percent` INT(4) DEFAULT 0 NOT NULL; diff --git a/utils/sql/git/required/2014_10_19_raid_group_mentor.sql b/utils/sql/git/required/2014_10_19_raid_group_mentor.sql new file mode 100644 index 000000000..4cb8d5613 --- /dev/null +++ b/utils/sql/git/required/2014_10_19_raid_group_mentor.sql @@ -0,0 +1,2 @@ +ALTER TABLE `raid_leaders` ADD `mentoree` VARCHAR(64) NOT NULL; +ALTER TABLE `raid_leaders` ADD `mentor_percent` INT(4) DEFAULT 0 NOT NULL; diff --git a/world/adventure.cpp b/world/adventure.cpp index 45dd206e5..5f61aa5e9 100644 --- a/world/adventure.cpp +++ b/world/adventure.cpp @@ -358,6 +358,7 @@ void Adventure::Finished(AdventureWinStatus ws) afe.points = 0; } adventure_manager.AddFinishedEvent(afe); + database.UpdateAdventureStatsEntry(database.GetCharacterID((*iter).c_str()), GetTemplate()->theme, (ws != AWS_Lose) ? true : false); } ++iter; diff --git a/world/adventure_manager.cpp b/world/adventure_manager.cpp index 05337e0ad..aeee8e3c5 100644 --- a/world/adventure_manager.cpp +++ b/world/adventure_manager.cpp @@ -1069,7 +1069,7 @@ void AdventureManager::LoadLeaderboardInfo() leaderboard_info_percentage_tak.clear(); std::string query = "SELECT ch.name, ch.id, adv_stats.* FROM adventure_stats " - "AS adv_stats LEFT JOIN character_ AS ch ON adv_stats.player_id = ch.id;"; + "AS adv_stats LEFT JOIN `character_data` AS ch ON adv_stats.player_id = ch.id;"; auto results = database.QueryDatabase(query); if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::GetLeaderboardInfo: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); diff --git a/world/client.cpp b/world/client.cpp index f34f4e3df..f3f1a78c8 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -64,8 +64,6 @@ extern ClientList client_list; extern uint32 numclients; extern volatile bool RunLoops; - - Client::Client(EQStreamInterface* ieqs) : autobootup_timeout(RuleI(World, ZoneAutobootTimeoutMS)), CLE_keepalive_timer(RuleI(World, ClientKeepaliveTimeoutMS)), @@ -130,7 +128,7 @@ void Client::SendLogServer() void Client::SendEnterWorld(std::string name) { -char char_name[32]= { 0 }; + char char_name[64] = { 0 }; if (pZoning && database.GetLiveChar(GetAccountID(), char_name)) { if(database.GetAccountIDByChar(char_name) != GetAccountID()) { eqs->Close(); @@ -174,7 +172,7 @@ void Client::SendCharInfo() { EQApplicationPacket *outapp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct)); CharacterSelect_Struct* cs = (CharacterSelect_Struct*)outapp->pBuffer; - database.GetCharSelectInfo(GetAccountID(), cs); + database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); QueuePacket(outapp); safe_delete(outapp); @@ -471,8 +469,8 @@ bool Client::HandleSendLoginInfoPacket(const EQApplicationPacket *app) { return true; } -bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { - +bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) +{ if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Name approval request with no logged in account"); return false; @@ -482,7 +480,7 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { uchar race = app->pBuffer[64]; uchar clas = app->pBuffer[68]; - clog(WORLD__CLIENT,"Name approval request. Name=%s, race=%s, class=%s",char_name,GetRaceName(race),GetEQClassName(clas)); + clog(WORLD__CLIENT, "Name approval request. Name=%s, race=%s, class=%s", char_name, GetRaceName(race), GetEQClassName(clas)); EQApplicationPacket *outapp; outapp = new EQApplicationPacket; @@ -490,27 +488,27 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) { outapp->pBuffer = new uchar[1]; outapp->size = 1; - bool valid; - if(!database.CheckNameFilter(char_name)) { - valid = false; + bool valid = false; + if(!database.CheckNameFilter(char_name)) { + valid = false; } - else if(char_name[0] < 'A' && char_name[0] > 'Z') { - //name must begin with an upper-case letter. - valid = false; + /* Name must begin with an upper-case letter. */ + else if (islower(char_name[0])) { + valid = false; + } + else if (database.ReserveName(GetAccountID(), char_name)) { + valid = true; } - else if (database.ReserveName(GetAccountID(), char_name)) { - valid = true; - } - else { - valid = false; + else { + valid = false; } + outapp->pBuffer[0] = valid? 1 : 0; QueuePacket(outapp); safe_delete(outapp); - if(!valid) { + if (!valid) memset(char_name, 0, sizeof(char_name)); - } return true; } @@ -642,13 +640,11 @@ bool Client::HandleCharacterCreateRequestPacket(const EQApplicationPacket *app) } bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { - if (GetAccountID() == 0) - { + if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Account ID not set; unable to create character."); return false; } - else if (app->size != sizeof(CharCreate_Struct)) - { + else if (app->size != sizeof(CharCreate_Struct)) { clog(WORLD__CLIENT_ERR,"Wrong size on OP_CharacterCreate. Got: %d, Expected: %d",app->size,sizeof(CharCreate_Struct)); DumpPacket(app); // the previous behavior was essentially returning true here @@ -657,8 +653,7 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { } CharCreate_Struct *cc = (CharCreate_Struct*)app->pBuffer; - if(OPCharCreate(char_name, cc) == false) - { + if(OPCharCreate(char_name, cc) == false) { database.DeleteCharacter(char_name); EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApproveName, 1); outapp->pBuffer[0] = 0; @@ -675,8 +670,7 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) { return true; } -bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { - +bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if (GetAccountID() == 0) { clog(WORLD__CLIENT_ERR,"Enter world with no logged in account"); eqs->Close(); @@ -713,11 +707,10 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { return true; } - if(!pZoning && ew->return_home && !ew->tutorial) - { + if(!pZoning && ew->return_home && !ew->tutorial) { CharacterSelect_Struct* cs = new CharacterSelect_Struct; memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); + database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); bool home_enabled = false; for(int x = 0; x < 10; ++x) @@ -733,12 +726,10 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { } safe_delete(cs); - if(home_enabled) - { + if(home_enabled) { zoneID = database.MoveCharacterToBind(charid,4); } - else - { + else { clog(WORLD__CLIENT_ERR,"'%s' is trying to go home before they're able...",char_name); database.SetHackerFlag(GetAccountName(), char_name, "MQGoHome: player tried to go home before they were able."); eqs->Close(); @@ -749,7 +740,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { if(!pZoning && (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial))) { CharacterSelect_Struct* cs = new CharacterSelect_Struct; memset(cs, 0, sizeof(CharacterSelect_Struct)); - database.GetCharSelectInfo(GetAccountID(), cs); + database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit); bool tutorial_enabled = false; for(int x = 0; x < 10; ++x) @@ -807,16 +798,16 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { database.SetLoginFlags(charid, false, false, 1); } else{ - uint32 groupid=database.GetGroupID(char_name); - if(groupid>0){ - char* leader=0; - char leaderbuf[64]={0}; - if((leader=database.GetGroupLeaderForLogin(char_name,leaderbuf)) && strlen(leader)>1){ + uint32 groupid = database.GetGroupID(char_name); + if(groupid > 0){ + char* leader = 0; + char leaderbuf[64] = {0}; + if((leader = database.GetGroupLeaderForLogin(char_name, leaderbuf)) && strlen(leader)>1){ EQApplicationPacket* outapp3 = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); GroupJoin_Struct* gj=(GroupJoin_Struct*)outapp3->pBuffer; gj->action=8; - strcpy(gj->yourname,char_name); - strcpy(gj->membername,leader); + strcpy(gj->yourname, char_name); + strcpy(gj->membername, leader); QueuePacket(outapp3); safe_delete(outapp3); } @@ -895,8 +886,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) { bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) { uint32 char_acct_id = database.GetAccountIDByChar((char*)app->pBuffer); - if(char_acct_id == GetAccountID()) - { + if(char_acct_id == GetAccountID()) { clog(WORLD__CLIENT,"Delete character: %s",app->pBuffer); database.DeleteCharacter((char *)app->pBuffer); SendCharInfo(); @@ -1355,57 +1345,41 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) time_t bday = time(nullptr); char startzone[50]={0}; uint32 i; - struct in_addr in; + struct in_addr in; - - int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX + - cc->WIS + cc->INT + cc->CHA; + int stats_sum = cc->STR + cc->STA + cc->AGI + cc->DEX + cc->WIS + cc->INT + cc->CHA; in.s_addr = GetIP(); - clog(WORLD__CLIENT,"Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); - clog(WORLD__CLIENT,"Name: %s", name); - clog(WORLD__CLIENT,"Race: %d Class: %d Gender: %d Deity: %d Start zone: %d", - cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone); - clog(WORLD__CLIENT,"STR STA AGI DEX WIS INT CHA Total"); - clog(WORLD__CLIENT,"%3d %3d %3d %3d %3d %3d %3d %3d", + + clog(WORLD__CLIENT, "Character creation request from %s LS#%d (%s:%d) : ", GetCLE()->LSName(), GetCLE()->LSID(), inet_ntoa(in), GetPort()); + clog(WORLD__CLIENT, "Name: %s", name); + clog(WORLD__CLIENT, "Race: %d Class: %d Gender: %d Deity: %d Start zone: %d Tutorial: %s", + cc->race, cc->class_, cc->gender, cc->deity, cc->start_zone, cc->tutorial ? "true" : "false"); + clog(WORLD__CLIENT, "STR STA AGI DEX WIS INT CHA Total"); + clog(WORLD__CLIENT, "%3d %3d %3d %3d %3d %3d %3d %3d", cc->STR, cc->STA, cc->AGI, cc->DEX, cc->WIS, cc->INT, cc->CHA, stats_sum); - clog(WORLD__CLIENT,"Face: %d Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2); - clog(WORLD__CLIENT,"Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor); - clog(WORLD__CLIENT,"Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); + clog(WORLD__CLIENT, "Face: %d Eye colors: %d %d", cc->face, cc->eyecolor1, cc->eyecolor2); + clog(WORLD__CLIENT, "Hairstyle: %d Haircolor: %d", cc->hairstyle, cc->haircolor); + clog(WORLD__CLIENT, "Beard: %d Beardcolor: %d", cc->beard, cc->beardcolor); - // validate the char creation struct - if(ClientVersionBit & BIT_SoFAndLater) { - if(!CheckCharCreateInfoSoF(cc)) - { + /* Validate the char creation struct */ + if (ClientVersionBit & BIT_SoFAndLater) { + if (!CheckCharCreateInfoSoF(cc)) { clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } } else { - if(!CheckCharCreateInfoTitanium(cc)) - { + if (!CheckCharCreateInfoTitanium(cc)) { clog(WORLD__CLIENT_ERR,"CheckCharCreateInfo did not validate the request (bad race/class/stats)"); return false; } } - // Convert incoming cc_s to the new PlayerProfile_Struct + /* Convert incoming cc_s to the new PlayerProfile_Struct */ memset(&pp, 0, sizeof(PlayerProfile_Struct)); // start building the profile - InitExtendedProfile(&ext); - strn0cpy(pp.name, name, 63); - // clean the capitalization of the name -#if 0 // on second thought, don't - this will just make the creation fail -// because the name won't match what was already reserved earlier - for (i = 0; pp.name[i] && i < 63; i++) - { - if(!isalpha(pp.name[i])) - return false; - pp.name[i] = tolower(pp.name[i]); - } - pp.name[0] = toupper(pp.name[0]); -#endif pp.race = cc->race; pp.class_ = cc->class_; @@ -1433,150 +1407,128 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc) pp.level = 1; pp.points = 5; pp.cur_hp = 1000; // 1k hp during dev only - //what was the point of this? zone dosent handle this: - //pp.expAA = 0xFFFFFFFF; - pp.hunger_level = 6000; pp.thirst_level = 6000; - - // FIXME: FV roleplay, database goodness... - - // Racial Languages - SetRacialLanguages( &pp ); // bUsh - SetRaceStartingSkills( &pp ); // bUsh - SetClassStartingSkills( &pp ); // bUsh + /* Set Racial and Class specific language and skills */ + 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 // strcpy(pp.servername, WorldConfig::get()->ShortName.c_str()); - for(i = 0; i < MAX_PP_SPELLBOOK; i++) + for (i = 0; i < MAX_PP_REF_SPELLBOOK; i++) pp.spell_book[i] = 0xFFFFFFFF; - for(i = 0; i < MAX_PP_MEMSPELL; i++) + for(i = 0; i < MAX_PP_REF_MEMSPELL; i++) pp.mem_spells[i] = 0xFFFFFFFF; for(i = 0; i < BUFF_COUNT; i++) pp.buffs[i].spellid = 0xFFFF; - - //was memset(pp.unknown3704, 0xffffffff, 8); - //but I dont think thats what you really wanted to do... - //memset is byte based - - //If server is PVP by default, make all character set to it. + /* If server is PVP by default, make all character set to it. */ pp.pvp = database.GetServerType() == 1 ? 1 : 0; - //If it is an SoF Client and the SoF Start Zone rule is set, send new chars there - if((ClientVersionBit & BIT_SoFAndLater) && (RuleI(World, SoFStartZoneID) > 0)) { - clog(WORLD__CLIENT,"Found 'SoFStartZoneID' rule setting: %i", (RuleI(World, SoFStartZoneID))); - pp.zone_id = (RuleI(World, SoFStartZoneID)); - if(pp.zone_id) + /* If it is an SoF Client and the SoF Start Zone rule is set, send new chars there */ + if (ClientVersionBit & BIT_SoFAndLater && RuleI(World, SoFStartZoneID) > 0) { + clog(WORLD__CLIENT,"Found 'SoFStartZoneID' rule setting: %i", RuleI(World, SoFStartZoneID)); + pp.zone_id = RuleI(World, SoFStartZoneID); + if (pp.zone_id) database.GetSafePoints(pp.zone_id, 0, &pp.x, &pp.y, &pp.z); else - clog(WORLD__CLIENT_ERR,"Error getting zone id for Zone ID %i", (RuleI(World, SoFStartZoneID))); - } - else - { - // if there's a startzone variable put them in there - if(database.GetVariable("startzone", startzone, 50)) - { + clog(WORLD__CLIENT_ERR,"Error getting zone id for Zone ID %i", RuleI(World, SoFStartZoneID)); + } else { + /* if there's a startzone variable put them in there */ + if (database.GetVariable("startzone", startzone, 50)) { clog(WORLD__CLIENT,"Found 'startzone' variable setting: %s", startzone); pp.zone_id = database.GetZoneID(startzone); - if(pp.zone_id) + if (pp.zone_id) database.GetSafePoints(pp.zone_id, 0, &pp.x, &pp.y, &pp.z); else clog(WORLD__CLIENT_ERR,"Error getting zone id for '%s'", startzone); - } - else // otherwise use normal starting zone logic - { + } else { /* otherwise use normal starting zone logic */ bool ValidStartZone = false; - - if(ClientVersionBit & BIT_TitaniumAndEarlier) + if (ClientVersionBit & BIT_TitaniumAndEarlier) ValidStartZone = database.GetStartZone(&pp, cc); else ValidStartZone = database.GetStartZoneSoF(&pp, cc); - if(!ValidStartZone) + if (!ValidStartZone) return false; } } - if(!pp.zone_id) - { + /* just in case */ + if (!pp.zone_id) { pp.zone_id = 1; // qeynos pp.x = pp.y = pp.z = -1; } - if(!pp.binds[0].zoneId) - { - pp.binds[0].zoneId = pp.zone_id; - pp.binds[0].x = pp.x; - pp.binds[0].y = pp.y; - pp.binds[0].z = pp.z; - pp.binds[0].heading = pp.heading; + /* Set Home Binds */ + pp.binds[4].zoneId = pp.zone_id; + pp.binds[4].x = pp.x; + pp.binds[4].y = pp.y; + pp.binds[4].z = pp.z; + pp.binds[4].heading = pp.heading; + + /* Overrides if we have the tutorial flag set! */ + if (cc->tutorial && RuleB(World, EnableTutorialButton)) { + pp.zone_id = RuleI(World, TutorialZoneID); + database.GetSafePoints(pp.zone_id, 0, &pp.x, &pp.y, &pp.z); } - // set starting city location to the initial bind point - pp.binds[4] = pp.binds[0]; + /* Will either be the same as home or tutorial */ + pp.binds[0].zoneId = pp.zone_id; + pp.binds[0].x = pp.x; + pp.binds[0].y = pp.y; + pp.binds[0].z = pp.z; + pp.binds[0].heading = pp.heading; + clog(WORLD__CLIENT,"Current location: %s (%d) %0.2f, %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.zone_id), pp.zone_id, pp.x, pp.y, pp.z, pp.heading); + clog(WORLD__CLIENT,"Bind location: %s (%d) %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].zoneId, pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); + clog(WORLD__CLIENT,"Home location: %s (%d) %0.2f, %0.2f, %0.2f", + database.GetZoneName(pp.binds[4].zoneId), pp.binds[4].zoneId, pp.binds[4].x, pp.binds[4].y, pp.binds[4].z); - clog(WORLD__CLIENT,"Current location: %s %0.2f, %0.2f, %0.2f, %0.2f", - database.GetZoneName(pp.zone_id), pp.x, pp.y, pp.z, pp.heading); - clog(WORLD__CLIENT,"Bind location: %s %0.2f, %0.2f, %0.2f", - database.GetZoneName(pp.binds[0].zoneId), pp.binds[0].x, pp.binds[0].y, pp.binds[0].z); - - - // Starting Items inventory + /* Starting Items inventory */ database.SetStartingItems(&pp, &inv, pp.race, pp.class_, pp.deity, pp.zone_id, pp.name, GetAdmin()); - // now we give the pp and the inv we made to StoreCharacter // to see if we can store it - if (!database.StoreCharacter(GetAccountID(), &pp, &inv, &ext)) - { + if (!database.StoreCharacter(GetAccountID(), &pp, &inv)) { clog(WORLD__CLIENT_ERR,"Character creation failed: %s", pp.name); return false; } - else - { - clog(WORLD__CLIENT,"Character creation successful: %s", pp.name); - return true; - } + clog(WORLD__CLIENT,"Character creation successful: %s", pp.name); + return true; } // returns true if the request is ok, false if there's an error bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) { - if(!cc) return false; + if (!cc) + return false; _log(WORLD__CLIENT, "Validating char creation info..."); RaceClassCombos class_combo; bool found = false; int combos = character_create_race_class_combos.size(); - for(int i = 0; i < combos; ++i) { - if(character_create_race_class_combos[i].Class == cc->class_ && - character_create_race_class_combos[i].Race == cc->race && - character_create_race_class_combos[i].Deity == cc->deity) { - if(RuleB(World, EnableTutorialButton) && - (RuleI(World, TutorialZoneID) == cc->start_zone || - (character_create_race_class_combos[i].Zone == cc->start_zone))) { - class_combo = character_create_race_class_combos[i]; - found = true; - break; - } else if(character_create_race_class_combos[i].Zone == cc->start_zone) { - class_combo = character_create_race_class_combos[i]; - found = true; - break; - } + for (int i = 0; i < combos; ++i) { + if (character_create_race_class_combos[i].Class == cc->class_ && + character_create_race_class_combos[i].Race == cc->race && + character_create_race_class_combos[i].Deity == cc->deity && + character_create_race_class_combos[i].Zone == cc->start_zone) { + class_combo = character_create_race_class_combos[i]; + found = true; + break; } } - if(!found) { + if (!found) { _log(WORLD__CLIENT_ERR, "Could not find class/race/deity/start_zone combination"); return false; } @@ -1585,15 +1537,15 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) uint32 allocs = character_create_allocations.size(); RaceClassAllocation allocation = {0}; found = false; - for(int i = 0; i < combos; ++i) { - if(character_create_allocations[i].Index == class_combo.AllocationIndex) { + for (int i = 0; i < allocs; ++i) { + if (character_create_allocations[i].Index == class_combo.AllocationIndex) { allocation = character_create_allocations[i]; found = true; break; } } - if(!found) { + if (!found) { _log(WORLD__CLIENT_ERR, "Could not find starting stats for selected character combo, cannot verify stats"); return false; } @@ -1606,37 +1558,37 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) allocation.DefaultPointAllocation[5] + allocation.DefaultPointAllocation[6]; - if(cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) { + if (cc->STR > allocation.BaseStats[0] + max_stats || cc->STR < allocation.BaseStats[0]) { _log(WORLD__CLIENT_ERR, "Strength out of range"); return false; } - if(cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) { + if (cc->DEX > allocation.BaseStats[1] + max_stats || cc->DEX < allocation.BaseStats[1]) { _log(WORLD__CLIENT_ERR, "Dexterity out of range"); return false; } - if(cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) { + if (cc->AGI > allocation.BaseStats[2] + max_stats || cc->AGI < allocation.BaseStats[2]) { _log(WORLD__CLIENT_ERR, "Agility out of range"); return false; } - if(cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) { + if (cc->STA > allocation.BaseStats[3] + max_stats || cc->STA < allocation.BaseStats[3]) { _log(WORLD__CLIENT_ERR, "Stamina out of range"); return false; } - if(cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) { + if (cc->INT > allocation.BaseStats[4] + max_stats || cc->INT < allocation.BaseStats[4]) { _log(WORLD__CLIENT_ERR, "Intelligence out of range"); return false; } - if(cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) { + if (cc->WIS > allocation.BaseStats[5] + max_stats || cc->WIS < allocation.BaseStats[5]) { _log(WORLD__CLIENT_ERR, "Wisdom out of range"); return false; } - if(cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) { + if (cc->CHA > allocation.BaseStats[6] + max_stats || cc->CHA < allocation.BaseStats[6]) { _log(WORLD__CLIENT_ERR, "Charisma out of range"); return false; } @@ -1649,7 +1601,7 @@ bool CheckCharCreateInfoSoF(CharCreate_Struct *cc) current_stats += cc->INT - allocation.BaseStats[4]; current_stats += cc->WIS - allocation.BaseStats[5]; current_stats += cc->CHA - allocation.BaseStats[6]; - if(current_stats > max_stats) { + if (current_stats > max_stats) { _log(WORLD__CLIENT_ERR, "Current Stats > Maximum Stats"); return false; } @@ -1728,7 +1680,8 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) { /*Berserker*/ false, true, false, false, false, false, false, true, true, true, false, false, false, true, false, false} };//Initial table by kathgar, editted by Wiz for accuracy, solar too - if(!cc) return false; + if (!cc) + return false; _log(WORLD__CLIENT,"Validating char creation info..."); @@ -1742,19 +1695,16 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) // if out of range looking it up in the table would crash stuff // so we return from these - if(classtemp >= PLAYER_CLASS_COUNT) - { + if (classtemp >= PLAYER_CLASS_COUNT) { _log(WORLD__CLIENT_ERR," class is out of range"); return false; } - if(racetemp >= _TABLE_RACES) - { + if (racetemp >= _TABLE_RACES) { _log(WORLD__CLIENT_ERR," race is out of range"); return false; } - if(!ClassRaceLookupTable[classtemp][racetemp]) //Lookup table better than a bunch of ifs? - { + if (!ClassRaceLookupTable[classtemp][racetemp]) { //Lookup table better than a bunch of ifs? _log(WORLD__CLIENT_ERR," invalid race/class combination"); // we return from this one, since if it's an invalid combination our table // doesn't have meaningful values for the stats @@ -1782,44 +1732,36 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) // NOTE: these could just be else if, but i want to see all the stats // that are messed up not just the first hit - if(bTOTAL + stat_points != cTOTAL) - { + if (bTOTAL + stat_points != cTOTAL) { _log(WORLD__CLIENT_ERR," stat points total doesn't match expected value: expecting %d got %d", bTOTAL + stat_points, cTOTAL); Charerrors++; } - if(cc->STR > bSTR + stat_points || cc->STR < bSTR) - { + if (cc->STR > bSTR + stat_points || cc->STR < bSTR) { _log(WORLD__CLIENT_ERR," stat STR is out of range"); Charerrors++; } - if(cc->STA > bSTA + stat_points || cc->STA < bSTA) - { + if (cc->STA > bSTA + stat_points || cc->STA < bSTA) { _log(WORLD__CLIENT_ERR," stat STA is out of range"); Charerrors++; } - if(cc->AGI > bAGI + stat_points || cc->AGI < bAGI) - { + if (cc->AGI > bAGI + stat_points || cc->AGI < bAGI) { _log(WORLD__CLIENT_ERR," stat AGI is out of range"); Charerrors++; } - if(cc->DEX > bDEX + stat_points || cc->DEX < bDEX) - { + if (cc->DEX > bDEX + stat_points || cc->DEX < bDEX) { _log(WORLD__CLIENT_ERR," stat DEX is out of range"); Charerrors++; } - if(cc->WIS > bWIS + stat_points || cc->WIS < bWIS) - { + if (cc->WIS > bWIS + stat_points || cc->WIS < bWIS) { _log(WORLD__CLIENT_ERR," stat WIS is out of range"); Charerrors++; } - if(cc->INT > bINT + stat_points || cc->INT < bINT) - { + if (cc->INT > bINT + stat_points || cc->INT < bINT) { _log(WORLD__CLIENT_ERR," stat INT is out of range"); Charerrors++; } - if(cc->CHA > bCHA + stat_points || cc->CHA < bCHA) - { + if (cc->CHA > bCHA + stat_points || cc->CHA < bCHA) { _log(WORLD__CLIENT_ERR," stat CHA is out of range"); Charerrors++; } @@ -1832,28 +1774,15 @@ bool CheckCharCreateInfoTitanium(CharCreate_Struct *cc) return Charerrors == 0; } -void Client::SetClassStartingSkills( PlayerProfile_Struct *pp ) +void Client::SetClassStartingSkills(PlayerProfile_Struct *pp) { - for(uint32 i = 0; i <= HIGHEST_SKILL; ++i) { - if(pp->skills[i] == 0) { - if(i >= SkillSpecializeAbjure && i <= SkillSpecializeEvocation) { + for (uint32 i = 0; i <= HIGHEST_SKILL; ++i) { + if (pp->skills[i] == 0) { + // Skip specialized, tradeskills (fishing excluded), Alcohol Tolerance, and Bind Wound + if (EQEmu::IsSpecializedSkill((SkillUseTypes)i) || + (EQEmu::IsTradeskill((SkillUseTypes)i) && i != SkillFishing) || + i == SkillAlcoholTolerance || i == SkillBindWound) continue; - } - - if(i == SkillMakePoison || - i == SkillTinkering || - i == SkillResearch || - i == SkillAlchemy || - i == SkillBaking || - i == SkillTailoring || - i == SkillBlacksmithing || - i == SkillFletching || - i == SkillBrewing || - i == SkillPottery || - i == SkillJewelryMaking || - i == SkillBegging) { - continue; - } pp->skills[i] = database.GetSkillCap(pp->class_, (SkillUseTypes)i, 1); } diff --git a/world/cliententry.cpp b/world/cliententry.cpp index d4b85acc3..fa5b60c43 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -93,6 +93,7 @@ ClientListEntry::~ClientListEntry() { Camp(); // updates zoneserver's numplayers client_list.RemoveCLEReferances(this); } + tell_queue.clear(); } void ClientListEntry::SetChar(uint32 iCharID, const char* iCharName) { @@ -233,6 +234,7 @@ void ClientListEntry::ClearVars(bool iAll) { pLFG = 0; gm = 0; pClientVersion = 0; + tell_queue.clear(); } void ClientListEntry::Camp(ZoneServer* iZS) { @@ -295,3 +297,21 @@ bool ClientListEntry::CheckAuth(uint32 id, const char* iKey, uint32 ip) { return false; } +void ClientListEntry::ProcessTellQueue() +{ + if (!Server()) + return; + + ServerPacket *pack; + auto it = tell_queue.begin(); + while (it != tell_queue.end()) { + pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen((*it)->message) + 1); + memcpy(pack->pBuffer, *it, pack->size); + pack->Deflate(); + Server()->SendPacket(pack); + safe_delete(pack); + it = tell_queue.erase(it); + } + return; +} + diff --git a/world/cliententry.h b/world/cliententry.h index dcf79ca4f..cb096950c 100644 --- a/world/cliententry.h +++ b/world/cliententry.h @@ -5,6 +5,8 @@ #include "../common/md5.h" //#include "../common/eq_packet_structs.h" #include "../common/servertalk.h" +#include "../common/rulesys.h" +#include #define CLE_Status_Never -1 @@ -80,6 +82,11 @@ public: inline const char* GetLFGComments() const { return pLFGComments; } inline uint8 GetClientVersion() { return pClientVersion; } + inline bool TellQueueFull() const { return tell_queue.size() >= RuleI(World, TellQueueSize); } + inline bool TellQueueEmpty() const { return tell_queue.empty(); } + inline void PushToTellQueue(ServerChannelMessage_Struct *scm) { tell_queue.push_back(scm); } + void ProcessTellQueue(); + private: void ClearVars(bool iAll = false); @@ -120,6 +127,9 @@ private: uint8 pLFGToLevel; bool pLFGMatchFilter; char pLFGComments[64]; + + // Tell Queue -- really a vector :D + std::vector tell_queue; }; #endif /*CLIENTENTRY_H_*/ diff --git a/world/console.cpp b/world/console.cpp index d5433067e..6caa9ce86 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -113,7 +113,7 @@ bool Console::SendChannelMessage(const ServerChannelMessage_Struct* scm) { break; } case 7: { - SendMessage(1, "%s tells you, '%s'", scm->from, scm->message); + SendMessage(1, "[%s] tells you, '%s'", scm->from, scm->message); ServerPacket* pack = new ServerPacket(ServerOP_ChannelMessage, sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1); memcpy(pack->pBuffer, scm, pack->size); ServerChannelMessage_Struct* scm2 = (ServerChannelMessage_Struct*) pack->pBuffer; @@ -847,6 +847,9 @@ void Console::ProcessCommand(const char* command) { zoneserver_list.SendPacket(pack); safe_delete(pack); } + else if (strcasecmp(sep.arg[0], "") == 0){ + /* Hit Enter with no command */ + } else { SendMessage(1, "Command unknown."); } diff --git a/world/net.cpp b/world/net.cpp index 4bfdc3c1a..7973b5872 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -67,7 +67,6 @@ #endif -#include "../common/dbasync.h" #include "../common/emu_tcp_server.h" #include "../common/patches/patches.h" #include "zoneserver.h" @@ -98,7 +97,6 @@ UCSConnection UCSLink; QueryServConnection QSLink; LauncherList launcher_list; AdventureManager adventure_manager; -DBAsync *dbasync = nullptr; volatile bool RunLoops = true; uint32 numclients = 0; uint32 numzones = 0; @@ -175,7 +173,6 @@ int main(int argc, char** argv) { _log(WORLD__INIT_ERR, "Cannot continue without a database connection."); return 1; } - dbasync = new DBAsync(&database); guild_mgr.SetDatabase(&database); if (argc >= 2) { @@ -222,9 +219,8 @@ int main(int argc, char** argv) { else if (strcasecmp(argv[1], "flag") == 0) { if (argc == 4) { if (Seperator::IsNumber(argv[3])) { - if (atoi(argv[3]) >= 0 && atoi(argv[3]) <= 255) { - if (database.SetAccountStatus(argv[2], atoi(argv[3]))) { + if (database.SetAccountStatus(argv[2], atoi(argv[3]))){ std::cout << "Account flagged: Username='" << argv[2] << "', status=" << argv[3] << std::endl; return 0; } @@ -276,6 +272,8 @@ int main(int argc, char** argv) { _log(WORLD__INIT, "HTTP world service disabled."); } + _log(WORLD__INIT, "Checking Database Conversions.."); + database.CheckDatabaseConversions(); _log(WORLD__INIT, "Loading variables.."); database.LoadVariables(); _log(WORLD__INIT, "Loading zones.."); @@ -285,10 +283,13 @@ int main(int argc, char** argv) { _log(WORLD__INIT, "Clearing raids.."); database.ClearRaid(); database.ClearRaidDetails(); + database.ClearRaidLeader(); _log(WORLD__INIT, "Loading items.."); - if (!database.LoadItems()) { + if (!database.LoadItems()) _log(WORLD__INIT_ERR, "Error: Could not load item data. But ignoring"); - } + _log(WORLD__INIT, "Loading skill caps.."); + if (!database.LoadSkillCaps()) + _log(WORLD__INIT_ERR, "Error: Could not load skill cap data. But ignoring"); _log(WORLD__INIT, "Loading guilds.."); guild_mgr.LoadGuilds(); //rules: diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 7a07c6f6f..c82a728eb 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -21,7 +21,6 @@ #include "../common/string_util.h" #include "../common/eq_packet_structs.h" #include "../common/item.h" -#include "../common/dbasync.h" #include "../common/rulesys.h" #include #include @@ -34,295 +33,222 @@ extern std::vector character_create_race_class_combos; // solar: the current stuff is at the bottom of this function -void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; +void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs, uint32 ClientVersion) { Inventory *inv; + uint8 has_home = 0; + uint8 has_bind = 0; + /* Initialize Variables */ for (int i=0; i<10; i++) { strcpy(cs->name[i], ""); cs->zone[i] = 0; cs->level[i] = 0; - cs->tutorial[i] = 0; + cs->tutorial[i] = 0; cs->gohome[i] = 0; } - int char_num = 0; - unsigned long* lengths; + /* Get Character Info */ + std::string cquery = StringFormat( + "SELECT " + "`id`, " // 0 + "name, " // 1 + "gender, " // 2 + "race, " // 3 + "class, " // 4 + "`level`, " // 5 + "deity, " // 6 + "last_login, " // 7 + "time_played, " // 8 + "hair_color, " // 9 + "beard_color, " // 10 + "eye_color_1, " // 11 + "eye_color_2, " // 12 + "hair_style, " // 13 + "beard, " // 14 + "face, " // 15 + "drakkin_heritage, " // 16 + "drakkin_tattoo, " // 17 + "drakkin_details, " // 18 + "zone_id " // 19 + "FROM " + "character_data " + "WHERE `account_id` = %i ORDER BY `name` LIMIT 10 ", account_id); + auto results = database.QueryDatabase(cquery); int char_num = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + PlayerProfile_Struct pp; + memset(&pp, 0, sizeof(PlayerProfile_Struct)); - // Populate character info - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name,profile,zonename,class,level FROM character_ WHERE account_id=%i order by name limit 10", account_id), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - lengths = mysql_fetch_lengths(result); - //////////// - //////////// This is the current one, the other are for converting - //////////// - if ((lengths[1] == sizeof(PlayerProfile_Struct))) { - strcpy(cs->name[char_num], row[0]); - PlayerProfile_Struct* pp = (PlayerProfile_Struct*)row[1]; - uint8 clas = atoi(row[3]); - uint8 lvl = atoi(row[4]); + uint32 character_id = atoi(row[0]); + strcpy(cs->name[char_num], row[1]); + uint8 lvl = atoi(row[5]); + cs->level[char_num] = lvl; + cs->class_[char_num] = atoi(row[4]); + cs->race[char_num] = atoi(row[3]); + cs->gender[char_num] = atoi(row[2]); + cs->deity[char_num] = atoi(row[6]); + cs->zone[char_num] = atoi(row[19]); + cs->face[char_num] = atoi(row[15]); + cs->haircolor[char_num] = atoi(row[9]); + cs->beardcolor[char_num] = atoi(row[10]); + cs->eyecolor2[char_num] = atoi(row[12]); + cs->eyecolor1[char_num] = atoi(row[11]); + cs->hairstyle[char_num] = atoi(row[13]); + cs->beard[char_num] = atoi(row[14]); + cs->drakkin_heritage[char_num] = atoi(row[16]); + cs->drakkin_tattoo[char_num] = atoi(row[17]); + cs->drakkin_details[char_num] = atoi(row[18]); - // Character information - if(lvl == 0) - cs->level[char_num] = pp->level; //no level in DB, trust PP - else - cs->level[char_num] = lvl; - if(clas == 0) - cs->class_[char_num] = pp->class_; //no class in DB, trust PP - else - cs->class_[char_num] = clas; - cs->race[char_num] = pp->race; - cs->gender[char_num] = pp->gender; - cs->deity[char_num] = pp->deity; - cs->zone[char_num] = GetZoneID(row[2]); - cs->face[char_num] = pp->face; - cs->haircolor[char_num] = pp->haircolor; - cs->beardcolor[char_num] = pp->beardcolor; - cs->eyecolor2[char_num] = pp->eyecolor2; - cs->eyecolor1[char_num] = pp->eyecolor1; - cs->hairstyle[char_num] = pp->hairstyle; - cs->beard[char_num] = pp->beard; - cs->drakkin_heritage[char_num] = pp->drakkin_heritage; - cs->drakkin_tattoo[char_num] = pp->drakkin_tattoo; - cs->drakkin_details[char_num] = pp->drakkin_details; + if (RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) + cs->tutorial[char_num] = 1; - if(RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial))) - cs->tutorial[char_num] = 1; + if (RuleB(World, EnableReturnHomeButton)) { + int now = time(nullptr); + if ((now - atoi(row[7])) >= RuleI(World, MinOfflineTimeToReturnHome)) + cs->gohome[char_num] = 1; + } - if(RuleB(World, EnableReturnHomeButton)) { - int now = time(nullptr); - if((now - pp->lastlogin) >= RuleI(World, MinOfflineTimeToReturnHome)) - cs->gohome[char_num] = 1; + /* Set Bind Point Data for any character that may possibly be missing it for any reason */ + cquery = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %i LIMIT 2", character_id); + auto results_bind = database.QueryDatabase(cquery); has_home = 0; has_bind = 0; + for (auto row_b = results_bind.begin(); row_b != results_bind.end(); ++row_b) { + if (row_b[6] && atoi(row_b[6]) == 1){ has_home = 1; } + if (row_b[6] && atoi(row_b[6]) == 0){ has_bind = 1; } + } + + if (has_home == 0 || has_bind == 0){ + cquery = StringFormat("SELECT `zone_id`, `bind_id`, `x`, `y`, `z` FROM `start_zones` WHERE `player_class` = %i AND `player_deity` = %i AND `player_race` = %i", + cs->class_[char_num], cs->deity[char_num], cs->race[char_num]); + auto results_bind = database.QueryDatabase(cquery); + for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) { + /* If a bind_id is specified, make them start there */ + if (atoi(row_d[1]) != 0) { + pp.binds[4].zoneId = (uint32)atoi(row_d[1]); + GetSafePoints(pp.binds[4].zoneId, 0, &pp.binds[4].x, &pp.binds[4].y, &pp.binds[4].z); } + /* Otherwise, use the zone and coordinates given */ + else { + pp.binds[4].zoneId = (uint32)atoi(row_d[0]); + float x = atof(row_d[2]); + float y = atof(row_d[3]); + float z = atof(row_d[4]); + if (x == 0 && y == 0 && z == 0){ GetSafePoints(pp.binds[4].zoneId, 0, &x, &y, &z); } + pp.binds[4].x = x; pp.binds[4].y = y; pp.binds[4].z = z; - - // This part creates home city entries for characters created before the home bind point was tracked. - // Do it here because the player profile is already loaded and it's as good a spot as any. This whole block should - // probably be removed at some point, when most accounts are safely converted. - if(pp->binds[4].zoneId == 0) { - bool altered = false; - MYSQL_RES *result2; - MYSQL_ROW row2; - char startzone[50] = {0}; - - // check for start zone variable (I didn't even know any variables were still being used...) - if(database.GetVariable("startzone", startzone, 50)) { - uint32 zoneid = database.GetZoneID(startzone); - if(zoneid) { - pp->binds[4].zoneId = zoneid; - GetSafePoints(zoneid, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); - altered = true; - } - } - else { - RunQuery(query, - MakeAnyLenString(&query, - "SELECT zone_id,bind_id,x,y,z FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - pp->class_, - pp->deity, - pp->race - ), - errbuf, - &result2 - ); - safe_delete_array(query); - - // if there is only one possible start city, set it - if(mysql_num_rows(result2) == 1) { - row2 = mysql_fetch_row(result2); - if(atoi(row2[1]) != 0) { // if a bind_id is specified, make them start there - pp->binds[4].zoneId = (uint32)atoi(row2[1]); - GetSafePoints(pp->binds[4].zoneId, 0, &pp->binds[4].x, &pp->binds[4].y, &pp->binds[4].z); - } - else { // otherwise, use the zone and coordinates given - pp->binds[4].zoneId = (uint32)atoi(row2[0]); - float x = atof(row2[2]); - float y = atof(row2[3]); - float z = atof(row2[4]); - if(x == 0 && y == 0 && z == 0) - GetSafePoints(pp->binds[4].zoneId, 0, &x, &y, &z); - - pp->binds[4].x = x; - pp->binds[4].y = y; - pp->binds[4].z = z; - } - altered = true; - } - - mysql_free_result(result2); - } - - // update the player profile - if(altered) { - uint32 char_id = GetCharacterID(cs->name[char_num]); - RunQuery(query,MakeAnyLenString(&query,"SELECT extprofile FROM character_ WHERE id=%i",char_id), errbuf, &result2); - safe_delete_array(query); - if(result2) { - row2 = mysql_fetch_row(result2); - ExtendedProfile_Struct* ext = (ExtendedProfile_Struct*)row2[0]; - SetPlayerProfile(account_id,char_id,pp,inv,ext, 0, 0, 5); - } - mysql_free_result(result2); - } - } // end of "set start zone" block - - - // Character's equipped items - // @merth: Haven't done bracer01/bracer02 yet. - // Also: this needs a second look after items are a little more solid - // NOTE: items don't have a color, players MAY have a tint, if the - // use_tint part is set. otherwise use the regular color - inv = new Inventory; - if(GetInventory(account_id, cs->name[char_num], inv)) - { - for (uint8 material = 0; material <= 8; material++) - { - uint32 color; - ItemInst *item = inv->GetItem(Inventory::CalcSlotFromMaterial(material)); - if(item == 0) - continue; - - cs->equip[char_num][material] = item->GetItem()->Material; - - if(pp->item_tint[material].rgb.use_tint) // they have a tint (LoY dye) - color = pp->item_tint[material].color; - else // no tint, use regular item color - color = item->GetItem()->Color; - - cs->cs_colors[char_num][material].color = color; - - // the weapons are kept elsewhere - if ((material==MaterialPrimary) || (material==MaterialSecondary)) - { - if(strlen(item->GetItem()->IDFile) > 2) { - uint32 idfile=atoi(&item->GetItem()->IDFile[2]); - if (material==MaterialPrimary) - cs->primary[char_num]=idfile; - else - cs->secondary[char_num]=idfile; - } - } - } } - else - { - printf("Error loading inventory for %s\n", cs->name[char_num]); - } - safe_delete(inv); - if (++char_num > 10) - break; } - else - { - std::cout << "Got a bogus character (" << row[0] << ") Ignoring!!!" << std::endl; - std::cout << "PP length ="<name[char_num], inv)) { + for (uint8 material = 0; material <= 8; material++) { + uint32 color = 0; + ItemInst *item = inv->GetItem(Inventory::CalcSlotFromMaterial(material)); + if (item == 0) + continue; + + cs->equip[char_num][material] = item->GetItem()->Material; + + if (pp.item_tint[material].rgb.use_tint){ color = pp.item_tint[material].color; } + else{ color = item->GetItem()->Color; } + + cs->cs_colors[char_num][material].color = color; + + /* Weapons are handled a bit differently */ + if ((material == MaterialPrimary) || (material == MaterialSecondary)) { + if (strlen(item->GetItem()->IDFile) > 2) { + uint32 idfile = atoi(&item->GetItem()->IDFile[2]); + if (material == MaterialPrimary) + cs->primary[char_num] = idfile; + else + cs->secondary[char_num] = idfile; + } + } + } + } + else { + printf("Error loading inventory for %s\n", cs->name[char_num]); + } + safe_delete(inv); + if (++char_num > 10) + break; } return; } int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) { - // if an invalid bind point is specified, use the primary bind - if (bindnum > 4) - bindnum = 0; + /* if an invalid bind point is specified, use the primary bind */ + if (bindnum > 4){ bindnum = 0; } + int is_home = 0; + if (bindnum == 4){ is_home = 1; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 affected_rows = 0; - PlayerProfile_Struct pp; - - bool PPValid = false; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile from character_ where id='%i'", CharID), errbuf, &result)) { - row = mysql_fetch_row(result); - unsigned long* lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(PlayerProfile_Struct)) { - memcpy(&pp, row[0], sizeof(PlayerProfile_Struct)); - PPValid = true; - } - mysql_free_result(result); + std::string query = StringFormat("SELECT `zone_id` FROM `character_bind` WHERE `id` = %u AND `is_home` = %u LIMIT 1", CharID, is_home); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); } - safe_delete_array(query); - - if(!PPValid) return 0; - - const char *BindZoneName = StaticGetZoneName(pp.binds[bindnum].zoneId); - - if(!strcmp(BindZoneName, "UNKNWN")) return pp.zone_id; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET zonename = '%s',zoneid=%i,x=%f, y=%f, z=%f, instanceid=0 WHERE id='%i'", - BindZoneName, pp.binds[bindnum].zoneId, pp.binds[bindnum].x, pp.binds[bindnum].y, pp.binds[bindnum].z, - CharID), errbuf, 0,&affected_rows)) { - - return pp.zone_id; - } - safe_delete_array(query); - - return pp.binds[bindnum].zoneId; } bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row = 0; - int rows; - if(!in_pp || !in_cc) return false; in_pp->x = in_pp->y = in_pp->z = in_pp->heading = in_pp->zone_id = 0; in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "SELECT x,y,z,heading,zone_id,bind_id FROM start_zones WHERE player_choice=%i AND player_class=%i " - "AND player_deity=%i AND player_race=%i", - in_cc->start_zone, - in_cc->class_, - in_cc->deity, - in_cc->race), errbuf, &result)) - { - LogFile->write(EQEMuLog::Error, "Start zone query failed: %s : %s\n", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT x, y, z, heading, zone_id, bind_id " + "FROM start_zones WHERE player_choice = % i " + "AND player_class = %i AND player_deity = %i " + "AND player_race = %i", + in_cc->start_zone, in_cc->class_, in_cc->deity, + in_cc->race); + auto results = QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Start zone query failed: %s : %s\n", query.c_str(), results.ErrorMessage().c_str()); return false; } - LogFile->write(EQEMuLog::Status, "Start zone query: %s\n", query); - safe_delete_array(query); + LogFile->write(EQEMuLog::Status, "Start zone query: %s\n", query.c_str()); - if((rows = mysql_num_rows(result)) > 0) - row = mysql_fetch_row(result); - - if(row) - { - LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); - in_pp->x = atof(row[0]); - in_pp->y = atof(row[1]); - in_pp->z = atof(row[2]); - in_pp->heading = atof(row[3]); - in_pp->zone_id = atoi(row[4]); - in_pp->binds[0].zoneId = atoi(row[5]); - } - else - { - printf("No start_zones entry in database, using defaults\n"); + if (results.RowCount() == 0) { + printf("No start_zones entry in database, using defaults\n"); switch(in_cc->start_zone) { case 0: @@ -410,6 +336,16 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* break; } } + } + else { + LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); + auto row = results.begin(); + in_pp->x = atof(row[0]); + in_pp->y = atof(row[1]); + in_pp->z = atof(row[2]); + in_pp->heading = atof(row[3]); + in_pp->zone_id = atoi(row[4]); + in_pp->binds[0].zoneId = atoi(row[5]); } if(in_pp->x == 0 && in_pp->y == 0 && in_pp->z == 0) @@ -417,14 +353,12 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* if(in_pp->binds[0].x == 0 && in_pp->binds[0].y == 0 && in_pp->binds[0].z == 0) database.GetSafePoints(in_pp->binds[0].zoneId, 0, &in_pp->binds[0].x, &in_pp->binds[0].y, &in_pp->binds[0].z); - if(result) - mysql_free_result(result); + return true; } bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc) { - // SoF doesn't send the player_choice field in character creation, it now sends the real zoneID instead. // // For SoF, search for an entry in start_zones with a matching zone_id, class, race and deity. @@ -432,49 +366,25 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru // For now, if no row matching row is found, send them to Crescent Reach, as that is probably the most likely // reason for no match being found. // - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row = 0; - int rows; - if(!in_pp || !in_cc) return false; in_pp->x = in_pp->y = in_pp->z = in_pp->heading = in_pp->zone_id = 0; in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "SELECT x,y,z,heading,bind_id FROM start_zones WHERE zone_id=%i AND player_class=%i " - "AND player_deity=%i AND player_race=%i", - in_cc->start_zone, - in_cc->class_, - in_cc->deity, - in_cc->race), errbuf, &result)) - { - LogFile->write(EQEMuLog::Status, "SoF Start zone query failed: %s : %s\n", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT x, y, z, heading, bind_id FROM start_zones WHERE zone_id = %i " + "AND player_class = %i AND player_deity = %i AND player_race = %i", + in_cc->start_zone, in_cc->class_, in_cc->deity, in_cc->race); + auto results = QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Status, "SoF Start zone query failed: %s : %s\n", query.c_str(), results.ErrorMessage().c_str()); return false; } - LogFile->write(EQEMuLog::Status, "SoF Start zone query: %s\n", query); - safe_delete_array(query); + LogFile->write(EQEMuLog::Status, "SoF Start zone query: %s\n", query.c_str()); - if((rows = mysql_num_rows(result)) > 0) - row = mysql_fetch_row(result); - - if(row) - { - LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); - in_pp->x = atof(row[0]); - in_pp->y = atof(row[1]); - in_pp->z = atof(row[2]); - in_pp->heading = atof(row[3]); - in_pp->zone_id = in_cc->start_zone; - in_pp->binds[0].zoneId = atoi(row[4]); - } - else - { - printf("No start_zones entry in database, using defaults\n"); + if (results.RowCount() == 0) { + printf("No start_zones entry in database, using defaults\n"); if(in_cc->start_zone == RuleI(World, TutorialZoneID)) in_pp->zone_id = in_cc->start_zone; @@ -484,7 +394,16 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru in_pp->z = in_pp->binds[0].z = 0.79; in_pp->zone_id = in_pp->binds[0].zoneId = 394; // Crescent Reach. } - + } + else { + LogFile->write(EQEMuLog::Status, "Found starting location in start_zones"); + auto row = results.begin(); + in_pp->x = atof(row[0]); + in_pp->y = atof(row[1]); + in_pp->z = atof(row[2]); + in_pp->heading = atof(row[3]); + in_pp->zone_id = in_cc->start_zone; + in_pp->binds[0].zoneId = atoi(row[4]); } if(in_pp->x == 0 && in_pp->y == 0 && in_pp->z == 0) @@ -492,39 +411,27 @@ bool WorldDatabase::GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Stru if(in_pp->binds[0].x == 0 && in_pp->binds[0].y == 0 && in_pp->binds[0].z == 0) database.GetSafePoints(in_pp->binds[0].zoneId, 0, &in_pp->binds[0].x, &in_pp->binds[0].y, &in_pp->binds[0].z); - if(result) - mysql_free_result(result); + return true; } void WorldDatabase::GetLauncherList(std::vector &rl) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - rl.clear(); - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT name FROM launcher" ) - , errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) { - rl.push_back(row[0]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "WorldDatabase::GetLauncherList: %s", errbuf); - } - safe_delete_array(query); + const std::string query = "SELECT name FROM launcher"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "WorldDatabase::GetLauncherList: %s", results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + rl.push_back(row[0]); + } void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char MailKeyString[17]; if(RuleB(Chat, EnableMailKeyIPVerification) == true) @@ -532,75 +439,60 @@ void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) { else sprintf(MailKeyString, "%08X", MailKey); - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET mailkey = '%s' WHERE id='%i'", - MailKeyString, CharID), errbuf)) - - LogFile->write(EQEMuLog::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, errbuf); - - safe_delete_array(query); + std::string query = StringFormat("UPDATE character_data SET mailkey = '%s' WHERE id = '%i'", + MailKeyString, CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "WorldDatabase::SetMailKey(%i, %s) : %s", CharID, MailKeyString, results.ErrorMessage().c_str()); } bool WorldDatabase::GetCharacterLevel(const char *name, int &level) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT level FROM character_data WHERE name = '%s'", name); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "WorldDatabase::GetCharacterLevel: %s", results.ErrorMessage().c_str()); + return false; + } - if(RunQuery(query, MakeAnyLenString(&query, "SELECT level FROM character_ WHERE name='%s'", name), errbuf, &result)) - { - if(row = mysql_fetch_row(result)) - { - level = atoi(row[0]); - mysql_free_result(result); - safe_delete_array(query); - return true; - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "WorldDatabase::GetCharacterLevel: %s", errbuf); - } - safe_delete_array(query); - return false; + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + level = atoi(row[0]); + + return true; } bool WorldDatabase::LoadCharacterCreateAllocations() { character_create_allocations.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, "SELECT * FROM char_create_point_allocations order by id"), errbuf, &result)) { - safe_delete_array(query); - while(row = mysql_fetch_row(result)) { - RaceClassAllocation allocate; - int r = 0; - allocate.Index = atoi(row[r++]); - allocate.BaseStats[0] = atoi(row[r++]); - allocate.BaseStats[3] = atoi(row[r++]); - allocate.BaseStats[1] = atoi(row[r++]); - allocate.BaseStats[2] = atoi(row[r++]); - allocate.BaseStats[4] = atoi(row[r++]); - allocate.BaseStats[5] = atoi(row[r++]); - allocate.BaseStats[6] = atoi(row[r++]); - allocate.DefaultPointAllocation[0] = atoi(row[r++]); - allocate.DefaultPointAllocation[3] = atoi(row[r++]); - allocate.DefaultPointAllocation[1] = atoi(row[r++]); - allocate.DefaultPointAllocation[2] = atoi(row[r++]); - allocate.DefaultPointAllocation[4] = atoi(row[r++]); - allocate.DefaultPointAllocation[5] = atoi(row[r++]); - allocate.DefaultPointAllocation[6] = atoi(row[r++]); - character_create_allocations.push_back(allocate); - } - mysql_free_result(result); - } else { - safe_delete_array(query); - return false; - } + std::string query = "SELECT * FROM char_create_point_allocations ORDER BY id"; + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + for (auto row = results.begin(); row != results.end(); ++row) { + RaceClassAllocation allocate; + allocate.Index = atoi(row[0]); + allocate.BaseStats[0] = atoi(row[1]); + allocate.BaseStats[3] = atoi(row[2]); + allocate.BaseStats[1] = atoi(row[3]); + allocate.BaseStats[2] = atoi(row[4]); + allocate.BaseStats[4] = atoi(row[5]); + allocate.BaseStats[5] = atoi(row[6]); + allocate.BaseStats[6] = atoi(row[7]); + allocate.DefaultPointAllocation[0] = atoi(row[8]); + allocate.DefaultPointAllocation[3] = atoi(row[9]); + allocate.DefaultPointAllocation[1] = atoi(row[10]); + allocate.DefaultPointAllocation[2] = atoi(row[11]); + allocate.DefaultPointAllocation[4] = atoi(row[12]); + allocate.DefaultPointAllocation[5] = atoi(row[13]); + allocate.DefaultPointAllocation[6] = atoi(row[14]); + + character_create_allocations.push_back(allocate); + } return true; } @@ -608,27 +500,21 @@ bool WorldDatabase::LoadCharacterCreateAllocations() { bool WorldDatabase::LoadCharacterCreateCombos() { character_create_race_class_combos.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, "select * from char_create_combinations order by race, class, deity, start_zone"), errbuf, &result)) { - safe_delete_array(query); - while(row = mysql_fetch_row(result)) { - RaceClassCombos combo; - int r = 0; - combo.AllocationIndex = atoi(row[r++]); - combo.Race = atoi(row[r++]); - combo.Class = atoi(row[r++]); - combo.Deity = atoi(row[r++]); - combo.Zone = atoi(row[r++]); - combo.ExpansionRequired = atoi(row[r++]); - character_create_race_class_combos.push_back(combo); - } - mysql_free_result(result); - } else { - safe_delete_array(query); - return false; + std::string query = "SELECT * FROM char_create_combinations ORDER BY race, class, deity, start_zone"; + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + for (auto row = results.begin(); row != results.end(); ++row) { + RaceClassCombos combo; + combo.AllocationIndex = atoi(row[0]); + combo.Race = atoi(row[1]); + combo.Class = atoi(row[2]); + combo.Deity = atoi(row[3]); + combo.Zone = atoi(row[4]); + combo.ExpansionRequired = atoi(row[5]); + + character_create_race_class_combos.push_back(combo); } return true; diff --git a/world/worlddb.h b/world/worlddb.h index 24f43d308..ecb39ef61 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -31,7 +31,7 @@ public: bool GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc); bool GetStartZoneSoF(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc); - void GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*); + void GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*, uint32 ClientVersion); int MoveCharacterToBind(int CharID, uint8 bindnum = 0); void GetLauncherList(std::vector &result); diff --git a/world/zonelist.cpp b/world/zonelist.cpp index fdf9db5cf..c97010aa8 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -87,7 +87,7 @@ void ZSList::Process() { CatchSignal(2); } if(reminder && reminder->Check()){ - SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i seconds...",shutdowntimer->GetRemainingTime()/1000); + SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down, everyone log out now. World will shut down in %i minutes...", ((shutdowntimer->GetRemainingTime()/1000) / 60)); } LinkedListIterator iterator(list); @@ -718,7 +718,7 @@ void ZSList::GetZoneIDList(std::vector &zones) { void ZSList::WorldShutDown(uint32 time, uint32 interval) { if( time > 0 ) { - SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down in %i seconds, everyone log out before this time.",time); + SendEmoteMessage(0,0,0,15,":SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60)); time *= 1000; interval *= 1000; diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index 7c4f4f963..b94bda20a 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -409,6 +409,14 @@ bool ZoneServer::Process() { break; } + case ServerOP_RaidMOTD: { + if (pack->size < sizeof(ServerRaidMOTD_Struct)) + break; + + zoneserver_list.SendPacket(pack); + break; + } + case ServerOP_SpawnCondition: { if(pack->size != sizeof(ServerSpawnCondition_Struct)) break; @@ -437,45 +445,48 @@ bool ZoneServer::Process() { Console* con = 0; con = console_list.FindByAccountName(&scm->deliverto[1]); if (((!con) || (!con->SendChannelMessage(scm))) && (!scm->noreply)) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); + zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "%s is not online at this time.", scm->to); break; } ClientListEntry* cle = client_list.FindCharacter(scm->deliverto); - if (cle == 0 || cle->Online() < CLE_Status_Zoning || (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { - if (!scm->noreply) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - } - else if (cle->Online() == CLE_Status_Zoning) { - if (!scm->noreply) - { - time_t rawtime; - struct tm * timeinfo; - time ( &rawtime ); - timeinfo = localtime ( &rawtime ); - char *telldate=asctime(timeinfo); - - std::string query = StringFormat("SELECT name FROM character_ WHERE name = '%s'",scm->deliverto); - auto results = database.QueryDatabase(query); - if (!results.Success()) - break; - - if (results.RowCount() == 0) { - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - break; - } - - query = StringFormat("INSERT INTO tellque " - "(Date, Receiver, Sender, Message) " - "VALUES('%s', '%s', '%s', '%s')", - telldate, scm->deliverto, scm->from, scm->message); - results = database.QueryDatabase(query); - if (results.Success()) - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "Your message has been added to the %s's que.", scm->to); - else - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - + if (cle == 0 || cle->Online() < CLE_Status_Zoning || + (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { + if (!scm->noreply) { + ClientListEntry* sender = client_list.FindCharacter(scm->from); + if (!sender) + break; + scm->noreply = true; + scm->queued = 3; // offline + strcpy(scm->deliverto, scm->from); + // ideally this would be trimming off the message too, oh well + sender->Server()->SendPacket(pack); + } + } else if (cle->Online() == CLE_Status_Zoning) { + if (!scm->noreply) { + ClientListEntry* sender = client_list.FindCharacter(scm->from); + if (cle->TellQueueFull()) { + if (!sender) + break; + scm->noreply = true; + scm->queued = 2; // queue full + strcpy(scm->deliverto, scm->from); + sender->Server()->SendPacket(pack); + } else { + size_t struct_size = sizeof(ServerChannelMessage_Struct) + strlen(scm->message) + 1; + ServerChannelMessage_Struct *temp = (ServerChannelMessage_Struct *) new uchar[struct_size]; + memset(temp, 0, struct_size); // just in case, was seeing some corrupt messages, but it shouldn't happen + memcpy(temp, scm, struct_size); + temp->noreply = true; + cle->PushToTellQueue(temp); // deallocation is handled in processing or deconstructor + + if (!sender) + break; + scm->noreply = true; + scm->queued = 1; // queued + strcpy(scm->deliverto, scm->from); + sender->Server()->SendPacket(pack); + } } - // zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); } else if (cle->Server() == 0) { if (!scm->noreply) @@ -652,17 +663,17 @@ bool ZoneServer::Process() { client->Clearance(wtz->response); } case ServerOP_ZoneToZoneRequest: { - // - // solar: ZoneChange is received by the zone the player is in, then the - // zone sends a ZTZ which ends up here. This code then find the target - // (ingress point) and boots it if needed, then sends the ZTZ to it. - // The ingress server will decide wether the player can enter, then will - // send back the ZTZ to here. This packet is passed back to the egress - // server, which will send a ZoneChange response back to the client - // which can be an error, or a success, in which case the client will - // disconnect, and their zone location will be saved when ~Client is - // called, so it will be available when they ask to zone. - // + // + // solar: ZoneChange is received by the zone the player is in, then the + // zone sends a ZTZ which ends up here. This code then find the target + // (ingress point) and boots it if needed, then sends the ZTZ to it. + // The ingress server will decide wether the player can enter, then will + // send back the ZTZ to here. This packet is passed back to the egress + // server, which will send a ZoneChange response back to the client + // which can be an error, or a success, in which case the client will + // disconnect, and their zone location will be saved when ~Client is + // called, so it will be available when they ask to zone. + // if(pack->size != sizeof(ZoneToZone_Struct)) @@ -675,40 +686,31 @@ bool ZoneServer::Process() { zlog(WORLD__ZONE,"ZoneToZone request for %s current zone %d req zone %d\n", ztz->name, ztz->current_zone_id, ztz->requested_zone_id); - if(GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id) // this is a request from the egress zone - { + /* This is a request from the egress zone */ + if(GetZoneID() == ztz->current_zone_id && GetInstanceID() == ztz->current_instance_id) { zlog(WORLD__ZONE,"Processing ZTZ for egress from zone for client %s\n", ztz->name); - if - ( - ztz->admin < 80 && - ztz->ignorerestrictions < 2 && - zoneserver_list.IsZoneLocked(ztz->requested_zone_id) - ) - { + if (ztz->admin < 80 && ztz->ignorerestrictions < 2 && zoneserver_list.IsZoneLocked(ztz->requested_zone_id)) { ztz->response = 0; SendPacket(pack); break; } ZoneServer *ingress_server = nullptr; - if(ztz->requested_instance_id > 0) - { + if(ztz->requested_instance_id > 0) { ingress_server = zoneserver_list.FindByInstanceID(ztz->requested_instance_id); } - else - { - ingress_server = zoneserver_list.FindByZoneID(ztz->requested_zone_id); - + else { + ingress_server = zoneserver_list.FindByZoneID(ztz->requested_zone_id); } - if(ingress_server) // found a zone already running - { + /* Zone was already running*/ + if(ingress_server) { _log(WORLD__ZONE,"Found a zone already booted for %s\n", ztz->name); ztz->response = 1; } - else // need to boot one - { + /* Boot the Zone*/ + else { int server_id; if ((server_id = zoneserver_list.TriggerBootup(ztz->requested_zone_id, ztz->requested_instance_id))){ _log(WORLD__ZONE,"Successfully booted a zone for %s\n", ztz->name); @@ -716,8 +718,7 @@ bool ZoneServer::Process() { ztz->response = 1; ingress_server = zoneserver_list.FindByID(server_id); } - else - { + else { _log(WORLD__ZONE_ERR,"FAILED to boot a zone for %s\n", ztz->name); // bootup failed, send back error code 0 ztz->response = 0; @@ -725,27 +726,24 @@ bool ZoneServer::Process() { } if(ztz->response!=0 && client) client->LSZoneChange(ztz); - SendPacket(pack); // send back to egress server - if(ingress_server) // if we couldn't boot one, this is 0 - { - ingress_server->SendPacket(pack); // inform target server - } + SendPacket(pack); // send back to egress server + if(ingress_server) { + ingress_server->SendPacket(pack); // inform target server + } } - else // this is response from the ingress server, route it back to the egress server - { + /* Response from Ingress server, route back to egress */ + else{ + zlog(WORLD__ZONE,"Processing ZTZ for ingress to zone for client %s\n", ztz->name); ZoneServer *egress_server = nullptr; - if(ztz->current_instance_id > 0) - { + if(ztz->current_instance_id > 0) { egress_server = zoneserver_list.FindByInstanceID(ztz->current_instance_id); } - else - { + else { egress_server = zoneserver_list.FindByZoneID(ztz->current_zone_id); } - if(egress_server) - { + if(egress_server) { egress_server->SendPacket(pack); } } @@ -783,21 +781,18 @@ bool ZoneServer::Process() { delete whom; break; } - case ServerOP_RequestOnlineGuildMembers: - { + case ServerOP_RequestOnlineGuildMembers: { ServerRequestOnlineGuildMembers_Struct *srogms = (ServerRequestOnlineGuildMembers_Struct*) pack->pBuffer; zlog(GUILDS__IN_PACKETS, "ServerOP_RequestOnlineGuildMembers Recieved. FromID=%i GuildID=%i", srogms->FromID, srogms->GuildID); client_list.SendOnlineGuildMembers(srogms->FromID, srogms->GuildID); break; } - case ServerOP_ClientVersionSummary: - { + case ServerOP_ClientVersionSummary: { ServerRequestClientVersionSummary_Struct *srcvss = (ServerRequestClientVersionSummary_Struct*) pack->pBuffer; client_list.SendClientVersionSummary(srcvss->Name); break; } - case ServerOP_ReloadRules: - { + case ServerOP_ReloadRules: { zoneserver_list.SendPacket(pack); RuleManager::Instance()->LoadRules(&database, "default"); break; @@ -1272,37 +1267,19 @@ bool ZoneServer::Process() { case ServerOP_QueryServGeneric: case ServerOP_Speech: case ServerOP_QSPlayerLogTrades: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogHandins: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogNPCKills: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogDeletes: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogMoves: - { - QSLink.SendPacket(pack); - break; - } case ServerOP_QSPlayerLogMerchantTransactions: { QSLink.SendPacket(pack); break; } case ServerOP_CZSignalClientByName: - case ServerOP_CZMessagePlayer: + case ServerOP_CZMessagePlayer: + case ServerOP_CZSignalNPC: + case ServerOP_CZSetEntityVariableByNPCTypeID: case ServerOP_CZSignalClient: { zoneserver_list.SendPacket(pack); @@ -1319,6 +1296,16 @@ bool ZoneServer::Process() { zoneserver_list.SendPacket(pack); break; } + case ServerOP_RequestTellQueue: + { + ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer; + ClientListEntry *cle = client_list.FindCharacter(rtq->name); + if (!cle || cle->TellQueueEmpty()) + break; + + cle->ProcessTellQueue(); + break; + } default: { zlog(WORLD__ZONE_ERR,"Unknown ServerOPcode from zone 0x%04x, size %d",pack->opcode,pack->size); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 0b6ebc10a..8cd086405 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -119,7 +119,6 @@ SET(zone_sources zone_logsys.cpp zone_config.cpp zonedb.cpp - zonedbasync.cpp zoning.cpp ) @@ -207,7 +206,6 @@ SET(zone_headers zone.h zone_config.h zonedb.h - zonedbasync.h zonedump.h ) diff --git a/zone/aa.cpp b/zone/aa.cpp index 2964dc6f3..83a6b7967 100644 --- a/zone/aa.cpp +++ b/zone/aa.cpp @@ -18,6 +18,8 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) // Test 1 +#include + #include "../common/debug.h" #include "aa.h" #include "mob.h" @@ -300,7 +302,7 @@ void Client::ActivateAA(aaID activate){ return; } } else { - if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { + if (!CastSpell(caa->spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { //Reset on failed cast SendAATimer(AATimerID, 0, 0xFFFFFF); Message_StringID(15,ABILITY_FAILED); @@ -316,13 +318,14 @@ void Client::ActivateAA(aaID activate){ } } // Check if AA is expendable - if (aas_send[activate - activate_val]->special_category == 7) - { + if (aas_send[activate - activate_val]->special_category == 7) { + // Add the AA cost to the extended profile to track overall total m_epp.expended_aa += aas_send[activate]->cost; + SetAA(activate, 0); - Save(); + SaveAA(); /* Save Character AA */ SendAA(activate); SendAATable(); } @@ -525,7 +528,7 @@ void Client::HandleAAAction(aaID activate) { //cast the spell, if we have one if(IsValidSpell(spell_id)) { int aatid = GetAATimerID(activate); - if(!CastSpell(spell_id, target_id , 10, -1, -1, 0, -1, pTimerAAStart + aatid , CalcAAReuseTimer(caa), 1)) { + if (!CastSpell(spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, pTimerAAStart + aatid, CalcAAReuseTimer(caa), 1)) { SendAATimer(aatid, 0, 0xFFFFFF); Message_StringID(15,ABILITY_FAILED); p_timers.Clear(&database, pTimerAAStart + aatid); @@ -1035,8 +1038,7 @@ void Client::BuyAA(AA_Action* action) uint32 real_cost; std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(action->ability); - if(RequiredLevel != AARequiredLevelAndCost.end()) - { + if(RequiredLevel != AARequiredLevelAndCost.end()) { real_cost = RequiredLevel->second.Cost; } else @@ -1049,7 +1051,11 @@ void Client::BuyAA(AA_Action* action) m_pp.aapoints -= real_cost; - Save(); + /* Do Player Profile rank calculations and set player profile */ + SaveAA(); + /* Save to Database to avoid having to write the whole AA array to the profile, only write changes*/ + // database.SaveCharacterAA(this->CharacterID(), aa2->id, (cur_level + 1)); + if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u)) && ((aa2->max_level == (cur_level + 1)) && aa2->sof_next_id)){ SendAA(aa2->id); @@ -1060,8 +1066,10 @@ void Client::BuyAA(AA_Action* action) SendAATable(); - //we are building these messages ourself instead of using the stringID to work around patch discrepencies - //these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2 + /* + We are building these messages ourself instead of using the stringID to work around patch discrepencies + these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2 + */ /* Initial purchase of an AA ability */ if (cur_level < 1){ @@ -1084,8 +1092,6 @@ void Client::BuyAA(AA_Action* action) } } - - SendAAStats(); CalcBonuses(); @@ -1514,11 +1520,15 @@ bool ZoneDatabase::LoadAAEffects2() { return true; } void Client::ResetAA(){ + RefundAA(); uint32 i; for(i=0;iAA = 0; aa[i]->value = 0; + m_pp.aa_array[MAX_PP_AA_ARRAY].AA = 0; + m_pp.aa_array[MAX_PP_AA_ARRAY].value = 0; } + std::map::iterator itr; for(itr=aa_points.begin();itr!=aa_points.end();++itr) aa_points[itr->first] = 0; @@ -1530,10 +1540,51 @@ void Client::ResetAA(){ m_pp.raid_leadership_points = 0; m_pp.group_leadership_exp = 0; m_pp.raid_leadership_exp = 0; + + database.DeleteCharacterAAs(this->CharacterID()); + SaveAA(); + SendAATable(); + database.DeleteCharacterLeadershipAAs(this->CharacterID()); + Kick(); } int Client::GroupLeadershipAAHealthEnhancement() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAHealthEnhancement, group_id)) { + case 1: + bonus = 30; + break; + case 2: + bonus = 60; + break; + case 3: + bonus = 100; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAHealthEnhancement)) { + case 1: + bonus += 30; + break; + case 2: + bonus += 60; + break; + case 3: + bonus += 100; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) @@ -1556,6 +1607,41 @@ int Client::GroupLeadershipAAHealthEnhancement() int Client::GroupLeadershipAAManaEnhancement() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAManaEnhancement, group_id)) { + case 1: + bonus = 30; + break; + case 2: + bonus = 60; + break; + case 3: + bonus = 100; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAManaEnhancement)) { + case 1: + bonus += 30; + break; + case 2: + bonus += 60; + break; + case 3: + bonus += 100; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) @@ -1578,6 +1664,41 @@ int Client::GroupLeadershipAAManaEnhancement() int Client::GroupLeadershipAAHealthRegeneration() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAHealthRegeneration, group_id)) { + case 1: + bonus = 4; + break; + case 2: + bonus = 6; + break; + case 3: + bonus = 8; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAHealthRegeneration)) { + case 1: + bonus += 4; + break; + case 2: + bonus += 6; + break; + case 3: + bonus += 8; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) @@ -1600,6 +1721,53 @@ int Client::GroupLeadershipAAHealthRegeneration() int Client::GroupLeadershipAAOffenseEnhancement() { + if (IsRaidGrouped()) { + int bonus = 0; + Raid *raid = GetRaid(); + if (!raid) + return 0; + uint32 group_id = raid->GetGroup(this); + if (group_id < 12 && raid->GroupCount(group_id) >= 3) { + switch (raid->GetLeadershipAA(groupAAOffenseEnhancement, group_id)) { + case 1: + bonus = 10; + break; + case 2: + bonus = 19; + break; + case 3: + bonus = 28; + break; + case 4: + bonus = 34; + break; + case 5: + bonus = 40; + break; + } + } + if (raid->RaidCount() >= 18) { + switch (raid->GetLeadershipAA(raidAAOffenseEnhancement)) { + case 1: + bonus += 10; + break; + case 2: + bonus += 19; + break; + case 3: + bonus += 28; + break; + case 4: + bonus += 34; + break; + case 5: + bonus += 40; + break; + } + } + return bonus; + } + Group *g = GetGroup(); if(!g || (g->GroupCount() < 3)) @@ -1625,29 +1793,26 @@ int Client::GroupLeadershipAAOffenseEnhancement() void Client::InspectBuffs(Client* Inspector, int Rank) { - if(!Inspector || (Rank == 0)) return; + // At some point the removed the restriction of being a group member for this to work + // not sure when, but the way it's coded now, it wouldn't work with mobs. + if (!Inspector || Rank == 0) + return; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_InspectBuffs, sizeof(InspectBuffs_Struct)); + InspectBuffs_Struct *ib = (InspectBuffs_Struct *)outapp->pBuffer; - Inspector->Message_StringID(0, CURRENT_SPELL_EFFECTS, GetName()); uint32 buff_count = GetMaxTotalSlots(); - for (uint32 i = 0; i < buff_count; ++i) - { - if (buffs[i].spellid != SPELL_UNKNOWN) - { - if(Rank == 1) - Inspector->Message(0, "%s", spells[buffs[i].spellid].name); - else - { - if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) - Inspector->Message(0, "%s (Permanent)", spells[buffs[i].spellid].name); - else { - char *TempString = nullptr; - MakeAnyLenString(&TempString, "%.1f", static_cast(buffs[i].ticsremaining) / 10.0f); - Inspector->Message_StringID(0, BUFF_MINUTES_REMAINING, spells[buffs[i].spellid].name, TempString); - safe_delete_array(TempString); - } - } - } + uint32 packet_index = 0; + for (uint32 i = 0; i < buff_count; i++) { + if (buffs[i].spellid == SPELL_UNKNOWN) + continue; + ib->spell_id[packet_index] = buffs[i].spellid; + if (Rank > 1) + ib->tics_remaining[packet_index] = spells[buffs[i].spellid].buffdurationformula == DF_Permanent ? 0xFFFFFFFF : buffs[i].ticsremaining; + packet_index++; } + + Inspector->FastQueuePacket(&outapp); } //this really need to be renamed to LoadAAActions() @@ -1735,19 +1900,15 @@ void ZoneDatabase::FillAAEffects(SendAA_Struct* aa_struct){ if(!aa_struct) return; - std::string query = StringFormat("SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id); - auto results = QueryDatabase(query); - if (!results.Success()) { - LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - return; - } - - int index = 0; - for (auto row = results.begin(); row != results.end(); ++row, ++index) { - aa_struct->abilities[index].skill_id=atoi(row[0]); - aa_struct->abilities[index].base1=atoi(row[1]); - aa_struct->abilities[index].base2=atoi(row[2]); - aa_struct->abilities[index].slot=atoi(row[3]); + auto it = aa_effects.find(aa_struct->id); + if (it != aa_effects.end()) { + for (int slot = 0; slot < aa_struct->total_abilities; slot++) { + // aa_effects is a map of a map, so the slot reference does not start at 0 + aa_struct->abilities[slot].skill_id = it->second[slot + 1].skill_id; + aa_struct->abilities[slot].base1 = it->second[slot + 1].base1; + aa_struct->abilities[slot].base2 = it->second[slot + 1].base2; + aa_struct->abilities[slot].slot = it->second[slot + 1].slot; + } } } @@ -1818,8 +1979,7 @@ void ZoneDatabase::LoadAAs(SendAA_Struct **load){ } AALevelCost_Struct aalcs; - for (auto row = results.begin(); row != results.end(); ++row) - { + for (auto row = results.begin(); row != results.end(); ++row) { aalcs.Level = atoi(row[1]); aalcs.Cost = atoi(row[2]); AARequiredLevelAndCost[atoi(row[0])] = aalcs; diff --git a/zone/attack.cpp b/zone/attack.cpp index a515a08c4..0b8cf07f1 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3315,7 +3315,10 @@ int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTi if(damage < 1) return 0; - + + //Regular runes absorb spell damage (except dots) - Confirmed on live. + if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) + damage = RuneAbsorb(damage, SE_Rune); if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); @@ -3331,10 +3334,13 @@ int32 Mob::ReduceAllDamage(int32 damage) if(damage <= 0) return damage; - if(spellbonuses.ManaAbsorbPercentDamage[0] && (GetMana() > damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100)) { - damage -= (damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100); - SetMana(GetMana() - damage); - TryTriggerOnValueAmount(false, true); + if(spellbonuses.ManaAbsorbPercentDamage[0]) { + int32 mana_reduced = damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100; + if (GetMana() >= mana_reduced){ + damage -= mana_reduced; + SetMana(GetMana() - mana_reduced); + TryTriggerOnValueAmount(false, true); + } } CheckNumHitsRemaining(NUMHIT_IncomingDamage); @@ -4197,24 +4203,26 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack } #endif //BOTS - float critChance = 0.0f; bool IsBerskerSPA = false; //1: Try Slay Undead - if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire){ - + if (defender && (defender->GetBodyType() == BT_Undead || + defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) { int16 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0]; - if (SlayRateBonus) { - - critChance += (float(SlayRateBonus)/100.0f); - critChance /= 100.0f; - - if(MakeRandomFloat(0, 1) < critChance){ + float slayChance = static_cast(SlayRateBonus) / 10000.0f; + if (MakeRandomFloat(0, 1) < slayChance) { int16 SlayDmgBonus = aabonuses.SlayUndead[1] + itembonuses.SlayUndead[1] + spellbonuses.SlayUndead[1]; - damage = (damage*SlayDmgBonus*2.25)/100; - entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s cleanses %s target!(%d)", GetCleanName(), this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its", damage); + damage = (damage * SlayDmgBonus * 2.25) / 100; + if (GetGender() == 1) // female + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, FEMALE_SLAYUNDEAD, + GetCleanName(), itoa(damage)); + else // males and neuter I guess + entity_list.FilteredMessageClose_StringID(this, false, 200, + MT_CritMelee, FilterMeleeCrits, MALE_SLAYUNDEAD, + GetCleanName(), itoa(damage)); return; } } @@ -4804,6 +4812,26 @@ void Mob::CommonBreakInvisible() improved_hidden = false; } +/* Dev quotes: + * Old formula + * Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 100) * Original Delay) + * New formula + * Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 1000) * (Original Delay / (haste mod *.01f)) + * Base Delay 20 25 30 37 + * Haste 2.25 2.25 2.25 2.25 + * HHE (old) -17 -17 -17 -17 + * Final Delay 5.488888889 6.861111111 8.233333333 10.15444444 + * + * Base Delay 20 25 30 37 + * Haste 2.25 2.25 2.25 2.25 + * HHE (new) -383 -383 -383 -383 + * Final Delay 5.484444444 6.855555556 8.226666667 10.14622222 + * + * Difference -0.004444444 -0.005555556 -0.006666667 -0.008222222 + * + * These times are in 10th of a second + */ + void Mob::SetAttackTimer() { attack_timer.SetAtTrigger(4000, true); @@ -4811,7 +4839,7 @@ void Mob::SetAttackTimer() void Client::SetAttackTimer() { - float PermaHaste = GetPermaHaste(); + float haste_mod = GetHaste() * 0.01f; //default value for attack timer in case they have //an invalid weapon equipped: @@ -4876,28 +4904,30 @@ void Client::SetAttackTimer() } } - int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99); + int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; int speed = 0; + int delay = 36; + float quiver_haste = 0.0f; //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); + if (GetClass() == MONK || GetClass() == BEASTLORD) + delay = GetMonkHandToHandDelay(); } 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; - } + delay = ItemToUse->Delay; + if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) + quiver_haste = GetQuiverHaste(); } + if (RuleB(Spells, Jun182014HundredHandsRevamp)) + speed = static_cast(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100); + else + speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); + // this is probably wrong + if (quiver_haste > 0) + speed *= quiver_haste; TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); if (i == MainPrimary) @@ -4907,13 +4937,24 @@ void Client::SetAttackTimer() void NPC::SetAttackTimer() { - float PermaHaste = GetPermaHaste(); + float haste_mod = GetHaste() * 0.01f; //default value for attack timer in case they have //an invalid weapon equipped: attack_timer.SetAtTrigger(4000, true); Timer *TimerToUse = nullptr; + int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; + + // 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 = 0; + if (RuleB(Spells, Jun182014HundredHandsRevamp)) + speed = static_cast(((attack_delay / haste_mod) + ((hhe / 1000.0f) * (attack_delay / haste_mod))) * 100); + else + speed = static_cast(((attack_delay / haste_mod) + ((hhe / 100.0f) * attack_delay)) * 100); for (int i = MainRange; i <= MainSecondary; i++) { //pick a timer @@ -4935,13 +4976,6 @@ void NPC::SetAttackTimer() } } - 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/bonuses.cpp b/zone/bonuses.cpp index f1105ce73..89af2a244 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -104,6 +104,8 @@ void Client::CalcBonuses() CalcMaxMana(); CalcMaxEndurance(); + SetAttackTimer(); + rooted = FindType(SE_Root); XPRate = 100 + spellbonuses.XPRateMod; @@ -174,8 +176,6 @@ void Client::CalcItemBonuses(StatBonuses* newbon) { if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) newbon->EnduranceRegen = CalcEnduranceRegenCap(); - - SetAttackTimer(); } void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { @@ -1546,6 +1546,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_AttackSpeed4: { + // These don't generate the IMMUNE_ATKSPEED message and the icon shows up + // but have no effect on the mobs attack speed + if (GetSpecialAbility(UNSLOWABLE)) + break; + if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow) effect_value = effect_value * -1; @@ -2467,7 +2472,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne } break; } - + case SE_ManaAbsorbPercentDamage: { if (newbon->ManaAbsorbPercentDamage[0] < effect_value){ @@ -2488,7 +2493,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne case SE_ShieldBlock: newbon->ShieldBlock += effect_value; break; - + case SE_ShieldEquipHateMod: newbon->ShieldEquipHateMod += effect_value; break; @@ -2502,6 +2507,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne newbon->BlockBehind += effect_value; break; + case SE_Blind: + newbon->IsBlind = true; + break; + case SE_Fear: newbon->IsFeared = true; break; @@ -4081,6 +4090,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) itembonuses.BlockBehind = effect_value; break; + case SE_Blind: + spellbonuses.IsBlind = false; + break; + case SE_Fear: spellbonuses.IsFeared = false; break; diff --git a/zone/bot.cpp b/zone/bot.cpp index d001a0b88..2453a308a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1466,85 +1466,75 @@ void Bot::GenerateAABonuses(StatBonuses* newbon) { } void Bot::LoadAAs() { - std::string errorMessage; - char* Query = 0; - int length = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - int maxAAExpansion = RuleI(Bots, BotAAExpansion); //get expansion to get AAs up to botAAs.clear(); //start fresh + std::string query; + if(GetClass() == BERSERKER) - length = MakeAnyLenString(&Query, "SELECT skill_id FROM altadv_vars WHERE berserker = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetLevel(), maxAAExpansion); + query = StringFormat("SELECT skill_id FROM altadv_vars WHERE berserker = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetLevel(), maxAAExpansion); else - length = MakeAnyLenString(&Query, "SELECT skill_id FROM altadv_vars WHERE ((classes & ( 1 << %i )) >> %i) = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetClass(), GetClass(), GetLevel(), maxAAExpansion); + query = StringFormat("SELECT skill_id FROM altadv_vars WHERE ((classes & ( 1 << %i )) >> %i) = 1 AND class_type > 1 AND class_type <= %i AND aa_expansion <= %i ORDER BY skill_id;", GetClass(), GetClass(), GetLevel(), maxAAExpansion); - if(!database.RunQuery(Query, length, TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int totalAAs = database.CountAAs(); + auto results = database.QueryDatabase(query); - while(DataRow = mysql_fetch_row(DatasetResult)) { - uint32 skill_id = 0; - skill_id = atoi(DataRow[0]); - - if(skill_id > 0 && skill_id < totalAAs) { - SendAA_Struct *sendAA = zone->FindAA(skill_id); - - if(sendAA) { - for(int i=0; imax_level; i++) { - //Get AA info & add to list - uint32 aaid = sendAA->id + i; - uint8 total_levels = 0; - uint8 req_level; - std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(aaid); - - //Get level required for AA - if(RequiredLevel != AARequiredLevelAndCost.end()) - req_level = RequiredLevel->second.Level; - else - req_level = (sendAA->class_type + i * sendAA->level_inc); - - if(req_level > GetLevel()) - break; - - //Bot is high enough level for AA - std::map::iterator foundAA = botAAs.find(aaid); - - // AA is not already in list - if(foundAA == botAAs.end()) { - if(sendAA->id == aaid) { - BotAA newAA; - - newAA.total_levels = 0; - newAA.aa_id = aaid; - newAA.req_level = req_level; - newAA.total_levels += 1; - - botAAs[aaid] = newAA; //add to list - } - else { - //update master AA record with number of levels a bot has in AA, based on level. - botAAs[sendAA->id].total_levels+=1; - } - } - } - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadAAs()"); + return; } + + int totalAAs = database.CountAAs(); + + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 skill_id = 0; + skill_id = atoi(row[0]); + + if(skill_id <= 0 || skill_id >= totalAAs) + continue; + + SendAA_Struct *sendAA = zone->FindAA(skill_id); + + if(!sendAA) + continue; + + for(int i=0; imax_level; i++) { + //Get AA info & add to list + uint32 aaid = sendAA->id + i; + uint8 total_levels = 0; + uint8 req_level; + std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(aaid); + + //Get level required for AA + if(RequiredLevel != AARequiredLevelAndCost.end()) + req_level = RequiredLevel->second.Level; + else + req_level = (sendAA->class_type + i * sendAA->level_inc); + + if(req_level > GetLevel()) + break; + + //Bot is high enough level for AA + std::map::iterator foundAA = botAAs.find(aaid); + + // AA is already in list + if(foundAA != botAAs.end()) + continue; + + if(sendAA->id == aaid) { + BotAA newAA; + + newAA.total_levels = 0; + newAA.aa_id = aaid; + newAA.req_level = req_level; + newAA.total_levels += 1; + + botAAs[aaid] = newAA; //add to list + } + else //update master AA record with number of levels a bot has in AA, based on level. + botAAs[sendAA->id].total_levels+=1; + } + } + } uint32 Bot::GetAA(uint32 aa_id) { @@ -2349,85 +2339,105 @@ bool Bot::IsValidName() { } bool Bot::IsBotNameAvailable(std::string* errorMessage) { - bool Result = false; - if(this->GetCleanName()) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!this->GetCleanName()) + return false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(id) FROM vwBotCharacterMobs WHERE name LIKE '%s'", this->GetCleanName()), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - uint32 ExistingNameCount = 0; + std::string query = StringFormat("SELECT COUNT(id) FROM vwBotCharacterMobs " + "WHERE name LIKE '%s'", this->GetCleanName()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return false; + } - while(DataRow = mysql_fetch_row(DatasetResult)) { - ExistingNameCount = atoi(DataRow[0]); - break; - } + uint32 existingNameCount = 0; - if(ExistingNameCount == 0) - Result = true; + for (auto row = results.begin(); row != results.end(); ++row) { + existingNameCount = atoi(row[0]); + break; + } - mysql_free_result(DatasetResult); - } + if(existingNameCount != 0) + return false; - safe_delete(Query); - } - - return Result; + return true; } bool Bot::Save() { - bool Result = false; - std::string errorMessage; - - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - uint32 affectedRows = 0; if(this->GetBotID() == 0) { // New bot record - uint32 TempNewBotID = 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO bots (BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, LastSpawnDate, TotalPlayTime, LastZoneId) VALUES('%u', '%u', '%s', '%s', '%u', '%i', '%i', '%i', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', NOW(), 0, %i)", this->_botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), this->lastname, this->GetLevel(), GetRace(), GetClass(), GetGender(), GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), this->GetDrakkinTattoo(), this->GetDrakkinDetails(), GetHP(), GetMana(), GetMR(), GetCR(), GetDR(), GetFR(), GetPR(), GetCorrup(), GetAC(), GetSTR(), GetSTA(), GetDEX(), GetAGI(), GetINT(), GetWIS(), GetCHA(), GetATK(), _lastZoneId), TempErrorMessageBuffer, 0, &affectedRows, &TempNewBotID)) { - errorMessage = std::string(TempErrorMessageBuffer); + std::string query = StringFormat("INSERT INTO bots (BotOwnerCharacterID, BotSpellsID, Name, LastName, " + "BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, " + "LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, " + "LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, " + "MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, " + "LastSpawnDate, TotalPlayTime, LastZoneId) " + "VALUES('%u', '%u', '%s', '%s', '%u', '%i', '%i', '%i', '%f', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', " + "'%i', NOW(), 0, %i)", + this->_botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), + this->lastname, this->GetLevel(), GetRace(), GetClass(), GetGender(), + GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), + this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), + this->GetBeard(), this->GetDrakkinHeritage(), this->GetDrakkinTattoo(), + this->GetDrakkinDetails(), GetHP(), GetMana(), GetMR(), GetCR(), GetDR(), + GetFR(), GetPR(), GetCorrup(), GetAC(), GetSTR(), GetSTA(), GetDEX(), + GetAGI(), GetINT(), GetWIS(), GetCHA(), GetATK(), _lastZoneId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + auto botOwner = GetBotOwner(); + if (botOwner) + botOwner->Message(13, results.ErrorMessage().c_str()); + return false; } - else { - SetBotID(TempNewBotID); - Result = true; - } - } - else { - // Update existing bot record - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE bots SET BotOwnerCharacterID = '%u', BotSpellsID = '%u', Name = '%s', LastName = '%s', BotLevel = '%u', Race = '%i', Class = '%i', Gender = '%i', Size = '%f', Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', DrakkinDetails = '%i', HP = '%i', Mana = '%i', MR = '%i', CR = '%i', DR = '%i', FR = '%i', PR = '%i', Corrup = '%i', AC = '%i', STR = '%i', STA = '%i', DEX = '%i', AGI = '%i', _INT = '%i', WIS = '%i', CHA = '%i', ATK = '%i', LastSpawnDate = NOW(), TotalPlayTime = '%u', LastZoneId = %i WHERE BotID = '%u'", _botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), this->lastname, this->GetLevel(), _baseRace, this->GetClass(), _baseGender, GetSize(), this->GetLuclinFace(), this->GetHairStyle(), GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), GetDrakkinTattoo(), GetDrakkinDetails(), GetHP(), GetMana(), _baseMR, _baseCR, _baseDR, _baseFR, _basePR, _baseCorrup, _baseAC, _baseSTR, _baseSTA, _baseDEX, _baseAGI, _baseINT, _baseWIS, _baseCHA, _baseATK, GetTotalPlayTime(), _lastZoneId, GetBotID()), TempErrorMessageBuffer, 0, &affectedRows)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - Result = true; - time(&_startTotalPlayTime); - } - } - safe_delete(Query); - - if(!errorMessage.empty() || (Result && affectedRows != 1)) { - if(GetBotOwner() && !errorMessage.empty()) - GetBotOwner()->Message(13, errorMessage.c_str()); - else if(GetBotOwner()) - GetBotOwner()->Message(13, std::string("Unable to save bot to the database.").c_str()); - - Result = false; - } - else { - SaveBuffs(); + SetBotID(results.LastInsertedID()); + SaveBuffs(); SavePet(); SaveStance(); SaveTimers(); + return true; } - return Result; + // Update existing bot record + std::string query = StringFormat("UPDATE bots SET BotOwnerCharacterID = '%u', BotSpellsID = '%u', " + "Name = '%s', LastName = '%s', BotLevel = '%u', Race = '%i', " + "Class = '%i', Gender = '%i', Size = '%f', Face = '%i', " + "LuclinHairStyle = '%i', LuclinHairColor = '%i', " + "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', " + "LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', " + "DrakkinTattoo = '%i', DrakkinDetails = '%i', HP = '%i', Mana = '%i', " + "MR = '%i', CR = '%i', DR = '%i', FR = '%i', PR = '%i', " + "Corrup = '%i', AC = '%i', STR = '%i', STA = '%i', DEX = '%i', " + "AGI = '%i', _INT = '%i', WIS = '%i', CHA = '%i', ATK = '%i', " + "LastSpawnDate = NOW(), TotalPlayTime = '%u', LastZoneId = %i " + "WHERE BotID = '%u'", + _botOwnerCharacterID, this->GetBotSpellID(), this->GetCleanName(), + this->lastname, this->GetLevel(), _baseRace, this->GetClass(), + _baseGender, GetSize(), this->GetLuclinFace(), this->GetHairStyle(), + GetHairColor(), this->GetEyeColor1(), this->GetEyeColor2(), + this->GetBeardColor(), this->GetBeard(), this->GetDrakkinHeritage(), + GetDrakkinTattoo(), GetDrakkinDetails(), GetHP(), GetMana(), + _baseMR, _baseCR, _baseDR, _baseFR, _basePR, _baseCorrup, _baseAC, + _baseSTR, _baseSTA, _baseDEX, _baseAGI, _baseINT, _baseWIS, _baseCHA, + _baseATK, GetTotalPlayTime(), _lastZoneId, GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + auto botOwner = GetBotOwner(); + if (botOwner) + botOwner->Message(13, results.ErrorMessage().c_str()); + return false; + } + + SaveBuffs(); + SavePet(); + SaveStance(); + SaveTimers(); + + return true; } // Returns the current total play time for the bot @@ -2448,169 +2458,107 @@ uint32 Bot::GetTotalPlayTime() { } void Bot::SaveBuffs() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; - int InsertCount = 0; - uint32 buff_count = GetMaxTotalSlots(); - while(BuffCount < BUFF_COUNT) { - if(buffs[BuffCount].spellid > 0 && buffs[BuffCount].spellid != SPELL_UNKNOWN) { - if(InsertCount == 0) { - // Remove any existing buff saves - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - } + // Remove any existing buff saves + std::string query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - int IsPersistent = 0; + for (int buffIndex = 0; buffIndex < BUFF_COUNT; buffIndex++) { + if (buffs[buffIndex].spellid <= 0 || buffs[buffIndex].spellid == SPELL_UNKNOWN) + continue; - if(buffs[BuffCount].persistant_buff) - IsPersistent = 1; - else - IsPersistent = 0; + int isPersistent = buffs[buffIndex].persistant_buff? 1: 0; + query = StringFormat("INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " + "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, " + "CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, " + "caston_x, Persistent, caston_y, caston_z, ExtraDIChance) " + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, " + "%i, %i, %i);", + GetBotID(), buffs[buffIndex].spellid, buffs[buffIndex].casterlevel, + spells[buffs[buffIndex].spellid].buffdurationformula, + buffs[buffIndex].ticsremaining, + CalculatePoisonCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + CalculateDiseaseCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + CalculateCurseCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + CalculateCorruptionCounters(buffs[buffIndex].spellid) > 0 ? buffs[buffIndex].counters : 0, + buffs[buffIndex].numhits, buffs[buffIndex].melee_rune, + buffs[buffIndex].magic_rune, buffs[buffIndex].dot_rune, + buffs[buffIndex].caston_x, isPersistent, buffs[buffIndex].caston_y, + buffs[buffIndex].caston_z, buffs[buffIndex].ExtraDIChance); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botbuffs (BotId, SpellId, CasterLevel, DurationFormula, " - "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", - GetBotID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, - buffs[BuffCount].ticsremaining, - CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, - buffs[BuffCount].dot_rune, - buffs[BuffCount].caston_x, - IsPersistent, - buffs[BuffCount].caston_y, - buffs[BuffCount].caston_z, - buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - InsertCount++; - } - } + } - BuffCount++; - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } } void Bot::LoadBuffs() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - bool BuffsLoaded = false; + std::string query = StringFormat("SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, " + "PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, " + "HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, " + "caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", + GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; + int buffCount = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; + for (auto row = results.begin(); row != results.end(); ++row) { + if(buffCount == BUFF_COUNT) + break; - buffs[BuffCount].spellid = atoi(DataRow[0]); - buffs[BuffCount].casterlevel = atoi(DataRow[1]); - buffs[BuffCount].ticsremaining = atoi(DataRow[3]); + buffs[buffCount].spellid = atoi(row[0]); + buffs[buffCount].casterlevel = atoi(row[1]); + buffs[buffCount].ticsremaining = atoi(row[3]); - if(CalculatePoisonCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[4]); - } else if(CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[5]); - } else if(CalculateCurseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[6]); - } else if(CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[7]); - } - buffs[BuffCount].numhits = atoi(DataRow[8]); - buffs[BuffCount].melee_rune = atoi(DataRow[9]); - buffs[BuffCount].magic_rune = atoi(DataRow[10]); - buffs[BuffCount].dot_rune = atoi(DataRow[11]); - buffs[BuffCount].caston_x = atoi(DataRow[12]); - buffs[BuffCount].casterid = 0; + if(CalculatePoisonCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[4]); + else if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[5]); + else if(CalculateCurseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[6]); + else if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[7]); - bool IsPersistent = false; + buffs[buffCount].numhits = atoi(row[8]); + buffs[buffCount].melee_rune = atoi(row[9]); + buffs[buffCount].magic_rune = atoi(row[10]); + buffs[buffCount].dot_rune = atoi(row[11]); + buffs[buffCount].caston_x = atoi(row[12]); + buffs[buffCount].casterid = 0; - if(atoi(DataRow[13])) - IsPersistent = true; + buffs[buffCount].persistant_buff = atoi(row[13])? true: false; - buffs[BuffCount].caston_y = atoi(DataRow[14]); - buffs[BuffCount].caston_z = atoi(DataRow[15]); - buffs[BuffCount].ExtraDIChance = atoi(DataRow[16]); + buffs[buffCount].caston_y = atoi(row[14]); + buffs[buffCount].caston_z = atoi(row[15]); + buffs[buffCount].ExtraDIChance = atoi(row[16]); - buffs[BuffCount].persistant_buff = IsPersistent; + buffCount++; + } - BuffCount++; - } + query = StringFormat("DELETE FROM botbuffs WHERE BotId = %u", GetBotID()); + results = database.QueryDatabase(query); - mysql_free_result(DatasetResult); - - BuffsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE BotId = %u", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } } uint32 Bot::GetPetSaveId() { - uint32 Result = 0; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotPetsId from botpets where BotId = %u;", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - break; - } + std::string query = StringFormat("SELECT BotPetsId FROM botpets WHERE BotId = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - mysql_free_result(DatasetResult); - } + if (results.RowCount() == 0) + return 0; - safe_delete(Query); + auto row = results.begin(); - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - - return Result; + return atoi(row[0]); } void Bot::LoadPet() { @@ -2646,135 +2594,80 @@ void Bot::LoadPet() { } void Bot::LoadPetStats(std::string* petName, uint16* petMana, uint16* petHitPoints, uint32* botPetId, uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botPetSaveId == 0) + return; - bool statsLoaded = false; + std::string query = StringFormat("SELECT PetId, Name, Mana, HitPoints " + "FROM botpets WHERE BotPetsId = %u;", + botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select PetId, Name, Mana, HitPoints from botpets where BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - *botPetId = atoi(DataRow[0]); - *petName = std::string(DataRow[1]); - *petMana = atoi(DataRow[2]); - *petHitPoints = atoi(DataRow[3]); - break; - } + if (results.RowCount() == 0) + return; - mysql_free_result(DatasetResult); + auto row = results.begin(); - statsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } + *botPetId = atoi(row[0]); + *petName = std::string(row[1]); + *petMana = atoi(row[2]); + *petHitPoints = atoi(row[3]); } void Bot::LoadPetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { - if(petBuffs && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!petBuffs || botPetSaveId == 0) + return; - bool BuffsLoaded = false; + std::string query = StringFormat("SELECT SpellId, CasterLevel, Duration " + "FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, Duration FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; + int buffIndex = 0; - petBuffs[BuffCount].spellid = atoi(DataRow[0]); - petBuffs[BuffCount].level = atoi(DataRow[1]); - petBuffs[BuffCount].duration = atoi(DataRow[2]); + for (auto row = results.begin();row != results.end(); ++row) { + if(buffIndex == BUFF_COUNT) + break; - BuffCount++; - } + petBuffs[buffIndex].spellid = atoi(row[0]); + petBuffs[buffIndex].level = atoi(row[1]); + petBuffs[buffIndex].duration = atoi(row[2]); - mysql_free_result(DatasetResult); - - BuffsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } + buffIndex++; } + + query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + results = database.QueryDatabase(query); + } void Bot::LoadPetItems(uint32* petItems, uint32 botPetSaveId) { - if(petItems && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(!petItems || botPetSaveId == 0) + return; - bool itemsLoaded = false; + std::string query = StringFormat("SELECT ItemId FROM botpetinventory " + "WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT ItemId FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int ItemCount = 0; + int itemIndex = 0; - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(ItemCount == EmuConstants::EQUIPMENT_SIZE) - break; + for(auto row = results.begin(); row != results.end(); ++row) { + if(itemIndex == EmuConstants::EQUIPMENT_SIZE) + break; - petItems[ItemCount] = atoi(DataRow[0]); + petItems[itemIndex] = atoi(row[0]); - ItemCount++; - } + itemIndex++; + } - mysql_free_result(DatasetResult); + query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + results = database.QueryDatabase(query); - itemsLoaded = true; - } - - safe_delete(Query); - Query = 0; - - if(errorMessage.empty() && itemsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } } void Bot::SavePet() { @@ -2814,246 +2707,165 @@ void Bot::SavePet() { } uint32 Bot::SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoints, uint32 botPetId) { - uint32 Result = 0; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + std::string query = StringFormat("REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', " + "Mana = %u, HitPoints = %u;", botPetId, GetBotID(), petName.c_str(), + petMana, petHitPoints); + auto results = database.QueryDatabase(query); - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO botpets SET PetId = %u, BotId = %u, Name = '%s', Mana = %u, HitPoints = %u;", botPetId, GetBotID(), petName.c_str(), petMana, petHitPoints), TempErrorMessageBuffer, 0, 0, &Result)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - - safe_delete(Query); - Query = 0; - - return Result; + return 0; } void Bot::SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId) { - if(petBuffs && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; + if(!petBuffs || botPetSaveId == 0) + return; - while(BuffCount < BUFF_COUNT) { - if(petBuffs[BuffCount].spellid > 0 && petBuffs[BuffCount].spellid != SPELL_UNKNOWN) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botpetbuffs (BotPetsId, SpellId, CasterLevel, Duration) VALUES(%u, %u, %u, %u);", botPetSaveId, petBuffs[BuffCount].spellid, petBuffs[BuffCount].level, petBuffs[BuffCount].duration), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - } - } + int buffIndex = 0; - BuffCount++; - } + while(buffIndex < BUFF_COUNT) { + if(petBuffs[buffIndex].spellid > 0 && petBuffs[buffIndex].spellid != SPELL_UNKNOWN) { + + std::string query = StringFormat("INSERT INTO botpetbuffs " + "(BotPetsId, SpellId, CasterLevel, Duration) " + "VALUES(%u, %u, %u, %u);", + botPetSaveId, petBuffs[buffIndex].spellid, + petBuffs[buffIndex].level, petBuffs[buffIndex].duration); + auto results = database.QueryDatabase(query); + if(!results.Success()) + break; + + } + + buffIndex++; + } - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } } void Bot::SavePetItems(uint32* petItems, uint32 botPetSaveId) { - if(petItems && botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int ItemCount = 0; + if(!petItems || botPetSaveId == 0) + return; - while (ItemCount < EmuConstants::EQUIPMENT_SIZE) { - if(petItems[ItemCount] > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO botpetinventory (BotPetsId, ItemId) VALUES(%u, %u);", botPetSaveId, petItems[ItemCount]), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - ItemCount++; - } - } + for (int itemIndex = 0;itemIndex < EmuConstants::EQUIPMENT_SIZE; itemIndex++) { + if(petItems[itemIndex] == 0) + continue; - ItemCount++; - } - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } + std::string query = StringFormat("INSERT INTO botpetinventory " + "(BotPetsId, ItemId) VALUES(%u, %u);", + botPetSaveId, petItems[itemIndex]); + auto results = database.QueryDatabase(query); + if(!results.Success()) + break; } + } void Bot::DeletePetBuffs(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE FROM botpetbuffs WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::DeletePetItems(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE FROM botpetinventory WHERE BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::DeletePetStats(uint32 botPetSaveId) { - if(botPetSaveId > 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(botPetSaveId == 0) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE from botpets where BotPetsId = %u;", botPetSaveId), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = StringFormat("DELETE from botpets where BotPetsId = %u;", botPetSaveId); + auto results = database.QueryDatabase(query); - safe_delete(Query); - Query = 0; - } } void Bot::LoadStance() { - int Result = 0; - bool loaded = false; - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select StanceID from botstances where BotID = %u;", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - loaded = true; - break; - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + std::string query = StringFormat("SELECT StanceID FROM botstances WHERE BotID = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success() || results.RowCount() == 0) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadStance()"); + SetDefaultBotStance(); + return; } - if(loaded) - SetBotStance((BotStanceType)Result); - else - SetDefaultBotStance(); + auto row = results.begin(); + + SetBotStance((BotStanceType)atoi(row[0])); } void Bot::SaveStance() { - if(_baseBotStance != _botStance) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(_baseBotStance == _botStance) + return; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO botstances (BotID, StanceId) VALUES(%u, %u);", GetBotID(), GetBotStance()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - } - else { - safe_delete(Query); - Query = 0; - } + std::string query = StringFormat("REPLACE INTO botstances (BotID, StanceId) " + "VALUES(%u, %u);", GetBotID(), GetBotStance()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in Bot::SaveStance()"); - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error in Bot::SaveStance()"); - } - } } void Bot::LoadTimers() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT IfNull(bt.TimerID, 0) As TimerID, IfNull(bt.Value, 0) As Value, IfNull(MAX(sn.recast_time), 0) AS MaxTimer FROM bottimers bt, spells_new sn WHERE bt.BotID = %u AND sn.EndurTimerIndex = (SELECT case WHEN TimerID > %i THEN TimerID - %i ELSE TimerID END AS TimerID FROM bottimers WHERE TimerID = bt.TimerID AND BotID = bt.BotID ) AND sn.classes%i <= %i;", GetBotID(), DisciplineReuseStart-1, DisciplineReuseStart-1, GetClass(), GetLevel()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int TimerID = 0; - uint32 Value = 0; - uint32 MaxValue = 0; - - while(DataRow = mysql_fetch_row(DatasetResult)) { - TimerID = atoi(DataRow[0]) - 1; - Value = atoi(DataRow[1]); - MaxValue = atoi(DataRow[2]); - - if(TimerID >= 0 && TimerID < MaxTimer && Value < (Timer::GetCurrentTime() + MaxValue)) { - timers[TimerID] = Value; - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete(Query); - Query = 0; - - if(!errorMessage.empty()) { + std::string query = StringFormat("SELECT IfNull(bt.TimerID, 0) As TimerID, IfNull(bt.Value, 0) As Value, " + "IfNull(MAX(sn.recast_time), 0) AS MaxTimer FROM bottimers bt, spells_new sn " + "WHERE bt.BotID = %u AND sn.EndurTimerIndex = " + "(SELECT case WHEN TimerID > %i THEN TimerID - %i ELSE TimerID END AS TimerID " + "FROM bottimers WHERE TimerID = bt.TimerID AND BotID = bt.BotID ) " + "AND sn.classes%i <= %i;", + GetBotID(), DisciplineReuseStart-1, DisciplineReuseStart-1, GetClass(), GetLevel()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in Bot::LoadTimers()"); + return; } + + int timerID = 0; + uint32 value = 0; + uint32 maxValue = 0; + + for (auto row = results.begin(); row != results.end(); ++row) { + timerID = atoi(row[0]) - 1; + value = atoi(row[1]); + maxValue = atoi(row[2]); + + if(timerID >= 0 && timerID < MaxTimer && value < (Timer::GetCurrentTime() + maxValue)) + timers[timerID] = value; + } + } void Bot::SaveTimers() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + bool hadError = false; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM bottimers WHERE BotID = %u;", GetBotID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; + std::string query = StringFormat("DELETE FROM bottimers WHERE BotID = %u;", GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + hadError = true; + + for(int timerIndex = 0; timerIndex < MaxTimer; timerIndex++) { + if(timers[timerIndex] <= Timer::GetCurrentTime()) + continue; + + query = StringFormat("REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u);", + GetBotID(), timerIndex+1, timers[timerIndex]); + results = database.QueryDatabase(query); + + if(!results.Success()) + hadError = true; } - for(int i = 0; i < MaxTimer; i++) { - if(timers[i] > Timer::GetCurrentTime()) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "REPLACE INTO bottimers (BotID, TimerID, Value) VALUES(%u, %u, %u);", GetBotID(), i+1, timers[i]), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - - safe_delete(Query); - Query = 0; - } - } - - if(!errorMessage.empty()) { + if(hadError) LogFile->write(EQEMuLog::Error, "Error in Bot::SaveTimers()"); - } + } bool Bot::Process() @@ -3398,7 +3210,7 @@ void Bot::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if (CanSkillProc && HasSkillProcs()) TrySkillProc(other, skillinuse, ReuseTime); - + if (CanSkillProc && (damage > 0) && HasSkillProcSuccess()) TrySkillProc(other, skillinuse, ReuseTime, true); } @@ -4255,44 +4067,41 @@ void Bot::Depop() { } bool Bot::DeleteBot(std::string* errorMessage) { - bool Result = false; - int TempCounter = 0; + bool hadError = false; - if(this->GetBotID() > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(this->GetBotID() == 0) + return false; - // TODO: These queries need to be ran together as a transaction.. ie, if one or more fail then they all will fail to commit to the database. + // TODO: These queries need to be ran together as a transaction.. ie, if one or more fail then they all will fail to commit to the database. + std::string query = StringFormat("DELETE FROM botinventory WHERE botid = '%u'", this->GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botinventory WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM botbuffs WHERE botid = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botbuffs WHERE botid = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM botstances WHERE BotID = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM botstances WHERE BotID = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; + query = StringFormat("DELETE FROM bots WHERE BotID = '%u'", this->GetBotID()); + results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + hadError = true; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM bots WHERE BotID = '%u'", this->GetBotID()), TempErrorMessageBuffer)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; - - if(TempCounter == 4) - Result = true; - } - - return Result; + return !hadError; } void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { @@ -4347,161 +4156,145 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) { // Saves the specified item as an inventory record in the database for this bot. void Bot::SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, std::string *errorMessage) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (this->GetBotID() > 0 && slotID >= EmuConstants::EQUIPMENT_BEGIN && itemID > NO_ITEM) { - if (inst && inst->IsType(ItemClassCommon)) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; ++i) { - ItemInst* auginst = inst->GetItem(i); - augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } - } - if(!database.RunQuery(query, MakeAnyLenString(&query, - "REPLACE INTO botinventory " - " (botid,slotid,itemid,charges,instnodrop,color," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,%lu,%lu," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)this->GetBotID(), (unsigned long)slotID, (unsigned long)itemID, (unsigned long)inst->GetCharges(), (unsigned long)(inst->IsInstNoDrop() ? 1:0),(unsigned long)inst->GetColor(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]), errbuf)) { - *errorMessage = std::string(errbuf); - } + if (this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN || itemID <= NO_ITEM) + return; - safe_delete_array(query); - } + if (inst && inst->IsType(ItemClassCommon)) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; ++i) { + ItemInst* auginst = inst->GetItem(i); + augslot[i] = (auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; + } + } + + std::string query = StringFormat("REPLACE INTO botinventory (botid, slotid, itemid, charges, instnodrop, color, " + "augslot1, augslot2, augslot3, augslot4, augslot5) " + "VALUES(%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)", + (unsigned long)this->GetBotID(), (unsigned long)slotID, (unsigned long)itemID, + (unsigned long)inst->GetCharges(), (unsigned long)(inst->IsInstNoDrop()? 1: 0), + (unsigned long)inst->GetColor(), (unsigned long)augslot[0], (unsigned long)augslot[1], + (unsigned long)augslot[2], (unsigned long)augslot[3], (unsigned long)augslot[4]); + auto results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); } // Deletes the inventory record for the specified item from the database for this bot. void Bot::RemoveBotItemBySlot(uint32 slotID, std::string *errorMessage) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(this->GetBotID() > 0 && slotID >= 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botinventory WHERE botid=%i AND slotid=%i", this->GetBotID(), slotID), errbuf)){ - *errorMessage = std::string(errbuf); - } - safe_delete_array(query); - m_inv.DeleteItem(slotID); - } + if(this->GetBotID() == 0) + return; + + std::string query = StringFormat("DELETE FROM botinventory " + "WHERE botid = %i AND slotid = %i", + this->GetBotID(), slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); + + m_inv.DeleteItem(slotID); } // Retrieves all the inventory records from the database for this bot. void Bot::GetBotItems(std::string* errorMessage, Inventory &inv) { - if(this->GetBotID() > 0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0) + return; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5,instnodrop FROM botinventory WHERE botid=%i order by slotid", this->GetBotID()), errbuf, &DatasetResult)) { - while(DataRow = mysql_fetch_row(DatasetResult)) { - int16 slot_id = atoi(DataRow[0]); - uint32 item_id = atoi(DataRow[1]); - uint16 charges = atoi(DataRow[2]); - uint32 color = atoul(DataRow[3]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoul(DataRow[4]); - aug[1] = (uint32)atoul(DataRow[5]); - aug[2] = (uint32)atoul(DataRow[6]); - aug[3] = (uint32)atoul(DataRow[7]); - aug[4] = (uint32)atoul(DataRow[8]); - bool instnodrop = (DataRow[9] && (uint16)atoi(DataRow[9])) ? true : false; + std::string query = StringFormat("SELECT slotid, itemid, charges, color, " + "augslot1, augslot2, augslot3, augslot4, " + "augslot5, instnodrop FROM botinventory " + "WHERE botid = %i ORDER BY slotid", this->GetBotID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } - ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); - if(inst) { - int16 put_slot_id = INVALID_INDEX; - if(instnodrop || ((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) && inst->GetItem()->Attuneable)) - inst->SetInstNoDrop(true); - if(color > 0) - inst->SetColor(color); - if(charges==255) - inst->SetCharges(-1); - else - inst->SetCharges(charges); - if((slot_id >= 8000) && (slot_id <= 8999)) { - // do nothing - } - else { - put_slot_id = inv.PutItem(slot_id, *inst); - } - safe_delete(inst); + for (auto row = results.begin(); row != results.end(); ++row) { + int16 slot_id = atoi(row[0]); + uint32 item_id = atoi(row[1]); + uint16 charges = atoi(row[2]); + uint32 color = atoul(row[3]); + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoul(row[4]); + aug[1] = (uint32)atoul(row[5]); + aug[2] = (uint32)atoul(row[6]); + aug[3] = (uint32)atoul(row[7]); + aug[4] = (uint32)atoul(row[8]); + bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: botid=%i, item_id=%i, slot_id=%i", - this->GetBotID(), item_id, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: botid %i has an invalid item_id %i in inventory slot %i", - this->GetBotID(), item_id, slot_id); - } - } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(errbuf); + ItemInst* inst = database.CreateItem(item_id, charges, aug[0], aug[1], aug[2], aug[3], aug[4]); + if (!inst) { + LogFile->write(EQEMuLog::Error, "Warning: botid %i has an invalid item_id %i in inventory slot %i", this->GetBotID(), item_id, slot_id); + continue; + } + + int16 put_slot_id = INVALID_INDEX; + + if (instnodrop || ((slot_id >= EmuConstants::EQUIPMENT_BEGIN) && (slot_id <= EmuConstants::EQUIPMENT_END) && inst->GetItem()->Attuneable)) + inst->SetInstNoDrop(true); + + if (color > 0) + inst->SetColor(color); + + if (charges==255) + inst->SetCharges(-1); + else + inst->SetCharges(charges); + + if (slot_id < 8000 || slot_id > 8999) + put_slot_id = inv.PutItem(slot_id, *inst); + + safe_delete(inst); + + // Save ptr to item in inventory + if (put_slot_id == INVALID_INDEX) + LogFile->write(EQEMuLog::Error, "Warning: Invalid slot_id for item in inventory: botid=%i, item_id=%i, slot_id=%i",this->GetBotID(), item_id, slot_id); + + } - safe_delete_array(query); - } } // Returns the inventory record for this bot from the database for the specified equipment slot. uint32 Bot::GetBotItemBySlot(uint32 slotID) { - uint32 Result = 0; - if(this->GetBotID() > 0 && slotID >= EmuConstants::EQUIPMENT_BEGIN) { - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0 || slotID < EmuConstants::EQUIPMENT_BEGIN) + return 0; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT itemid FROM botinventory WHERE botid=%i AND slotid=%i", GetBotID(), slotID), 0, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT itemid FROM botinventory WHERE botid=%i AND slotid = %i", GetBotID(), slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - mysql_free_result(DatasetResult); - } + if(results.RowCount() != 1) + return 0; - safe_delete_array(query); - } + auto row = results.begin(); - return Result; + return atoi(row[0]); } // Returns the number of inventory records the bot has in the database. uint32 Bot::GetBotItemsCount(std::string *errorMessage) { - uint32 Result = 0; - if(this->GetBotID() > 0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(this->GetBotID() == 0) + return 0; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT COUNT(*) FROM botinventory WHERE botid=%i", this->GetBotID()), errbuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT COUNT(*) FROM botinventory WHERE botid = %i", this->GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(errbuf); + if(results.RowCount() != 1) + return 0; - safe_delete_array(query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } bool Bot::MesmerizeTarget(Mob* target) { @@ -4694,92 +4487,82 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { } uint32 Bot::GetBotIDByBotName(std::string botName) { - uint32 Result = 0; + if(botName.empty()) + return 0; - if(!botName.empty()) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - std::string errorMessage; + std::string query = StringFormat("SELECT BotID FROM bots WHERE Name = '%s'", botName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotID FROM bots WHERE Name = '%s'", botName.c_str()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - Result = atoi(DataRow[0]); - break; - } + if (results.RowCount() == 0) + return 0; - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - - if(!errorMessage.empty()) { - // TODO: Log this error to zone error log - } - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } Bot* Bot::LoadBot(uint32 botID, std::string* errorMessage) { - Bot* Result = 0; + Bot* loadedBot = nullptr; - if(botID > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botID == 0) + return nullptr; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, BotCreateDate, LastSpawnDate, TotalPlayTime, LastZoneId FROM bots WHERE BotID = '%u'", botID), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - NPCType DefaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(DataRow[2]), std::string(DataRow[3]), atoi(DataRow[4]), atoi(DataRow[5]), atoi(DataRow[6]), atoi(DataRow[7])); - NPCType TempNPCStruct = FillNPCTypeStruct(atoi(DataRow[1]), std::string(DataRow[2]), std::string(DataRow[3]), atoi(DataRow[4]), atoi(DataRow[5]), atoi(DataRow[6]), atoi(DataRow[7]), atof(DataRow[8]), atoi(DataRow[9]), atoi(DataRow[10]), atoi(DataRow[11]), atoi(DataRow[12]), atoi(DataRow[13]), atoi(DataRow[14]), atoi(DataRow[15]), atoi(DataRow[16]), atoi(DataRow[17]), atoi(DataRow[18]), atoi(DataRow[19]), atoi(DataRow[20]), DefaultNPCTypeStruct.MR, DefaultNPCTypeStruct.CR, DefaultNPCTypeStruct.DR, DefaultNPCTypeStruct.FR, DefaultNPCTypeStruct.PR, DefaultNPCTypeStruct.Corrup, DefaultNPCTypeStruct.AC, DefaultNPCTypeStruct.STR, DefaultNPCTypeStruct.STA, DefaultNPCTypeStruct.DEX, DefaultNPCTypeStruct.AGI, DefaultNPCTypeStruct.INT, DefaultNPCTypeStruct.WIS, DefaultNPCTypeStruct.CHA, DefaultNPCTypeStruct.ATK); - Result = new Bot(botID, atoi(DataRow[0]), atoi(DataRow[1]), atof(DataRow[38]), atoi(DataRow[39]), TempNPCStruct); - break; - } + std::string query = StringFormat("SELECT BotOwnerCharacterID, BotSpellsID, Name, LastName, BotLevel, " + "Race, Class, Gender, Size, Face, LuclinHairStyle, LuclinHairColor, " + "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails, HP, Mana, MR, CR, " + "DR, FR, PR, Corrup, AC, STR, STA, DEX, AGI, _INT, WIS, CHA, ATK, " + "BotCreateDate, LastSpawnDate, TotalPlayTime, LastZoneId " + "FROM bots WHERE BotID = '%u'", botID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return nullptr; + } - mysql_free_result(DatasetResult); - } + if (results.RowCount() == 0) + return nullptr; - safe_delete_array(Query); - } + auto row = results.begin(); + NPCType defaultNPCTypeStruct = CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), + atoi(row[4]), atoi(row[5]), atoi(row[6]), atoi(row[7])); - return Result; + NPCType tempNPCStruct = FillNPCTypeStruct(atoi(row[1]), std::string(row[2]), std::string(row[3]), atoi(row[4]), + atoi(row[5]), atoi(row[6]), atoi(row[7]), atof(row[8]), atoi(row[9]), + atoi(row[10]), atoi(row[11]), atoi(row[12]), atoi(row[13]), atoi(row[14]), + atoi(row[15]), atoi(row[16]), atoi(row[17]), atoi(row[18]), atoi(row[19]), + atoi(row[20]), defaultNPCTypeStruct.MR, defaultNPCTypeStruct.CR, + defaultNPCTypeStruct.DR, defaultNPCTypeStruct.FR, defaultNPCTypeStruct.PR, + defaultNPCTypeStruct.Corrup, defaultNPCTypeStruct.AC, defaultNPCTypeStruct.STR, + defaultNPCTypeStruct.STA, defaultNPCTypeStruct.DEX, defaultNPCTypeStruct.AGI, + defaultNPCTypeStruct.INT, defaultNPCTypeStruct.WIS, defaultNPCTypeStruct.CHA, + defaultNPCTypeStruct.ATK); + + loadedBot = new Bot(botID, atoi(row[0]), atoi(row[1]), atof(row[38]), atoi(row[39]), tempNPCStruct); + + return loadedBot; } std::list Bot::GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage) { - std::list Result; + std::list groupedBots; - if(groupId > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(groupId == 0) + return groupedBots; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select g.mobid as BotID from vwGroups as g join bots as b on g.mobid = b.BotId and g.mobtype = 'B' where g.groupid = %u", groupId), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(DataRow) { - Result.push_back(atoi(DataRow[0])); - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); + std::string query = StringFormat("SELECT g.mobid AS BotID FROM vwGroups AS g " + "JOIN bots AS b ON g.mobid = b.BotId AND g.mobtype = 'B' " + "WHERE g.groupid = %u", groupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return groupedBots; } - return Result; + for (auto row = results.begin(); row != results.end(); ++row) + groupedBots.push_back(atoi(row[0])); + + return groupedBots; } // Load and spawn all zoned bots by bot owner character @@ -4845,383 +4628,278 @@ bool Bot::GroupHasBot(Group* group) { } std::list Bot::GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage) { - std::list Result; + std::list ownersBots; - if(botOwnerCharacterID > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return ownersBots; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotID, Name, Class, BotLevel, Race FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID), TempErrorMessageBuffer, &DatasetResult)) { - *errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(DataRow) { - BotsAvailableList TempAvailableBot; - TempAvailableBot.BotID = atoi(DataRow[0]); - strcpy(TempAvailableBot.BotName, DataRow[1]); - TempAvailableBot.BotClass = atoi(DataRow[2]); - TempAvailableBot.BotLevel = atoi(DataRow[3]); - TempAvailableBot.BotRace = atoi(DataRow[4]); - - Result.push_back(TempAvailableBot); - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); + std::string query = StringFormat("SELECT BotID, Name, Class, BotLevel, Race " + "FROM bots WHERE BotOwnerCharacterID = '%u'", botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return ownersBots; } - return Result; + for (auto row = results.begin(); row != results.end(); ++row) { + BotsAvailableList availableBot; + availableBot.BotID = atoi(row[0]); + strcpy(availableBot.BotName, row[1]); + availableBot.BotClass = atoi(row[2]); + availableBot.BotLevel = atoi(row[3]); + availableBot.BotRace = atoi(row[4]); + + ownersBots.push_back(availableBot); + } + + return ownersBots; } std::list Bot::ListSpawnedBots(uint32 characterID, std::string* errorMessage) { - std::list Result; - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::list spawnedBots; - if(characterID > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT bot_name, zone_name FROM botleader WHERE leaderid=%i", characterID), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + if(characterID == 0) + return spawnedBots; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); - SpawnedBotsList TempSpawnedBotsList; - TempSpawnedBotsList.BotLeaderCharID = characterID; - strcpy(TempSpawnedBotsList.BotName, DataRow[0]); - strcpy(TempSpawnedBotsList.ZoneName, DataRow[1]); + std::string query = StringFormat("SELECT bot_name, zone_name FROM botleader WHERE leaderid=%i", characterID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return spawnedBots; + } - Result.push_back(TempSpawnedBotsList); - } - } + for(auto row = results.begin(); row != results.end(); ++row) { + SpawnedBotsList spawnedBotsList; + spawnedBotsList.BotLeaderCharID = characterID; + strcpy(spawnedBotsList.BotName, row[0]); + strcpy(spawnedBotsList.ZoneName, row[1]); - mysql_free_result(DatasetResult); - } + spawnedBots.push_back(spawnedBotsList); + } - safe_delete_array(Query); - } - - return Result; + return spawnedBots; } void Bot::SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage) { - if(botGroup && !botGroupName.empty()) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + if(!botGroup || botGroupName.empty()) + return; - Mob* tempGroupLeader = botGroup->GetLeader(); + Mob* tempGroupLeader = botGroup->GetLeader(); - if(tempGroupLeader->IsBot()) { - uint32 botGroupId = 0; + if(!tempGroupLeader->IsBot()) + return; - uint32 botGroupLeaderBotId = tempGroupLeader->CastToBot()->GetBotID(); + uint32 botGroupId = 0; + uint32 botGroupLeaderBotId = tempGroupLeader->CastToBot()->GetBotID(); - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT into botgroup (BotGroupLeaderBotId, BotGroupName) values (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()), errbuf, 0, 0, &botGroupId)) { - *errorMessage = std::string(errbuf); - } - else { - if(botGroupId > 0) { - for(int counter = 0; counter < botGroup->GroupCount(); counter++) { - Mob* tempBot = botGroup->members[counter]; + std::string query = StringFormat("INSERT INTO botgroup (BotGroupLeaderBotId, BotGroupName) " + "VALUES (%u, '%s')", botGroupLeaderBotId, botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } - if(tempBot && tempBot->IsBot()) { - uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); + if(botGroupId == 0) + return; - safe_delete_array(query); + for(int groupMemberIndex = 0; groupMemberIndex < botGroup->GroupCount(); groupMemberIndex++) { + Mob* tempBot = botGroup->members[groupMemberIndex]; - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT into botgroupmembers (BotGroupId, BotId) values (%u, %u)", botGroupId, botGroupMemberBotId), errbuf)) { - *errorMessage = std::string(errbuf); - } - } - } - } - } + if(!tempBot || !tempBot->IsBot()) + continue; + + uint32 botGroupMemberBotId = tempBot->CastToBot()->GetBotID(); + + query = StringFormat("INSERT INTO botgroupmembers (BotGroupId, BotId) " + "VALUES (%u, %u)", botGroupId, botGroupMemberBotId); + results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); + } - safe_delete_array(query); - } - } } void Bot::DeleteBotGroup(std::string botGroupName, std::string* errorMessage) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if(!botGroupName.empty()) { - uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); + if(botGroupName.empty()) + return; - if(errorMessage->empty() && botGroupId > 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botgroupmembers WHERE BotGroupId = %u", botGroupId), errbuf)) { - *errorMessage = std::string(errbuf); - } - else { - safe_delete_array(query); + uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botgroup WHERE BotGroupId = %u", botGroupId), errbuf)) { - *errorMessage = std::string(errbuf); - } - } + if(!errorMessage->empty() || botGroupId== 0) + return; - safe_delete_array(query); - } - } + std::string query = StringFormat("DELETE FROM botgroupmembers WHERE BotGroupId = %u", botGroupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return; + } + + query = StringFormat("DELETE FROM botgroup WHERE BotGroupId = %u", botGroupId); + results = database.QueryDatabase(query); + if(!results.Success()) + *errorMessage = std::string(results.ErrorMessage()); } std::list Bot::LoadBotGroup(std::string botGroupName, std::string* errorMessage) { - std::list Result; - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::list botGroup; - if(!botGroupName.empty()) { - uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); + if(botGroupName.empty()) + return botGroup; - if(botGroupId > 0) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotId from botgroupmembers where BotGroupId = %u", botGroupId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + uint32 botGroupId = GetBotGroupIdByBotGroupName(botGroupName, errorMessage); - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if(botGroupId == 0) + return botGroup; - if(DataRow) { - BotGroup tempBotGroup; - tempBotGroup.BotGroupID = botGroupId; - tempBotGroup.BotID = atoi(DataRow[0]); + std::string query = StringFormat("SELECT BotId FROM botgroupmembers WHERE BotGroupId = %u", botGroupId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return botGroup; + } - Result.push_back(tempBotGroup); - } - } - } + for(auto row = results.begin(); row != results.end(); ++row) { + BotGroup tempBotGroup; + tempBotGroup.BotGroupID = botGroupId; + tempBotGroup.BotID = atoi(row[0]); - mysql_free_result(DatasetResult); - } + botGroup.push_back(tempBotGroup); + } - safe_delete_array(Query); - } - } - - return Result; + return botGroup; } std::list Bot::GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage) { - std::list result; + std::list botGroups; - if(botOwnerCharacterId > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterId == 0) + return botGroups; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupName, BotGroupLeaderName from vwBotGroups where BotOwnerCharacterId = %u", botOwnerCharacterId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupName, BotGroupLeaderName FROM vwBotGroups " + "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return botGroups; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + for(auto row = results.begin(); row != results.end(); ++row) { + BotGroupList botGroupList; + botGroupList.BotGroupName = std::string(row[0]); + botGroupList.BotGroupLeaderName = std::string(row[1]); - if(DataRow) { - BotGroupList botGroupList; - botGroupList.BotGroupName = std::string(DataRow[0]); - botGroupList.BotGroupLeaderName = std::string(DataRow[1]); + botGroups.push_back(botGroupList); + } - result.push_back(botGroupList); - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + return botGroups; } bool Bot::DoesBotGroupNameExist(std::string botGroupName) { - bool result = false; - if(!botGroupName.empty()) { - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return false; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " + "WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return false; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + for(auto row = results.begin(); row != results.end(); ++row) { + uint32 tempBotGroupId = atoi(row[0]); + std::string tempBotGroupName = std::string(row[1]); - if(DataRow) { - uint32 tempBotGroupId = atoi(DataRow[0]); - std::string tempBotGroupName = std::string(DataRow[1]); + if (botGroupName == tempBotGroupName && tempBotGroupId != 0) + return true; + } - if(botGroupName == tempBotGroupName) { - result = tempBotGroupId; - break; - } - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + return false; } uint32 Bot::CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName, std::string* errorMessage) { - uint32 result = 0; - if(botOwnerCharacterId > 0 && !botGroupName.empty()) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterId == 0 || botGroupName.empty()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId, BotGroupName from vwBotGroups where BotOwnerCharacterId = %u", botOwnerCharacterId), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId, BotGroupName FROM vwBotGroups " + "WHERE BotOwnerCharacterId = %u", botOwnerCharacterId); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if(results.RowCount() == 0) + return 0; - if(DataRow) { - uint32 tempBotGroupId = atoi(DataRow[0]); - std::string tempBotGroupName = std::string(DataRow[1]); + for(auto row = results.begin(); row != results.end(); ++row) { - if(botGroupName == tempBotGroupName) { - result = tempBotGroupId; - break; - } - } - } - } + uint32 tempBotGroupId = atoi(row[0]); + std::string tempBotGroupName = std::string(row[1]); - mysql_free_result(DatasetResult); - } + if(botGroupName == tempBotGroupName) + return tempBotGroupId; + } - safe_delete_array(Query); - } - - return result; + return 0; } uint32 Bot::GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* errorMessage) { - uint32 result = 0; - if(!botGroupName.empty()) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), ErrBuf, &DatasetResult)) { - *errorMessage = std::string(ErrBuf); - } - else { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupId FROM vwBotGroups " + "WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); + if (results.RowCount() == 0) + return 0; - if(DataRow) { - result = atoi(DataRow[0]); - break; - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::GetBotGroupLeaderIdByBotGroupName(std::string botGroupName) { - uint32 result = 0; - if(!botGroupName.empty()) { - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botGroupName.empty()) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "select BotGroupLeaderBotId from vwBotGroups where BotGroupName = '%s'", botGroupName.c_str()), 0, &DatasetResult)) { - uint32 RowCount = mysql_num_rows(DatasetResult); + std::string query = StringFormat("SELECT BotGroupLeaderBotId FROM vwBotGroups WHERE BotGroupName = '%s'", botGroupName.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return 0; - if(RowCount > 0) { - for(int iCounter = 0; iCounter < RowCount; iCounter++) { - DataRow = mysql_fetch_row(DatasetResult); - - if(DataRow) { - result = atoi(DataRow[0]); - break; - } - } - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::AllowedBotSpawns(uint32 botOwnerCharacterID, std::string* errorMessage) { - uint32 Result = 0; - if(botOwnerCharacterID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT value FROM quest_globals WHERE name='bot_spawn_limit' and charid=%i", botOwnerCharacterID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT value FROM quest_globals " + "WHERE name = 'bot_spawn_limit' AND charid = %i", + botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { @@ -5237,56 +4915,42 @@ uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessag } uint32 Bot::CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage) { - uint32 Result = 0; - if(botOwnerCharacterID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botOwnerCharacterID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT COUNT(BotID) FROM bots WHERE BotOwnerCharacterID=%i", botOwnerCharacterID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT COUNT(BotID) FROM bots " + "WHERE BotOwnerCharacterID=%i", botOwnerCharacterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } uint32 Bot::GetBotOwnerCharacterID(uint32 botID, std::string* errorMessage) { - uint32 Result = 0; - if(botID > 0) { - char ErrBuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + if(botID == 0) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT BotOwnerCharacterID FROM bots WHERE BotID = %u", botID), ErrBuf, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - if(DataRow = mysql_fetch_row(DatasetResult)) - Result = atoi(DataRow[0]); - } + std::string query = StringFormat("SELECT BotOwnerCharacterID FROM bots WHERE BotID = %u", botID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + *errorMessage = std::string(results.ErrorMessage()); + return 0; + } - mysql_free_result(DatasetResult); - } - else - *errorMessage = std::string(ErrBuf); + if (results.RowCount() != 1) + return 0; - safe_delete_array(Query); - } - - return Result; + auto row = results.begin(); + return atoi(row[0]); } void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) { @@ -8109,7 +7773,7 @@ void Bot::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if (HasSkillProcs()) TrySkillProc(who, skill, ReuseTime*1000); - + if (max_damage > 0 && HasSkillProcSuccess()) TrySkillProc(who, skill, ReuseTime*1000, true); @@ -8349,11 +8013,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if(!ca_time) return; - float HasteModifier = 0; - if (GetHaste()) - HasteModifier = 10000 / (100 + GetHaste()); - else - HasteModifier = 100; + float HasteModifier = GetHaste() * 0.01f; int32 dmg = 0; uint16 skill_to_use = -1; @@ -8585,7 +8245,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { TryBackstab(target,reuse); } - classattack_timer.Start(reuse*HasteModifier/100); + classattack_timer.Start(reuse / HasteModifier); } bool Bot::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { @@ -8891,58 +8551,39 @@ bool Bot::ProcessGuildRemoval(Client* guildOfficer, std::string botName) { } void Bot::SetBotGuildMembership(uint32 botId, uint32 guildid, uint8 rank) { - if(botId > 0) { - std::string errorMessage; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + if(botId == 0) + return; - if(guildid > 0) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO botguildmembers SET char_id = %u, guild_id = %u, rank = %u;", botId, guildid, rank), errbuf)) { - errorMessage = std::string(errbuf); - } - } - else { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM botguildmembers WHERE char_id = %u;", botId), errbuf)) { - errorMessage = std::string(errbuf); - } - } + if(guildid > 0) { + std::string query = StringFormat("REPLACE INTO botguildmembers " + "SET char_id = %u, guild_id = %u, rank = %u;", + botId, guildid, rank); + auto results = database.QueryDatabase(query); + return; + } - safe_delete_array(query); - - if(!errorMessage.empty()) { - // TODO: Log this error message to the zone error log - } - } + std::string query = StringFormat("DELETE FROM botguildmembers WHERE char_id = %u;", botId); + auto results = database.QueryDatabase(query); } void Bot::LoadGuildMembership(uint32* guildId, uint8* guildRank, std::string* guildName) { - if(guildId && guildRank && guildName) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT gm.guild_id, gm.rank, g.name FROM vwGuildMembers AS gm JOIN guilds AS g ON gm.guild_id = g.id WHERE gm.char_id = %u AND gm.mobtype = 'B';", GetBotID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - *guildId = atoi(DataRow[0]); - *guildRank = atoi(DataRow[1]); - *guildName = std::string(DataRow[2]); - break; - } + if(guildId == nullptr || guildRank == nullptr || guildName == nullptr) + return; - mysql_free_result(DatasetResult); - } + std::string query = StringFormat("SELECT gm.guild_id, gm.rank, g.name " + "FROM vwGuildMembers AS gm JOIN guilds AS g " + "ON gm.guild_id = g.id " + "WHERE gm.char_id = %u AND gm.mobtype = 'B';", + GetBotID()); + auto results = database.QueryDatabase(query); + if(!results.Success() || results.RowCount() == 0) + return; - safe_delete(Query); - - if(!errorMessage.empty()) { - // TODO: Record this error message to zone error log - } - } + auto row = results.begin(); + *guildId = atoi(row[0]); + *guildRank = atoi(row[1]); + *guildName = std::string(row[2]); } int32 Bot::CalcMaxMana() { @@ -8978,11 +8619,7 @@ int32 Bot::CalcMaxMana() { } void Bot::SetAttackTimer() { - float PermaHaste; - if (GetHaste()) - PermaHaste = 1 / (1 + (float)GetHaste()/100); - else - PermaHaste = 1.0f; + float haste_mod = GetHaste() * 0.01f; //default value for attack timer in case they have //an invalid weapon equipped: @@ -8991,117 +8628,77 @@ void Bot::SetAttackTimer() { Timer* TimerToUse = nullptr; const Item_Struct* PrimaryWeapon = nullptr; - for (int i=MainRange; i<=MainSecondary; i++) { - + 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) + else if (i == MainSecondary) TimerToUse = &attack_dw_timer; else //invalid slot (hands will always hit this) continue; const Item_Struct* ItemToUse = nullptr; ItemInst* ci = GetBotItem(i); - if(ci) + if (ci) ItemToUse = ci->GetItem(); //special offhand stuff - if(i == MainSecondary) { + 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 (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(!GetSkill(SkillDualWield)) { + if (!GetSkill(SkillDualWield)) { attack_dw_timer.Disable(); continue; } } //see if we have a valid weapon - if(ItemToUse != nullptr) { + if (ItemToUse != nullptr) { //check type and damage/delay - if(ItemToUse->ItemClass != ItemClassCommon - || ItemToUse->Damage == 0 - || ItemToUse->Delay == 0) { + if (ItemToUse->ItemClass != ItemClassCommon + || ItemToUse->Damage == 0 + || ItemToUse->Delay == 0) { //no weapon - ItemToUse = nullptr; + ItemToUse = nullptr; } // Check to see if skill is valid - else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) { + 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; + int hhe = itembonuses.HundredHands + spellbonuses.HundredHands; + int speed = 0; + int delay = 36; //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 - } + if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) + delay = GetMonkHandToHandDelay(); } 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 = SLOT_PERSONAL_BEGIN; r <= SLOT_PERSONAL_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 / 3); - 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); + delay = ItemToUse->Delay; } + if (RuleB(Spells, Jun182014HundredHandsRevamp)) + speed = static_cast(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100); + else + speed = static_cast(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100); + TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); if(i == MainPrimary) PrimaryWeapon = ItemToUse; @@ -9109,7 +8706,7 @@ void Bot::SetAttackTimer() { } int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - + if (spells[spell_id].targettype == ST_Self) return value; @@ -9121,22 +8718,22 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { // Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40. if ( (spell_id == SPELL_HARM_TOUCH || spell_id == SPELL_HARM_TOUCH2 || spell_id == SPELL_IMP_HARM_TOUCH ) && GetLevel() > 40) value -= (GetLevel() - 40) * 20; - + //This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch. if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch - + int chance = RuleI(Spells, BaseCritChance); chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; - + if (chance > 0){ - + int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. //Improved Harm Touch is a guaranteed crit if you have at least one level of SCF. if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) chance = 100; - + if (MakeRandomInt(1,100) <= chance){ Critical = true; ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; @@ -9149,23 +8746,23 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } ratio += RuleI(Spells, WizCritRatio); //Default is zero - + if (Critical){ - value = value_BaseEffect*ratio/100; + value = value_BaseEffect*ratio/100; - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; value += int(value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100)*ratio/100; if (target) { - value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); + value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100; + value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id)*ratio/100; + value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id)*ratio/100; - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); + value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; @@ -9177,28 +8774,28 @@ int32 Bot::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { } value = value_BaseEffect; - - value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; - + + value += value_BaseEffect*GetBotFocusEffect(BotfocusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetBotFocusEffect(BotfocusFcDamagePctCrit, spell_id)/100; if (target) { value += value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100; - value -= target->GetFcDamageAmtIncoming(this, spell_id); + value -= target->GetFcDamageAmtIncoming(this, spell_id); } - value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); + value -= GetBotFocusEffect(BotfocusFcDamageAmtCrit, spell_id); + + value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - value -= GetBotFocusEffect(BotfocusFcDamageAmt, spell_id); - if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) - value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); + value += GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); return value; } int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - + if (target == nullptr) target = this; @@ -9206,37 +8803,37 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int16 chance = 0; int8 modifier = 1; bool Critical = false; - - value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); - + + value_BaseEffect = value + (value*GetBotFocusEffect(BotfocusFcBaseEffects, spell_id)/100); + value = value_BaseEffect; - value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id)/100); - + value += int(value_BaseEffect*GetBotFocusEffect(BotfocusImprovedHeal, spell_id)/100); + // Instant Heals if(spells[spell_id].buffduration < 1) { - chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + chance += itembonuses.CriticalHealChance + spellbonuses.CriticalHealChance + aabonuses.CriticalHealChance; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - if (spellbonuses.CriticalHealDecay) - chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); - + chance += GetDecayEffectValue(spell_id, SE_CriticalHealDecay); + if(chance && (MakeRandomInt(0,99) < chance)) { Critical = true; modifier = 2; //At present time no critical heal amount modifier SPA exists. } - + value *= modifier; - value += GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier; - value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); + value += GetBotFocusEffect(BotfocusFcHealAmtCrit, spell_id) * modifier; + value += GetBotFocusEffect(BotfocusFcHealAmt, spell_id); + value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; - value += value*target->GetHealRate(spell_id, this)/100; + value += value*target->GetHealRate(spell_id, this)/100; if (Critical) entity_list.MessageClose(this, false, 100, MT_SpellCrits, "%s performs an exceptional heal! (%d)", GetName(), value); @@ -9246,14 +8843,14 @@ int32 Bot::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Heal over time spells. [Heal Rate and Additional Healing effects do not increase this value] else { - - chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; - chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); - + chance = itembonuses.CriticalHealOverTime + spellbonuses.CriticalHealOverTime + aabonuses.CriticalHealOverTime; + + chance += target->GetFocusIncoming(focusFcHealPctCritIncoming, SE_FcHealPctCritIncoming, this, spell_id); + if (spellbonuses.CriticalRegenDecay) chance += GetDecayEffectValue(spell_id, SE_CriticalRegenDecay); - + if(chance && (MakeRandomInt(0,99) < chance)) return (value * 2); } @@ -11700,18 +11297,22 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { uint32 botid = c->GetTarget()->CastToBot()->GetBotID(); std::string errorMessage; - char* Query = 0; + int setslot = atoi(sep->arg[2]); uint8 red = atoi(sep->arg[3]); uint8 green = atoi(sep->arg[4]); uint8 blue = atoi(sep->arg[5]); uint32 setcolor = (red << 16) | (green << 8) | blue; + std::string query = StringFormat("UPDATE botinventory SET color = %u " + "WHERE slotID = %i AND botID = %u", + setcolor, setslot, botid); + auto results = database.QueryDatabase(query); + if(!results.Success()) + return; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE botinventory SET color = %u WHERE slotID = %i AND botID = %u",setcolor, setslot, botid))){ - int slotmaterial = Inventory::CalcMaterialFromSlot(setslot); - c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); - } + int slotmaterial = Inventory::CalcMaterialFromSlot(setslot); + c->GetTarget()->CastToBot()->SendWearChange(slotmaterial); } else { c->Message(15, "You must target a bot you own to do this."); @@ -11719,11 +11320,8 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { return; } // Help for coloring bot armor - if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor") ){ + if(!strcasecmp(sep->arg[1], "help") && !strcasecmp(sep->arg[2], "armorcolor") ){ //read from db - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; c->Message(0, "-----------------#bot armorcolor help-----------------------------"); c->Message(0, "Armor: 17(Chest/Robe), 7(Arms), 9(Bracer), 12(Hands), 18(Legs), 19(Boots), 2(Helm)"); @@ -16473,27 +16071,20 @@ uint32 Bot::GetEquipmentColor(uint8 material_slot) const { //Bot tints uint32 slotid = 0; - uint32 returncolor = 0; uint32 botid = this->GetBotID(); //Translate code slot # to DB slot # slotid = Inventory::CalcSlotFromMaterial(material_slot); //read from db - char* Query = 0; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::string query = StringFormat("SELECT color FROM botinventory " + "WHERE BotID = %u AND SlotID = %u", botid, slotid); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() != 1) + return 0; - if(database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT color FROM botinventory WHERE BotID = %u AND SlotID = %u", botid, slotid), 0, &DatasetResult)) { - if(mysql_num_rows(DatasetResult) == 1) { - DataRow = mysql_fetch_row(DatasetResult); - if(DataRow) - returncolor = atoul(DataRow[0]); - } - mysql_free_result(DatasetResult); - safe_delete_array(Query); - } - return returncolor; + auto row = results.begin(); + return atoul(row[0]); } int Bot::GetRawACNoShield(int &shield_ac) diff --git a/zone/bot.h b/zone/bot.h index 9c1ec9cb2..7601dd00c 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -311,11 +311,11 @@ public: virtual float GetAOERange(uint16 spell_id); virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100); virtual void DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caster_level, Mob* caster = 0); - virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr); + virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr); virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction); - virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF); + virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF); // Bot Action Command Methods bool MesmerizeTarget(Mob* target); diff --git a/zone/client.cpp b/zone/client.cpp index 345bec4ca..b8527204f 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -39,15 +39,10 @@ extern volatile bool RunLoops; #include "../common/features.h" -#include "masterentity.h" -#include "worldserver.h" #include "../common/misc.h" -#include "zonedb.h" #include "../common/spdat.h" -#include "net.h" #include "../common/packet_dump.h" #include "../common/packet_functions.h" -#include "petitions.h" #include "../common/serverinfo.h" #include "../common/zone_numbers.h" #include "../common/moremath.h" @@ -55,6 +50,12 @@ extern volatile bool RunLoops; #include "../common/breakdowns.h" #include "../common/rulesys.h" #include "../common/string_util.h" +#include "../common/data_verification.h" +#include "net.h" +#include "masterentity.h" +#include "worldserver.h" +#include "zonedb.h" +#include "petitions.h" #include "forage.h" #include "command.h" #include "string_ids.h" @@ -73,8 +74,6 @@ extern uint32 numclients; extern PetitionList petition_list; bool commandlogged; char entirecommand[255]; -extern DBAsyncFinishedQueue MTdbafq; -extern DBAsync *dbasync; Client::Client(EQStreamInterface* ieqs) : Mob("No name", // name @@ -325,7 +324,7 @@ Client::Client(EQStreamInterface* ieqs) initial_respawn_selection = 0; alternate_currency_loaded = false; - + EngagedRaidTarget = false; SavedRaidRestTimer = 0; } @@ -479,60 +478,23 @@ void Client::ReportConnectingState() { }; } -bool Client::Save(uint8 iCommitNow) { -#if 0 -// Orig. Offset: 344 / 0x00000000 -// Length: 36 / 0x00000024 - unsigned char rawData[36] = -{ - 0x0D, 0x30, 0xE1, 0x30, 0x1E, 0x10, 0x22, 0x10, 0x20, 0x10, 0x21, 0x10, 0x1C, 0x20, 0x1F, 0x10, - 0x7C, 0x10, 0x68, 0x10, 0x51, 0x10, 0x78, 0x10, 0xBD, 0x10, 0xD2, 0x10, 0xCD, 0x10, 0xD1, 0x10, - 0x01, 0x10, 0x6D, 0x10 -} ; - for (int tmp = 0;tmp <=35;tmp++){ - m_pp.unknown0256[89+tmp] = rawData[tmp]; - } -#endif - - if(!ClientDataLoaded()) - return false; - - m_pp.x = x_pos; - m_pp.y = y_pos; - m_pp.z = z_pos; - m_pp.guildrank=guildrank; - m_pp.heading = heading; - - // Temp Hack for signed values until we get the root of the problem changed over to signed... - if (m_pp.copper < 0) { m_pp.copper = 0; } - if (m_pp.silver < 0) { m_pp.silver = 0; } - if (m_pp.gold < 0) { m_pp.gold = 0; } - if (m_pp.platinum < 0) { m_pp.platinum = 0; } - if (m_pp.copper_bank < 0) { m_pp.copper_bank = 0; } - if (m_pp.silver_bank < 0) { m_pp.silver_bank = 0; } - if (m_pp.gold_bank < 0) { m_pp.gold_bank = 0; } - if (m_pp.platinum_bank < 0) { m_pp.platinum_bank = 0; } - - - int spentpoints=0; - for(int a=0;a < MAX_PP_AA_ARRAY;a++) { +bool Client::SaveAA(){ + int first_entry = 0; + std::string rquery; + /* Save Player AA */ + int spentpoints = 0; + for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { uint32 points = aa[a]->value; - if(points > HIGHEST_AA_VALUE) // Unifying this - { + if (points > HIGHEST_AA_VALUE) { aa[a]->value = HIGHEST_AA_VALUE; points = HIGHEST_AA_VALUE; } - if (points > 0) - { - SendAA_Struct* curAA = zone->FindAA(aa[a]->AA-aa[a]->value+1); - if(curAA) - { - for (int rank=0; rank::iterator RequiredLevel = AARequiredLevelAndCost.find(aa[a]->AA-aa[a]->value + 1 + rank); - - if(RequiredLevel != AARequiredLevelAndCost.end()) - { + if (points > 0) { + SendAA_Struct* curAA = zone->FindAA(aa[a]->AA - aa[a]->value + 1); + if (curAA) { + for (int rank = 0; rank::iterator RequiredLevel = AARequiredLevelAndCost.find(aa[a]->AA - aa[a]->value + 1 + rank); + if (RequiredLevel != AARequiredLevelAndCost.end()) { spentpoints += RequiredLevel->second.Cost; } else @@ -541,42 +503,72 @@ bool Client::Save(uint8 iCommitNow) { } } } - m_pp.aapoints_spent = spentpoints + m_epp.expended_aa; + for (int a = 0; a < MAX_PP_AA_ARRAY; a++) { + if (aa[a]->AA > 0 && aa[a]->value){ + if (first_entry != 1){ + rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, slot, aa_id, aa_value)" + " VALUES (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value); + first_entry = 1; + } + rquery = rquery + StringFormat(", (%u, %u, %u, %u)", character_id, a, aa[a]->AA, aa[a]->value); + } + } + auto results = database.QueryDatabase(rquery); + return true; +} +bool Client::Save(uint8 iCommitNow) { + if(!ClientDataLoaded()) + return false; + + /* Wrote current basics to PP for saves */ + m_pp.x = x_pos; + m_pp.y = y_pos; + m_pp.z = z_pos; + m_pp.guildrank = guildrank; + m_pp.heading = heading; + + /* Mana and HP */ if (GetHP() <= 0) { m_pp.cur_hp = GetMaxHP(); } - else + else { m_pp.cur_hp = GetHP(); + } m_pp.mana = cur_mana; m_pp.endurance = cur_end; + /* Save Character Currency */ + database.SaveCharacterCurrency(this->CharacterID(), &m_pp); + + /* Save Current Bind Points : Sets Instance to 0 because it is currently not implemented */ + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[0].zoneId, 0, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0, 0); /* Regular bind */ + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[4].zoneId, 0, m_pp.binds[4].x, m_pp.binds[4].y, m_pp.binds[4].z, 0, 1); /* Home Bind */ + + /* Save Character Buffs */ database.SaveBuffs(this); + /* Total Time Played */ TotalSecondsPlayed += (time(nullptr) - m_pp.lastlogin); m_pp.timePlayedMin = (TotalSecondsPlayed / 60); m_pp.RestTimer = rest_timer.GetRemainingTime() / 1000; - if(GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) + /* Save Mercs */ + if (GetMercInfo().MercTimerRemaining > RuleI(Mercs, UpkeepIntervalMS)) { GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); + } - if(GetMercTimer()->Enabled()) { + if (GetMercTimer()->Enabled()) { GetMercInfo().MercTimerRemaining = GetMercTimer()->GetRemainingTime(); } - if (GetMerc() && !dead) { - - } else { + if (!(GetMerc() && !dead)) { memset(&m_mercinfo, 0, sizeof(struct MercInfo)); } m_pp.lastlogin = time(nullptr); - if (pQueuedSaveWorkID) { - dbasync->CancelWork(pQueuedSaveWorkID); - pQueuedSaveWorkID = 0; - } if (GetPet() && !GetPet()->IsFamiliar() && GetPet()->CastToNPC()->GetPetSpellID() && !dead) { NPC *pet = GetPet()->CastToNPC(); @@ -593,56 +585,24 @@ bool Client::Save(uint8 iCommitNow) { if(tribute_timer.Enabled()) { m_pp.tribute_time_remaining = tribute_timer.GetRemainingTime(); - } else { - m_pp.tribute_time_remaining = 0xFFFFFFFF; - m_pp.tribute_active = 0; + } + else { + m_pp.tribute_time_remaining = 0xFFFFFFFF; m_pp.tribute_active = 0; } p_timers.Store(&database); -// printf("Dumping inventory on save:\n"); -// m_inv.dumpEntireInventory(); + database.SaveCharacterTribute(this->CharacterID(), &m_pp); + SaveTaskState(); /* Save Character Task */ - SaveTaskState(); - if (iCommitNow <= 1) { - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Entity; - workpt.w2_3() = GetID(); - workpt.b1() = DBA_b1_Entity_Client_Save; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF); - dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets), false); - if (iCommitNow == 0){ - pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500); - } - else { - dbasync->AddWork(&dbaw, 0); - SaveBackup(); - } - safe_delete_array(query); - return true; - } - else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv, &m_epp, 0, 0, MaxXTargets)) { - SaveBackup(); - } - else { - std::cerr << "Failed to update player profile" << std::endl; - return false; - } - - /* Mirror Character Data */ - database.StoreCharacterLookup(this->CharacterID()); + m_pp.hunger_level = EQEmu::Clamp(m_pp.hunger_level, 0, 50000); + m_pp.thirst_level = EQEmu::Clamp(m_pp.thirst_level, 0, 50000); + database.SaveCharacterData(this->CharacterID(), this->AccountID(), &m_pp, &m_epp); /* Save Character Data */ return true; } void Client::SaveBackup() { - if (!RunLoops) - return; - char* query = 0; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &DBAsyncCB_CharacterBackup, this->CharacterID(), DBAsync::Read); - dbaw->AddQuery(0, &query, MakeAnyLenString(&query, "Select id, UNIX_TIMESTAMP()-UNIX_TIMESTAMP(ts) as age from character_backup where charid=%u and backupreason=0 order by ts asc", this->CharacterID()), true); - dbasync->AddWork(&dbaw, 0); } CLIENTPACKET::CLIENTPACKET() @@ -731,14 +691,10 @@ void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req, CLIENT_CO } void Client::FastQueuePacket(EQApplicationPacket** app, bool ack_req, CLIENT_CONN_STATUS required_state) { - - //std::cout << "Sending: 0x" << std::hex << std::setw(4) << std::setfill('0') << (*app)->GetOpcode() << std::dec << ", size=" << (*app)->size << std::endl; - // if the program doesnt care about the status or if the status isnt what we requested if (required_state != CLIENT_CONNECTINGALL && client_state != required_state) { // todo: save packets for later use AddPacket(app, ack_req); -// LogFile->write(EQEMuLog::Normal, "Adding Packet to list (%d) (%d)", (*app)->GetOpcode(), (int)required_state); return; } else { @@ -1033,7 +989,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s Message(13, "Command '%s' not recognized.", message); } } else { - if(!RuleB(Chat, SuppressCommandErrors)) + if(!RuleB(Chat, SuppressCommandErrors)) Message(13, "Command '%s' not recognized.", message); } } @@ -1331,7 +1287,7 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) m_pp.ldon_points_ruj += rujpts; m_pp.ldon_points_tak += takpts; points-=splitpts; - // if anything left, recursively loop thru again + // if anything left, recursively loop thru again if (splitpts !=0) UpdateLDoNPoints(splitpts,0); break; @@ -1388,6 +1344,7 @@ bool Client::UpdateLDoNPoints(int32 points, uint32 theme) } } m_pp.ldon_points_available += points; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventurePointsUpdate, sizeof(AdventurePoints_Update_Struct)); AdventurePoints_Update_Struct* apus = (AdventurePoints_Update_Struct*)outapp->pBuffer; apus->ldon_available_points = m_pp.ldon_points_available; @@ -1409,6 +1366,8 @@ void Client::SetSkill(SkillUseTypes skillid, uint16 value) { return; m_pp.skills[skillid] = value; // We need to be able to #setskill 254 and 255 to reset skills + database.SaveCharacterSkill(this->CharacterID(), skillid, value); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; skill->skillId=skillid; @@ -1427,6 +1386,8 @@ void Client::IncreaseLanguageSkill(int skill_id, int value) { if (m_pp.languages[skill_id] > 100) //Lang skill above max m_pp.languages[skill_id] = 100; + database.SaveCharacterLanguage(this->CharacterID(), skill_id, m_pp.languages[skill_id]); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; skill->skillId = 100 + skill_id; @@ -2115,7 +2076,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper = copperpp; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } silver -= copper; @@ -2130,7 +2091,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper += (silver-(m_pp.silver*10)); if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } @@ -2150,7 +2111,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper += coppertest; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } @@ -2168,7 +2129,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { if(updateclient) SendMoneyUpdate(); RecalcWeight(); - Save(); + SaveCurrency(); return true; } } @@ -2178,32 +2139,27 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ uint64 tmp2; tmp = copper; - // Add Amount of Platinum + /* Add Amount of Platinum */ tmp2 = tmp/1000; int32 new_val = m_pp.platinum + tmp2; - if(new_val < 0) { - m_pp.platinum = 0; - } else { - m_pp.platinum = m_pp.platinum + tmp2; - } + if(new_val < 0) { m_pp.platinum = 0; } + else { m_pp.platinum = m_pp.platinum + tmp2; } tmp-=tmp2*1000; //if (updateclient) // SendClientMoneyUpdate(3,tmp2); - // Add Amount of Gold + /* Add Amount of Gold */ tmp2 = tmp/100; new_val = m_pp.gold + tmp2; - if(new_val < 0) { - m_pp.gold = 0; - } else { - m_pp.gold = m_pp.gold + tmp2; - } + if(new_val < 0) { m_pp.gold = 0; } + else { m_pp.gold = m_pp.gold + tmp2; } + tmp-=tmp2*100; //if (updateclient) // SendClientMoneyUpdate(2,tmp2); - // Add Amount of Silver + /* Add Amount of Silver */ tmp2 = tmp/10; new_val = m_pp.silver + tmp2; if(new_val < 0) { @@ -2234,13 +2190,12 @@ void Client::AddMoneyToPP(uint64 copper, bool updateclient){ RecalcWeight(); - Save(); + SaveCurrency(); LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); } -void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){ - +void Client::EVENT_ITEM_ScriptStopReturn(){ /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items This will stopgap players from items being returned if global_npc.pl has a catch all return_items */ @@ -2249,6 +2204,10 @@ void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 plat gettimeofday(&read_time, 0); sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); this->SetEntityVariable("Stop_Return", buffer); +} + +void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){ + this->EVENT_ITEM_ScriptStopReturn(); int32 new_value = m_pp.platinum + platinum; if(new_value >= 0 && new_value > m_pp.platinum) @@ -2270,7 +2229,7 @@ void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 plat SendMoneyUpdate(); RecalcWeight(); - Save(); + SaveCurrency(); #if (EQDEBUG>=5) LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have: plat:%i gold:%i silver:%i copper:%i", @@ -2352,12 +2311,14 @@ bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int cha { // the higher your current skill level, the harder it is int16 Chance = 10 + chancemodi + ((252 - skillval) / 20); - if (Chance < 1) - Chance = 1; // Make it always possible + Chance = (Chance * RuleI(Character, SkillUpModifier) / 100); Chance = mod_increase_skill_chance(Chance, against_who); + if(Chance < 1) + Chance = 1; // Make it always possible + if(MakeRandomFloat(0, 99) < Chance) { SetSkill(skillid, GetRawSkill(skillid) + 1); @@ -2807,11 +2768,6 @@ void Client::SetMaterial(int16 in_slot, uint32 item_id) { m_pp.item_material[MaterialArms] = item->Material; else if (in_slot==MainWrist1) m_pp.item_material[MaterialWrist] = item->Material; - /* - // non-live behavior - else if (in_slot==SLOT_BRACER02) - m_pp.item_material[MaterialWrist] = item->Material; - */ else if (in_slot==MainHands) m_pp.item_material[MaterialHands] = item->Material; else if (in_slot==MainLegs) @@ -3134,10 +3090,19 @@ void Client::FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType fil safe_delete(outapp); } +void Client::Tell_StringID(uint32 string_id, const char *who, const char *message) +{ + char string_id_str[10]; + snprintf(string_id_str, 10, "%d", string_id); + + Message_StringID(MT_TellEcho, TELL_QUEUED_MESSAGE, who, string_id_str, message); +} + void Client::SetTint(int16 in_slot, uint32 color) { Color_Struct new_color; new_color.color = color; SetTint(in_slot, new_color); + database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color); } // Still need to reconcile bracer01 versus bracer02 @@ -3165,6 +3130,8 @@ void Client::SetTint(int16 in_slot, Color_Struct& color) { m_pp.item_tint[MaterialLegs].color=color.color; else if (in_slot==MainFeet) m_pp.item_tint[MaterialFeet].color=color.color; + + database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color); } void Client::SetHideMe(bool flag) @@ -3199,6 +3166,7 @@ void Client::SetLanguageSkill(int langid, int value) value = 100; //Max lang value m_pp.languages[langid] = value; + database.SaveCharacterLanguage(this->CharacterID(), langid, value); EQApplicationPacket* outapp = new EQApplicationPacket(OP_SkillUpdate, sizeof(SkillUpdate_Struct)); SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer; @@ -3702,7 +3670,11 @@ void Client::LogSQL(const char *fmt, ...) { } void Client::GetGroupAAs(GroupLeadershipAA_Struct *into) const { - memcpy(into, &m_pp.leader_abilities, sizeof(GroupLeadershipAA_Struct)); + memcpy(into, &m_pp.leader_abilities.group, sizeof(GroupLeadershipAA_Struct)); +} + +void Client::GetRaidAAs(RaidLeadershipAA_Struct *into) const { + memcpy(into, &m_pp.leader_abilities.raid, sizeof(RaidLeadershipAA_Struct)); } void Client::EnteringMessages(Client* client) @@ -4064,40 +4036,27 @@ void Client::KeyRingList() bool Client::IsDiscovered(uint32 itemid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT count(*) FROM discovered_items WHERE item_id = '%lu'", itemid); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in IsDiscovered query '" << query << "' " << results.ErrorMessage() << std::endl; + return false; + } - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT count(*) FROM discovered_items WHERE item_id = '%lu'", itemid), errbuf, &result)) - { - row = mysql_fetch_row(result); - if (atoi(row[0])) - { - mysql_free_result(result); - safe_delete_array(query); - return true; - } - } - else - { - std::cerr << "Error in IsDiscovered query '" << query << "' " << errbuf << std::endl; - } - mysql_free_result(result); - safe_delete_array(query); - return false; + auto row = results.begin(); + if (!atoi(row[0])) + return false; + + return true; } void Client::DiscoverItem(uint32 itemid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO discovered_items SET item_id=%lu, char_name='%s', discovered_date=UNIX_TIMESTAMP(), account_status=%i", itemid, GetName(), Admin()), errbuf, &result)) - { - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO discovered_items " + "SET item_id = %lu, char_name = '%s', " + "discovered_date = UNIX_TIMESTAMP(), account_status = %i", + itemid, GetName(), Admin()); + auto results = database.QueryDatabase(query); parse->EventPlayer(EVENT_DISCOVER_ITEM, this, "", itemid); } @@ -4270,7 +4229,6 @@ void Client::VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber) { } void Client::ClearGroupAAs() { - for(unsigned int i = 0; i < MAX_GROUP_LEADERSHIP_AA_ARRAY; i++) m_pp.leader_abilities.ranks[i] = 0; @@ -4280,40 +4238,46 @@ void Client::ClearGroupAAs() { m_pp.raid_leadership_exp = 0; Save(); + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); } void Client::UpdateGroupAAs(int32 points, uint32 type) { - - switch(type) - { - case 0: - { - m_pp.group_leadership_points += points; - break; - } - case 1: - { - m_pp.raid_leadership_points += points; - break; - } + switch(type) { + case 0: { m_pp.group_leadership_points += points; break; } + case 1: { m_pp.raid_leadership_points += points; break; } } SendLeadershipEXPUpdate(); } -bool Client::IsLeadershipEXPOn() -{ +bool Client::IsLeadershipEXPOn() { if(!m_pp.leadAAActive) return false; Group *g = GetGroup(); - if(g && g->IsLeader(this) && (g->GroupCount() > 2)) + if (g && g->IsLeader(this) && g->GroupCount() > 2) return true; Raid *r = GetRaid(); - if(r && r->IsLeader(this) && (r->RaidCount() > 17)) + if (!r) + return false; + + // raid leaders can only gain raid AA XP + if (r->IsLeader(this)) { + if (r->RaidCount() > 17) + return true; + else + return false; + } + + uint32 gid = r->GetGroup(this); + + if (gid > 11) // not in a group + return false; + + if (r->IsGroupLeader(GetName()) && r->GroupCount(gid) > 2) return true; return false; @@ -4334,15 +4298,15 @@ void Client::IncrementAggroCount() { if(!RuleI(Character, RestRegenPercent)) return; - + // If we already had aggro before this method was called, the combat indicator should already be up for SoF clients, // so we don't need to send it again. // if(AggroCount > 1) return; - + // Pause the rest timer - if (AggroCount == 1) + if (AggroCount == 1) SavedRaidRestTimer = rest_timer.GetRemainingTime(); if(GetClientVersion() >= EQClientSoF) { @@ -4387,9 +4351,9 @@ void Client::DecrementAggroCount() { time_until_rest = RuleI(Character, RestRegenTimeToActivate) * 1000; } } - + rest_timer.Start(time_until_rest); - + if(GetClientVersion() >= EQClientSoF) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 5); @@ -4494,7 +4458,7 @@ void Client::SendRespawnBinds() int num_options = respawn_options.size(); uint32 PacketLength = 17 + (26 * num_options); //Header size + per-option invariant size - + std::list::iterator itr; RespawnOption* opt; @@ -5280,71 +5244,54 @@ const bool Client::IsMQExemptedArea(uint32 zoneID, float x, float y, float z) co void Client::SendRewards() { std::vector rewards; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT reward_id, amount FROM" - " account_rewards WHERE account_id=%i ORDER by reward_id", AccountID()), - errbuf,&result)) - { - while((row = mysql_fetch_row(result))) - { - ClientReward cr; - cr.id = atoi(row[0]); - cr.amount = atoi(row[1]); - rewards.push_back(cr); - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Client::SendRewards(): %s (%s)", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT reward_id, amount " + "FROM account_rewards " + "WHERE account_id = %i " + "ORDER BY reward_id", AccountID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Client::SendRewards(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return; - } + } - if(rewards.size() > 0) - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(InternalVeteranReward) * rewards.size())); - uchar *data = vetapp->pBuffer; - for(int i = 0; i < rewards.size(); ++i) - { - InternalVeteranReward *ivr = (InternalVeteranReward*)data; - ivr->claim_id = rewards[i].id; - ivr->number_available = rewards[i].amount; - std::list::iterator iter = zone->VeteranRewards.begin(); - while(iter != zone->VeteranRewards.end()) - { - if((*iter).claim_id == rewards[i].id) - { - break; - } - ++iter; - } + for (auto row = results.begin(); row != results.end(); ++row) { + ClientReward cr; + cr.id = atoi(row[0]); + cr.amount = atoi(row[1]); + rewards.push_back(cr); + } - if(iter != zone->VeteranRewards.end()) - { - InternalVeteranReward ivro = (*iter); - ivr->claim_count = ivro.claim_count; - for(int x = 0; x < ivro.claim_count; ++x) - { - ivr->items[x].item_id = ivro.items[x].item_id; - ivr->items[x].charges = ivro.items[x].charges; - strcpy(ivr->items[x].item_name, ivro.items[x].item_name); - } - } + if(rewards.size() == 0) + return; - data += sizeof(InternalVeteranReward); - } - FastQueuePacket(&vetapp); - } + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(InternalVeteranReward) * rewards.size())); + uchar *data = vetapp->pBuffer; + for(int i = 0; i < rewards.size(); ++i) { + InternalVeteranReward *ivr = (InternalVeteranReward*)data; + ivr->claim_id = rewards[i].id; + ivr->number_available = rewards[i].amount; + auto iter = zone->VeteranRewards.begin(); + for (;iter != zone->VeteranRewards.end(); ++iter) + if((*iter).claim_id == rewards[i].id) + break; + + if(iter != zone->VeteranRewards.end()) { + InternalVeteranReward ivro = (*iter); + ivr->claim_count = ivro.claim_count; + for(int x = 0; x < ivro.claim_count; ++x) { + ivr->items[x].item_id = ivro.items[x].item_id; + ivr->items[x].charges = ivro.items[x].charges; + strcpy(ivr->items[x].item_name, ivro.items[x].item_name); + } + } + + data += sizeof(InternalVeteranReward); + } + + FastQueuePacket(&vetapp); } -bool Client::TryReward(uint32 claim_id) -{ +bool Client::TryReward(uint32 claim_id) { //Make sure we have an open spot //Make sure we have it in our acct and count > 0 //Make sure the entry was found @@ -5354,143 +5301,90 @@ bool Client::TryReward(uint32 claim_id) //save uint32 free_slot = 0xFFFFFFFF; - for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) - { + for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) { ItemInst *item = GetInv().GetItem(i); - if(!item) - { + if(!item) { free_slot = i; break; } } if(free_slot == 0xFFFFFFFF) - { return false; - } char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 amt = 0; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT amount FROM" - " account_rewards WHERE account_id=%i AND reward_id=%i", AccountID(), claim_id), - errbuf,&result)) - { - row = mysql_fetch_row(result); - if(row) - { - amt = atoi(row[0]); - } - else - { - mysql_free_result(result); - safe_delete_array(query); - return false; - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT amount FROM account_rewards " + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + uint32 amt = atoi(row[0]); if(amt == 0) - { return false; - } std::list::iterator iter = zone->VeteranRewards.begin(); - while(iter != zone->VeteranRewards.end()) - { + for (; iter != zone->VeteranRewards.end(); ++row) if((*iter).claim_id == claim_id) - { break; - } - ++iter; - } if(iter == zone->VeteranRewards.end()) - { return false; - } - if(amt == 1) - { - if(!database.RunQuery(query,MakeAnyLenString(&query,"DELETE FROM" - " account_rewards WHERE account_id=%i AND reward_id=%i", AccountID(), claim_id), - errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query, errbuf); - safe_delete_array(query); - } - else - { - safe_delete_array(query); - } + if(amt == 1) { + query = StringFormat("DELETE FROM account_rewards " + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); } - else - { - if(!database.RunQuery(query,MakeAnyLenString(&query,"UPDATE account_rewards SET amount=(amount-1)" - " WHERE account_id=%i AND reward_id=%i", AccountID(), claim_id), - errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query, errbuf); - safe_delete_array(query); - } - else - { - safe_delete_array(query); - } + else { + query = StringFormat("UPDATE account_rewards SET amount = (amount-1) " + "WHERE account_id = %i AND reward_id = %i", + AccountID(), claim_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in Client::TryReward(): %s (%s)", query.c_str(), results.ErrorMessage().c_str()); } InternalVeteranReward ivr = (*iter); ItemInst *claim = database.CreateItem(ivr.items[0].item_id, ivr.items[0].charges); - if(claim) - { - bool lore_conflict = false; - if(CheckLoreConflict(claim->GetItem())) - { - lore_conflict = true; - } - - for(int y = 1; y < 8; y++) - { - if(ivr.items[y].item_id) - { - if(claim->GetItem()->ItemClass == 1) - { - ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges); - if(item_temp) - { - if(CheckLoreConflict(item_temp->GetItem())) - { - lore_conflict = true; - DuplicateLoreMessage(ivr.items[y].item_id); - } - claim->PutItem(y-1, *item_temp); - } - } - } - } - - if(lore_conflict) - { - safe_delete(claim); - return true; - } - else - { - PutItemInInventory(free_slot, *claim); - SendItemPacket(free_slot, claim, ItemPacketTrade); - } + if(!claim) { + Save(); + return true; } + bool lore_conflict = CheckLoreConflict(claim->GetItem()); + + for(int y = 1; y < 8; y++) + if(ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) { + ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges); + if(item_temp) { + if(CheckLoreConflict(item_temp->GetItem())) { + lore_conflict = true; + DuplicateLoreMessage(ivr.items[y].item_id); + } + claim->PutItem(y-1, *item_temp); + } + } + + if(lore_conflict) { + safe_delete(claim); + return true; + } + + PutItemInInventory(free_slot, *claim); + SendItemPacket(free_slot, claim, ItemPacketTrade); + Save(); return true; } @@ -5718,7 +5612,7 @@ void Client::AddCrystals(uint32 Radiant, uint32 Ebon) m_pp.currentEbonCrystals += Ebon; m_pp.careerEbonCrystals += Ebon; - Save(); + SaveCurrency(); SendCrystalCounts(); } @@ -7674,7 +7568,7 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui tmpValue = current_value + mod + npc_value[i]; int16 FactionModPct = spellbonuses.FactionModPct + itembonuses.FactionModPct + aabonuses.FactionModPct; - tmpValue += (tmpValue * FactionModPct) / 100; + tmpValue += (tmpValue * FactionModPct) / 100; // Make sure faction hits don't go to GMs... if (m_pp.gm==1 && (tmpValue < current_value)) { @@ -7849,41 +7743,31 @@ void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalval void Client::LoadAccountFlags() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; accountflags.clear(); - MakeAnyLenString(&query, "SELECT p_flag, p_value FROM account_flags WHERE p_accid = '%d'", account_id); - if(database.RunQuery(query, strlen(query), errbuf, &result)) - { - while(row = mysql_fetch_row(result)) - { - std::string fname(row[0]); - std::string fval(row[1]); - accountflags[fname] = fval; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in LoadAccountFlags query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("SELECT p_flag, p_value " + "FROM account_flags WHERE p_accid = '%d'", + account_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in LoadAccountFlags query '" << query << "' " << results.ErrorMessage() << std::endl; + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + accountflags[row[0]] = row[1]; } -void Client::SetAccountFlag(std::string flag, std::string val) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; +void Client::SetAccountFlag(std::string flag, std::string val) { - MakeAnyLenString(&query, "REPLACE INTO account_flags (p_accid, p_flag, p_value) VALUES( '%d', '%s', '%s')", account_id, flag.c_str(), val.c_str()); - if(!database.RunQuery(query, strlen(query), errbuf)) - { - std::cerr << "Error in SetAccountFlags query '" << query << "' " << errbuf << std::endl; + std::string query = StringFormat("REPLACE INTO account_flags (p_accid, p_flag, p_value) " + "VALUES( '%d', '%s', '%s')", + account_id, flag.c_str(), val.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + std::cerr << "Error in SetAccountFlags query '" << query << "' " << results.ErrorMessage() << std::endl; + return; } - safe_delete_array(query); accountflags[flag] = val; } @@ -7988,7 +7872,7 @@ void Client::TryItemTimer(int slot) } ++it_iter; } - + if(slot > EmuConstants::EQUIPMENT_END) { return; } @@ -8036,8 +7920,9 @@ void Client::RefundAA() { } if(refunded) { + SaveAA(); Save(); - Kick(); + // Kick(); } } @@ -8052,7 +7937,7 @@ void Client::IncrementAA(int aa_id) { SetAA(aa_id, GetAA(aa_id) + 1); - Save(); + SaveAA(); SendAA(aa_id); SendAATable(); @@ -8293,30 +8178,26 @@ void Client::PlayMP3(const char* fname) } void Client::ExpeditionSay(const char *str, int ExpID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (!database.RunQuery(query,MakeAnyLenString(&query, "SELECT `player_name` FROM `cust_inst_players` WHERE `inst_id` = %i", ExpID),errbuf,&result)){ - safe_delete_array(query); + std::string query = StringFormat("SELECT `player_name` FROM `cust_inst_players` " + "WHERE `inst_id` = %i", ExpID); + auto results = database.QueryDatabase(query); + if (!results.Success()) return; + + if(results.RowCount() == 0) { + this->Message(14, "You say to the expedition, '%s'", str); + return; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + const char* charName = row[0]; + if(strcmp(charName, this->GetCleanName()) != 0) + worldserver.SendEmoteMessage(charName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); + // ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); } - safe_delete_array(query); - if(result) - this->Message(14, "You say to the expedition, '%s'", str); - - while((row = mysql_fetch_row(result))) { - const char* CharName = row[0]; - if(strcmp(CharName, this->GetCleanName()) != 0) - worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str); - // ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); - } - - mysql_free_result(result); - } void Client::ShowNumHits() @@ -8346,3 +8227,18 @@ float Client::GetQuiverHaste() quiver_haste = 1.0f / (1.0f + static_cast(quiver_haste) / 100.0f); return quiver_haste; } + +void Client::SendColoredText(uint32 color, std::string message) +{ + // arbitrary size limit + if (message.size() > 512) // live does send this with empty strings sometimes ... + return; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ColoredText, + sizeof(ColoredText_Struct) + message.size()); + ColoredText_Struct *cts = (ColoredText_Struct *)outapp->pBuffer; + cts->color = color; + strcpy(cts->msg, message.c_str()); + QueuePacket(outapp); + safe_delete(outapp); +} + diff --git a/zone/client.h b/zone/client.h index 697e4e2d3..e31c15598 100644 --- a/zone/client.h +++ b/zone/client.h @@ -37,6 +37,7 @@ class Client; #include "../common/item_struct.h" #include "../common/clientversions.h" +#include "common.h" #include "zonedb.h" #include "errno.h" #include "mob.h" @@ -102,11 +103,6 @@ enum { //scribing argument to MemorizeSpell memSpellSpellbar = 3 }; -#define USE_ITEM_SPELL_SLOT 10 -#define POTION_BELT_SPELL_SLOT 11 -#define DISCIPLINE_SPELL_SLOT 10 -#define ABILITY_SPELL_SLOT 9 - //Modes for the zoning state of the client. typedef enum { ZoneToSafeCoords, // Always send ZonePlayerToBind_Struct to client: Succor/Evac @@ -240,8 +236,6 @@ public: bool KeyRingCheck(uint32 item_id); void KeyRingList(); virtual bool IsClient() const { return true; } - virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw); - bool FinishConnState2(DBAsyncWork* dbaw); void CompleteConnect(); bool TryStacking(ItemInst* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true); void SendTraderPacket(Client* trader, uint32 Unknown72 = 51); @@ -260,6 +254,8 @@ public: const char *message5 = nullptr, const char *message6 = nullptr, const char *message7 = nullptr, const char *message8 = nullptr, const char *message9 = nullptr); + void Tell_StringID(uint32 string_id, const char *who, const char *message); + void SendColoredText(uint32 color, std::string message); void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice); void SendTraderItem(uint32 item_id,uint16 quantity); uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity); @@ -315,6 +311,10 @@ public: bool Save(uint8 iCommitNow); // 0 = delayed, 1=async now, 2=sync now void SaveBackup(); + /* New PP Save Functions */ + bool SaveCurrency(){ return database.SaveCharacterCurrency(this->CharacterID(), &m_pp); } + bool SaveAA(); + inline bool ClientDataLoaded() const { return client_data_loaded; } inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } @@ -559,6 +559,9 @@ public: void SendLeadershipEXPUpdate(); bool IsLeadershipEXPOn(); inline int GetLeadershipAA(int AAID) { return m_pp.leader_abilities.ranks[AAID]; } + inline LeadershipAA_Struct &GetLeadershipAA() { return m_pp.leader_abilities; } + inline GroupLeadershipAA_Struct &GetGroupLeadershipAA() { return m_pp.leader_abilities.group; } + inline RaidLeadershipAA_Struct &GetRaidLeadershipAA() { return m_pp.leader_abilities.raid; } int GroupLeadershipAAHealthEnhancement(); int GroupLeadershipAAManaEnhancement(); int GroupLeadershipAAHealthRegeneration(); @@ -585,6 +588,7 @@ public: bool CheckLoreConflict(const Item_Struct* item); void ChangeLastName(const char* in_lastname); void GetGroupAAs(GroupLeadershipAA_Struct *into) const; + void GetRaidAAs(RaidLeadershipAA_Struct *into) const; void ClearGroupAAs(); void UpdateGroupAAs(int32 points, uint32 type); void SacrificeConfirm(Client* caster); @@ -657,12 +661,12 @@ public: void OnDisconnect(bool hard_disconnect); - uint16 GetSkillPoints() {return m_pp.points;} - void SetSkillPoints(int inp) {m_pp.points = inp;} + uint16 GetSkillPoints() { return m_pp.points;} + void SetSkillPoints(int inp) { m_pp.points = inp;} void IncreaseSkill(int skill_id, int value = 1) { if (skill_id <= HIGHEST_SKILL) { m_pp.skills[skill_id] += value; } } void IncreaseLanguageSkill(int skill_id, int value = 1); - virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0)? m_pp.skills[skill_id]*(100 + itembonuses.skillmod[skill_id])/100 : m_pp.skills[skill_id]); } return 0; } + virtual uint16 GetSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return((itembonuses.skillmod[skill_id] > 0) ? m_pp.skills[skill_id] * (100 + itembonuses.skillmod[skill_id]) / 100 : m_pp.skills[skill_id]); } return 0; } uint32 GetRawSkill(SkillUseTypes skill_id) const { if (skill_id <= HIGHEST_SKILL) { return(m_pp.skills[skill_id]); } return 0; } bool HasSkill(SkillUseTypes skill_id) const; bool CanHaveSkill(SkillUseTypes skill_id) const; @@ -681,7 +685,7 @@ public: inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_); - void TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid); + void TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid); void SendTradeskillDetails(uint32 recipe_id); bool TradeskillExecute(DBTradeskillRecipe_Struct *spec); void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, SkillUseTypes tradeskill); @@ -785,6 +789,7 @@ public: int16 acmod(); // Item methods + void EVENT_ITEM_ScriptStopReturn(); uint32 NukeItem(uint32 itemnum, uint8 where_to_check = (invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor)); void SetTint(int16 slot_id, uint32 color); @@ -895,7 +900,8 @@ public: //This is used to later set the buff duration of the spell, in slot to duration. //Doesn't appear to work directly after the client recieves an action packet. - void SendBuffDurationPacket(uint16 spell_id, int duration, int inlevel); + void SendBuffDurationPacket(Buffs_Struct &buff); + void SendBuffNumHitPacket(Buffs_Struct &buff, int slot); void ProcessInspectRequest(Client* requestee, Client* requester); bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); } @@ -1154,6 +1160,7 @@ public: const char* GetClassPlural(Client* client); void SendWebLink(const char* website); void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg); + void SendSpellAnim(uint16 targetid, uint16 spell_id); void DuplicateLoreMessage(uint32 ItemID); void GarbleMessage(char *, uint8); @@ -1449,7 +1456,7 @@ private: unsigned int RestRegenHP; unsigned int RestRegenMana; unsigned int RestRegenEndurance; - + bool EngagedRaidTarget; uint32 SavedRaidRestTimer; diff --git a/zone/client_logs.cpp b/zone/client_logs.cpp index e51a51996..7ce3d5aab 100644 --- a/zone/client_logs.cpp +++ b/zone/client_logs.cpp @@ -33,14 +33,12 @@ void ClientLogs::subscribe(EQEMuLog::LogIDs id, Client *c) { if(c == nullptr) return; - //make sure they arnt allready subscribed. - std::vector::iterator cur,end; cur = entries[id].begin(); end = entries[id].end(); for(; cur != end; ++cur) { if(*cur == c) { - printf("%s was allready subscribed to %d\n", c->GetName(), id); + printf("%s was already subscribed to %d\n", c->GetName(), id); return; } } @@ -100,6 +98,7 @@ void ClientLogs::msg(EQEMuLog::LogIDs id, const char *buf) { for(; cur != end; ++cur) { if(!(*cur)->InZone()) continue; + (*cur)->Message(CLIENT_LOG_CHANNEL, buf); } } diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index ba8eae47e..2fb7ad9ca 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -1341,12 +1341,45 @@ int16 Client::CalcCHA() { return(CHA); } -int Client::CalcHaste() { - int h = spellbonuses.haste + spellbonuses.hastetype2; +int Client::CalcHaste() +{ + /* Tests: (based on results in newer char window) + * 68 v1 + 46 item + 25 over + 35 inhib = 204% + * 46 item + 5 v2 + 25 over + 35 inhib = 65% + * 68 v1 + 46 item + 5 v2 + 25 over + 35 inhib = 209% + * 75% slow + 35 inhib = 25% + * 35 inhib = 65% + * 75% slow = 25% + * Conclusions: + * the bigger effect in slow v. inhib wins + * slow negates all other hastes + * inhib will only negate all other hastes if you don't have v1 (ex. VQ) + */ + // slow beats all! Besides a better inhibit + if (spellbonuses.haste < 0) { + if (-spellbonuses.haste <= spellbonuses.inhibitmelee) + Haste = 100 - spellbonuses.inhibitmelee; + else + Haste = 100 + spellbonuses.haste; + return Haste; + } + + // No haste and inhibit, kills all other hastes + if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee) { + Haste = 100 - spellbonuses.inhibitmelee; + return Haste; + } + + int h = 0; int cap = 0; - int overhaste = 0; int level = GetLevel(); + // we know we have a haste spell and not slowed, no extra inhibit melee checks needed + if (spellbonuses.haste) + h += spellbonuses.haste - spellbonuses.inhibitmelee; + if (spellbonuses.hastetype2 && level > 49) // type 2 is capped at 10% and only available to 50+ + h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2; + // 26+ no cap, 1-25 10 if (level > 25) // 26+ h += itembonuses.haste; @@ -1368,24 +1401,16 @@ int Client::CalcHaste() { // 51+ 25 (despite there being higher spells...), 1-50 10 if (level > 50) // 51+ - overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; else // 1-50 - overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; - h += overhaste; h += ExtraHaste; //GM granted haste. h = mod_client_haste(h); - if (spellbonuses.inhibitmelee) { - if (h >= 0) - h -= spellbonuses.inhibitmelee; - else - h -= ((100 + h) * spellbonuses.inhibitmelee / 100); - } - - Haste = h; - return(Haste); + Haste = 100 + h; + return Haste; } //The AA multipliers are set to be 5, but were 2 on WR diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 261615169..efc8a5d8a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -48,6 +48,7 @@ #include "../common/guilds.h" #include "../common/rulesys.h" #include "../common/spdat.h" +#include "../common/data_verification.h" #include "petitions.h" #include "npc_ai.h" #include "../common/skills.h" @@ -77,9 +78,6 @@ extern volatile bool ZoneLoaded; extern WorldServer worldserver; extern PetitionList petition_list; extern EntityList entity_list; -extern DBAsyncFinishedQueue MTdbafq; -extern DBAsync *dbasync; - typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); //Use a map for connecting opcodes since it dosent get used a lot and is sparse @@ -87,312 +85,314 @@ std::map ConnectingOpcodes; //Use a static array for connected, for speed ClientPacketProc ConnectedOpcodes[_maxEmuOpcode]; -void MapOpcodes() { +void MapOpcodes() +{ ConnectingOpcodes.clear(); memset(ConnectedOpcodes, 0, sizeof(ConnectedOpcodes)); - //Now put all the opcodes into their home... - //Begin Connecting opcodes: - ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; - ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; - ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; - ConnectingOpcodes[OP_ReqClientSpawn] = &Client::Handle_Connect_OP_ReqClientSpawn; - ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; - ConnectingOpcodes[OP_WorldObjectsSent] = &Client::Handle_Connect_OP_WorldObjectsSent; - ConnectingOpcodes[OP_ZoneComplete] = &Client::Handle_Connect_OP_ZoneComplete; - ConnectingOpcodes[OP_ReqNewZone] = &Client::Handle_Connect_OP_ReqNewZone; - ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; - ConnectingOpcodes[OP_WearChange] = &Client::Handle_Connect_OP_WearChange; - ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; - ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; + // Now put all the opcodes into their home... + // connecting opcode handler assignments: ConnectingOpcodes[OP_ApproveZone] = &Client::Handle_Connect_OP_ApproveZone; - ConnectingOpcodes[OP_TGB] = &Client::Handle_Connect_OP_TGB; - ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; - ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; - ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; - ConnectingOpcodes[OP_UpdateAA] = &Client::Handle_Connect_OP_UpdateAA; ConnectingOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectingOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectingOpcodes[OP_ClientError] = &Client::Handle_Connect_OP_ClientError; + ConnectingOpcodes[OP_ClientReady] = &Client::Handle_Connect_OP_ClientReady; + ConnectingOpcodes[OP_ClientUpdate] = &Client::Handle_Connect_OP_ClientUpdate; + ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; // temporary hack + ConnectingOpcodes[OP_ReqClientSpawn] = &Client::Handle_Connect_OP_ReqClientSpawn; + ConnectingOpcodes[OP_ReqNewZone] = &Client::Handle_Connect_OP_ReqNewZone; + ConnectingOpcodes[OP_SendAAStats] = &Client::Handle_Connect_OP_SendAAStats; + ConnectingOpcodes[OP_SendAATable] = &Client::Handle_Connect_OP_SendAATable; + ConnectingOpcodes[OP_SendExpZonein] = &Client::Handle_Connect_OP_SendExpZonein; + ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; + ConnectingOpcodes[OP_SendGuildTributes] = &Client::Handle_Connect_OP_SendGuildTributes; // I guess it didn't believe us with the first assignment? + ConnectingOpcodes[OP_SendTributes] = &Client::Handle_Connect_OP_SendTributes; + ConnectingOpcodes[OP_SetServerFilter] = &Client::Handle_Connect_OP_SetServerFilter; + ConnectingOpcodes[OP_SpawnAppearance] = &Client::Handle_Connect_OP_SpawnAppearance; + ConnectingOpcodes[OP_TGB] = &Client::Handle_Connect_OP_TGB; + ConnectingOpcodes[OP_UpdateAA] = &Client::Handle_Connect_OP_UpdateAA; + ConnectingOpcodes[OP_WearChange] = &Client::Handle_Connect_OP_WearChange; + ConnectingOpcodes[OP_WorldObjectsSent] = &Client::Handle_Connect_OP_WorldObjectsSent; ConnectingOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; -//temporary hack: - ConnectingOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; + ConnectingOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectingOpcodes[OP_ZoneComplete] = &Client::Handle_Connect_OP_ZoneComplete; + ConnectingOpcodes[OP_ZoneEntry] = &Client::Handle_Connect_OP_ZoneEntry; - //Begin Connected opcodes: - ConnectedOpcodes[OP_ClientUpdate] = &Client::Handle_OP_ClientUpdate; - ConnectedOpcodes[OP_AutoAttack] = &Client::Handle_OP_AutoAttack; - ConnectedOpcodes[OP_AutoAttack2] = &Client::Handle_OP_AutoAttack2; - ConnectedOpcodes[OP_Consent] = &Client::Handle_OP_Consent; - ConnectedOpcodes[OP_ConsentDeny] = &Client::Handle_OP_ConsentDeny; - ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; - ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; - ConnectedOpcodes[OP_Shielding] = &Client::Handle_OP_Shielding; - ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; - ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; - ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; - ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton; - ConnectedOpcodes[OP_LeaveAdventure] = &Client::Handle_OP_LeaveAdventure; - ConnectedOpcodes[OP_Consume] = &Client::Handle_OP_Consume; - ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; - ConnectedOpcodes[OP_AdventureMerchantRequest] = &Client::Handle_OP_AdventureMerchantRequest; - ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase; - ConnectedOpcodes[OP_ConsiderCorpse] = &Client::Handle_OP_ConsiderCorpse; - ConnectedOpcodes[OP_Consider] = &Client::Handle_OP_Consider; - ConnectedOpcodes[OP_Begging] = &Client::Handle_OP_Begging; - ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; - ConnectedOpcodes[OP_Surname] = &Client::Handle_OP_Surname; - ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; - ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; - ConnectedOpcodes[OP_Assist] = &Client::Handle_OP_Assist; - ConnectedOpcodes[OP_AssistGroup] = &Client::Handle_OP_AssistGroup; - ConnectedOpcodes[OP_GMTraining] = &Client::Handle_OP_GMTraining; - ConnectedOpcodes[OP_GMEndTraining] = &Client::Handle_OP_GMEndTraining; - ConnectedOpcodes[OP_GMTrainSkill] = &Client::Handle_OP_GMTrainSkill; - ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse; - ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2; - ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; - ConnectedOpcodes[OP_SpawnAppearance] = &Client::Handle_OP_SpawnAppearance; - ConnectedOpcodes[OP_BazaarInspect] = &Client::Handle_OP_BazaarInspect; - ConnectedOpcodes[OP_Death] = &Client::Handle_OP_Death; - ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin; - ConnectedOpcodes[OP_ItemLinkClick] = &Client::Handle_OP_ItemLinkClick; - ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; - ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem; - ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp; - ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout; - ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen; - ConnectedOpcodes[OP_LDoNSenseTraps] = &Client::Handle_OP_LDoNSenseTraps; - ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps; - ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect; - ConnectedOpcodes[OP_LDoNPickLock] = &Client::Handle_OP_LDoNPickLock; - ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; - ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak; - ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; - ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; - ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; - ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; - ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn; - ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; - ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; - ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; - ConnectedOpcodes[OP_GMZoneRequest] = &Client::Handle_OP_GMZoneRequest; - ConnectedOpcodes[OP_GMZoneRequest2] = &Client::Handle_OP_GMZoneRequest2; - ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; - ConnectedOpcodes[OP_LootRequest] = &Client::Handle_OP_LootRequest; - ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; - ConnectedOpcodes[OP_LootItem] = &Client::Handle_OP_LootItem; - ConnectedOpcodes[OP_GuildDelete] = &Client::Handle_OP_GuildDelete; - ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; - ConnectedOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; - ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; - ConnectedOpcodes[OP_GuildPeace] = &Client::Handle_OP_GuildPeace; - ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; - ConnectedOpcodes[OP_GuildLeader] = &Client::Handle_OP_GuildLeader; - ConnectedOpcodes[OP_GuildDemote] = &Client::Handle_OP_GuildDemote; - ConnectedOpcodes[OP_GuildPromote] = &Client::Handle_OP_GuildPromote; - ConnectedOpcodes[OP_GuildInvite] = &Client::Handle_OP_GuildInvite; - ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; - ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; - ConnectedOpcodes[OP_GuildManageBanker] = &Client::Handle_OP_GuildManageBanker; - ConnectedOpcodes[OP_GuildInviteAccept] = &Client::Handle_OP_GuildInviteAccept; - ConnectedOpcodes[OP_ManaChange] = &Client::Handle_OP_ManaChange; - ConnectedOpcodes[OP_MemorizeSpell] = &Client::Handle_OP_MemorizeSpell; - ConnectedOpcodes[OP_SwapSpell] = &Client::Handle_OP_SwapSpell; - ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; - ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem; - ConnectedOpcodes[OP_CombatAbility] = &Client::Handle_OP_CombatAbility; - ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; - ConnectedOpcodes[OP_InstillDoubt] = &Client::Handle_OP_InstillDoubt; - ConnectedOpcodes[OP_RezzAnswer] = &Client::Handle_OP_RezzAnswer; - ConnectedOpcodes[OP_GMSummon] = &Client::Handle_OP_GMSummon; - ConnectedOpcodes[OP_TradeRequest] = &Client::Handle_OP_TradeRequest; - ConnectedOpcodes[OP_TradeRequestAck] = &Client::Handle_OP_TradeRequestAck; - ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; - ConnectedOpcodes[OP_TradeAcceptClick] = &Client::Handle_OP_TradeAcceptClick; - ConnectedOpcodes[OP_BoardBoat] = &Client::Handle_OP_BoardBoat; - ConnectedOpcodes[OP_LeaveBoat] = &Client::Handle_OP_LeaveBoat; - ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; - ConnectedOpcodes[OP_Buff] = &Client::Handle_OP_Buff; - ConnectedOpcodes[OP_GMHideMe] = &Client::Handle_OP_GMHideMe; - ConnectedOpcodes[OP_GMNameChange] = &Client::Handle_OP_GMNameChange; - ConnectedOpcodes[OP_GMKill] = &Client::Handle_OP_GMKill; - ConnectedOpcodes[OP_GMLastName] = &Client::Handle_OP_GMLastName; - ConnectedOpcodes[OP_GMToggle] = &Client::Handle_OP_GMToggle; - ConnectedOpcodes[OP_LFGCommand] = &Client::Handle_OP_LFGCommand; - ConnectedOpcodes[OP_GMGoto] = &Client::Handle_OP_GMGoto; - ConnectedOpcodes[OP_Trader] = &Client::Handle_OP_Trader; - ConnectedOpcodes[OP_TraderShop] = &Client::Handle_OP_TraderShop; - ConnectedOpcodes[OP_ShopRequest] = &Client::Handle_OP_ShopRequest; - ConnectedOpcodes[OP_BazaarSearch] = &Client::Handle_OP_BazaarSearch; - ConnectedOpcodes[OP_ShopPlayerBuy] = &Client::Handle_OP_ShopPlayerBuy; - ConnectedOpcodes[OP_ShopPlayerSell] = &Client::Handle_OP_ShopPlayerSell; - ConnectedOpcodes[OP_ShopEnd] = &Client::Handle_OP_ShopEnd; - ConnectedOpcodes[OP_ClickObjectAction] = &Client::Handle_OP_ClickObjectAction; - ConnectedOpcodes[OP_ClickObject] = &Client::Handle_OP_ClickObject; - ConnectedOpcodes[OP_RecipesFavorite] = &Client::Handle_OP_RecipesFavorite; - ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; - ConnectedOpcodes[OP_RecipeDetails] = &Client::Handle_OP_RecipeDetails; - ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; - ConnectedOpcodes[OP_TradeSkillCombine] = &Client::Handle_OP_TradeSkillCombine; - ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; - ConnectedOpcodes[OP_AugmentItem] = &Client::Handle_OP_AugmentItem; - ConnectedOpcodes[OP_ClickDoor] = &Client::Handle_OP_ClickDoor; - ConnectedOpcodes[OP_GroundSpawn] = &Client::Handle_OP_CreateObject; - ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; - ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; - ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; - ConnectedOpcodes[OP_GroupAcknowledge] = &Client::Handle_OP_GroupAcknowledge; - ConnectedOpcodes[OP_GroupCancelInvite] = &Client::Handle_OP_GroupCancelInvite; - ConnectedOpcodes[OP_GroupFollow] = &Client::Handle_OP_GroupFollow; - ConnectedOpcodes[OP_GroupFollow2] = &Client::Handle_OP_GroupFollow2; - ConnectedOpcodes[OP_GroupDisband] = &Client::Handle_OP_GroupDisband; - ConnectedOpcodes[OP_GroupDelete] = &Client::Handle_OP_GroupDelete; - ConnectedOpcodes[OP_GMEmoteZone] = &Client::Handle_OP_GMEmoteZone; - ConnectedOpcodes[OP_InspectRequest] = &Client::Handle_OP_InspectRequest; - ConnectedOpcodes[OP_InspectAnswer] = &Client::Handle_OP_InspectAnswer; - ConnectedOpcodes[OP_InspectMessageUpdate] = &Client::Handle_OP_InspectMessageUpdate; - ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell; - ConnectedOpcodes[OP_PetitionBug] = &Client::Handle_OP_PetitionBug; - ConnectedOpcodes[OP_Bug] = &Client::Handle_OP_Bug; - ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition; - ConnectedOpcodes[OP_PetitionCheckIn] = &Client::Handle_OP_PetitionCheckIn; - ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; - ConnectedOpcodes[OP_PetitionDelete] = &Client::Handle_OP_PetitionDelete; - ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands; - ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; - ConnectedOpcodes[OP_PetitionQue] = &Client::Handle_OP_PetitionQue; - ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition; - ConnectedOpcodes[OP_PetitionCheckout] = &Client::Handle_OP_PetitionCheckout; - ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; - ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; - ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; - ConnectedOpcodes[OP_Animation] = &Client::Handle_OP_Animation; - ConnectedOpcodes[OP_SetServerFilter] = &Client::Handle_OP_SetServerFilter; - ConnectedOpcodes[OP_GMDelCorpse] = &Client::Handle_OP_GMDelCorpse; - ConnectedOpcodes[OP_GMKick] = &Client::Handle_OP_GMKick; - ConnectedOpcodes[OP_GMServers] = &Client::Handle_OP_GMServers; - ConnectedOpcodes[OP_Illusion] = &Client::Handle_OP_Illusion; - ConnectedOpcodes[OP_GMBecomeNPC] = &Client::Handle_OP_GMBecomeNPC; - ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing; - ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; - ConnectedOpcodes[OP_Mend] = &Client::Handle_OP_Mend; - ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; - ConnectedOpcodes[OP_Damage] = &Client::Handle_OP_Damage; - ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; - ConnectedOpcodes[OP_TraderBuy] = &Client::Handle_OP_TraderBuy; - ConnectedOpcodes[OP_GMFind] = &Client::Handle_OP_GMFind; - ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; - ConnectedOpcodes[OP_Bind_Wound] = &Client::Handle_OP_Bind_Wound; - ConnectedOpcodes[OP_TrackTarget] = &Client::Handle_OP_TrackTarget; - ConnectedOpcodes[OP_Track] = &Client::Handle_OP_Track; - ConnectedOpcodes[OP_TrackUnknown] = &Client::Handle_OP_TrackUnknown; + // connected opcode handler assignments: ConnectedOpcodes[OP_0x0193] = &Client::Handle_0x0193; - ConnectedOpcodes[OP_ClientError] = &Client::Handle_OP_ClientError; - ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; - ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; - ConnectedOpcodes[OP_Split] = &Client::Handle_OP_Split; - ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; - ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps; - ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster; - ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster; - ConnectedOpcodes[OP_TributeItem] = &Client::Handle_OP_TributeItem; - ConnectedOpcodes[OP_TributeMoney] = &Client::Handle_OP_TributeMoney; - ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; - ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; - ConnectedOpcodes[OP_TributeToggle] = &Client::Handle_OP_TributeToggle; - ConnectedOpcodes[OP_TributeNPC] = &Client::Handle_OP_TributeNPC; - ConnectedOpcodes[OP_ConfirmDelete] = &Client::Handle_OP_ConfirmDelete; - ConnectedOpcodes[OP_CrashDump] = &Client::Handle_OP_CrashDump; - ConnectedOpcodes[OP_ControlBoat] = &Client::Handle_OP_ControlBoat; - ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; - ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; - ConnectedOpcodes[OP_SafeFallSuccess] = &Client::Handle_OP_SafeFallSuccess; - ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; - ConnectedOpcodes[OP_SafePoint] = &Client::Handle_OP_SafePoint; - ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; - ConnectedOpcodes[OP_BankerChange] = &Client::Handle_OP_BankerChange; - ConnectedOpcodes[OP_LeadershipExpToggle] = &Client::Handle_OP_LeadershipExpToggle; - ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; - ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; - ConnectedOpcodes[OP_SetTitle] = &Client::Handle_OP_SetTitle; - ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_LoadSpellSet] = &Client::Handle_OP_LoadSpellSet; - ConnectedOpcodes[OP_AutoFire] = &Client::Handle_OP_AutoFire; - ConnectedOpcodes[OP_Rewind] = &Client::Handle_OP_Rewind; - ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; - ConnectedOpcodes[OP_Translocate] = &Client::Handle_OP_Translocate; - ConnectedOpcodes[OP_Sacrifice] = &Client::Handle_OP_Sacrifice; + ConnectedOpcodes[OP_AAAction] = &Client::Handle_OP_AAAction; ConnectedOpcodes[OP_AcceptNewTask] = &Client::Handle_OP_AcceptNewTask; - ConnectedOpcodes[OP_CancelTask] = &Client::Handle_OP_CancelTask; - ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; - ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing; - ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; - ConnectedOpcodes[OP_Bandolier] = &Client::Handle_OP_Bandolier; - ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; - ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; - ConnectedOpcodes[OP_LFGGetMatchesRequest] = &Client::Handle_OP_LFGGetMatchesRequest; - ConnectedOpcodes[OP_LFPCommand] = &Client::Handle_OP_LFPCommand; - ConnectedOpcodes[OP_LFPGetMatchesRequest] = &Client::Handle_OP_LFPGetMatchesRequest; - ConnectedOpcodes[OP_Barter] = &Client::Handle_OP_Barter; - ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; - ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility; - ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; - ConnectedOpcodes[OP_DelegateAbility] = &Client::Handle_OP_DelegateAbility; - ConnectedOpcodes[OP_ApplyPoison] = &Client::Handle_OP_ApplyPoison; - ConnectedOpcodes[OP_AugmentInfo] = &Client::Handle_OP_AugmentInfo; - ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; - ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; - ConnectedOpcodes[OP_RespawnWindow] = &Client::Handle_OP_RespawnWindow; - ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; - ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; + ConnectedOpcodes[OP_AdventureInfoRequest] = &Client::Handle_OP_AdventureInfoRequest; ConnectedOpcodes[OP_AdventureLeaderboardRequest] = &Client::Handle_OP_AdventureLeaderboardRequest; - ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; - ConnectedOpcodes[OP_SetStartCity] = &Client::Handle_OP_SetStartCity; - ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; - ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; - ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; - ConnectedOpcodes[OP_GMSearchCorpse] = &Client::Handle_OP_GMSearchCorpse; - ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; - ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; - ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; - ConnectedOpcodes[OP_TradeBusy] = &Client::Handle_OP_TradeBusy; - ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; - ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; - ConnectedOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; - ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; - ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; - ConnectedOpcodes[OP_BuffRemoveRequest] = &Client::Handle_OP_BuffRemoveRequest; - ConnectedOpcodes[OP_CorpseDrag] = &Client::Handle_OP_CorpseDrag; - ConnectedOpcodes[OP_CorpseDrop] = &Client::Handle_OP_CorpseDrop; - ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; - ConnectedOpcodes[OP_GuildCreate] = &Client::Handle_OP_GuildCreate; + ConnectedOpcodes[OP_AdventureMerchantPurchase] = &Client::Handle_OP_AdventureMerchantPurchase; + ConnectedOpcodes[OP_AdventureMerchantRequest] = &Client::Handle_OP_AdventureMerchantRequest; + ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell; + ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest; + ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest; ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest; - ConnectedOpcodes[OP_AltCurrencySellSelection] = &Client::Handle_OP_AltCurrencySellSelection; ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase; ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim; ConnectedOpcodes[OP_AltCurrencySell] = &Client::Handle_OP_AltCurrencySell; - ConnectedOpcodes[OP_CrystalReclaim] = &Client::Handle_OP_CrystalReclaim; - ConnectedOpcodes[OP_CrystalCreate] = &Client::Handle_OP_CrystalCreate; - ConnectedOpcodes[OP_LFGuild] = &Client::Handle_OP_LFGuild; - ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; - ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; - ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; - ConnectedOpcodes[OP_MercenaryDataRequest] = &Client::Handle_OP_MercenaryDataRequest; - ConnectedOpcodes[OP_MercenaryHire] = &Client::Handle_OP_MercenaryHire; - ConnectedOpcodes[OP_MercenaryCommand] = &Client::Handle_OP_MercenaryCommand; - ConnectedOpcodes[OP_MercenaryDataUpdateRequest] = &Client::Handle_OP_MercenaryDataUpdateRequest; - ConnectedOpcodes[OP_MercenarySuspendRequest] = &Client::Handle_OP_MercenarySuspendRequest; - ConnectedOpcodes[OP_MercenaryDismiss] = &Client::Handle_OP_MercenaryDismiss; - ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest; - ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory; - ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer; + ConnectedOpcodes[OP_AltCurrencySellSelection] = &Client::Handle_OP_AltCurrencySellSelection; + ConnectedOpcodes[OP_Animation] = &Client::Handle_OP_Animation; + ConnectedOpcodes[OP_ApplyPoison] = &Client::Handle_OP_ApplyPoison; + ConnectedOpcodes[OP_Assist] = &Client::Handle_OP_Assist; + ConnectedOpcodes[OP_AssistGroup] = &Client::Handle_OP_AssistGroup; + ConnectedOpcodes[OP_AugmentInfo] = &Client::Handle_OP_AugmentInfo; + ConnectedOpcodes[OP_AugmentItem] = &Client::Handle_OP_AugmentItem; + ConnectedOpcodes[OP_AutoAttack] = &Client::Handle_OP_AutoAttack; + ConnectedOpcodes[OP_AutoAttack2] = &Client::Handle_OP_AutoAttack2; + ConnectedOpcodes[OP_AutoFire] = &Client::Handle_OP_AutoFire; + ConnectedOpcodes[OP_Bandolier] = &Client::Handle_OP_Bandolier; + ConnectedOpcodes[OP_BankerChange] = &Client::Handle_OP_BankerChange; + ConnectedOpcodes[OP_Barter] = &Client::Handle_OP_Barter; + ConnectedOpcodes[OP_BazaarInspect] = &Client::Handle_OP_BazaarInspect; + ConnectedOpcodes[OP_BazaarSearch] = &Client::Handle_OP_BazaarSearch; + ConnectedOpcodes[OP_Begging] = &Client::Handle_OP_Begging; + ConnectedOpcodes[OP_Bind_Wound] = &Client::Handle_OP_Bind_Wound; + ConnectedOpcodes[OP_BlockedBuffs] = &Client::Handle_OP_BlockedBuffs; + ConnectedOpcodes[OP_BoardBoat] = &Client::Handle_OP_BoardBoat; + ConnectedOpcodes[OP_Buff] = &Client::Handle_OP_Buff; + ConnectedOpcodes[OP_BuffRemoveRequest] = &Client::Handle_OP_BuffRemoveRequest; + ConnectedOpcodes[OP_Bug] = &Client::Handle_OP_Bug; + ConnectedOpcodes[OP_Camp] = &Client::Handle_OP_Camp; + ConnectedOpcodes[OP_CancelTask] = &Client::Handle_OP_CancelTask; + ConnectedOpcodes[OP_CancelTrade] = &Client::Handle_OP_CancelTrade; + ConnectedOpcodes[OP_CastSpell] = &Client::Handle_OP_CastSpell; + ConnectedOpcodes[OP_ChannelMessage] = &Client::Handle_OP_ChannelMessage; + ConnectedOpcodes[OP_ClearBlockedBuffs] = &Client::Handle_OP_ClearBlockedBuffs; + ConnectedOpcodes[OP_ClearNPCMarks] = &Client::Handle_OP_ClearNPCMarks; + ConnectedOpcodes[OP_ClearSurname] = &Client::Handle_OP_ClearSurname; + ConnectedOpcodes[OP_ClickDoor] = &Client::Handle_OP_ClickDoor; + ConnectedOpcodes[OP_ClickObject] = &Client::Handle_OP_ClickObject; + ConnectedOpcodes[OP_ClickObjectAction] = &Client::Handle_OP_ClickObjectAction; + ConnectedOpcodes[OP_ClientError] = &Client::Handle_OP_ClientError; ConnectedOpcodes[OP_ClientTimeStamp] = &Client::Handle_OP_ClientTimeStamp; + ConnectedOpcodes[OP_ClientUpdate] = &Client::Handle_OP_ClientUpdate; + ConnectedOpcodes[OP_CombatAbility] = &Client::Handle_OP_CombatAbility; + ConnectedOpcodes[OP_ConfirmDelete] = &Client::Handle_OP_ConfirmDelete; + ConnectedOpcodes[OP_Consent] = &Client::Handle_OP_Consent; + ConnectedOpcodes[OP_ConsentDeny] = &Client::Handle_OP_ConsentDeny; + ConnectedOpcodes[OP_Consider] = &Client::Handle_OP_Consider; + ConnectedOpcodes[OP_ConsiderCorpse] = &Client::Handle_OP_ConsiderCorpse; + ConnectedOpcodes[OP_Consume] = &Client::Handle_OP_Consume; + ConnectedOpcodes[OP_ControlBoat] = &Client::Handle_OP_ControlBoat; + ConnectedOpcodes[OP_CorpseDrag] = &Client::Handle_OP_CorpseDrag; + ConnectedOpcodes[OP_CorpseDrop] = &Client::Handle_OP_CorpseDrop; + ConnectedOpcodes[OP_CrashDump] = &Client::Handle_OP_CrashDump; + ConnectedOpcodes[OP_CrystalCreate] = &Client::Handle_OP_CrystalCreate; + ConnectedOpcodes[OP_CrystalReclaim] = &Client::Handle_OP_CrystalReclaim; + ConnectedOpcodes[OP_Damage] = &Client::Handle_OP_Damage; + ConnectedOpcodes[OP_Death] = &Client::Handle_OP_Death; + ConnectedOpcodes[OP_DelegateAbility] = &Client::Handle_OP_DelegateAbility; + ConnectedOpcodes[OP_DeleteItem] = &Client::Handle_OP_DeleteItem; + ConnectedOpcodes[OP_DeleteSpawn] = &Client::Handle_OP_DeleteSpawn; + ConnectedOpcodes[OP_DeleteSpell] = &Client::Handle_OP_DeleteSpell; + ConnectedOpcodes[OP_DisarmTraps] = &Client::Handle_OP_DisarmTraps; + ConnectedOpcodes[OP_DoGroupLeadershipAbility] = &Client::Handle_OP_DoGroupLeadershipAbility; + ConnectedOpcodes[OP_DuelResponse] = &Client::Handle_OP_DuelResponse; + ConnectedOpcodes[OP_DuelResponse2] = &Client::Handle_OP_DuelResponse2; + ConnectedOpcodes[OP_DumpName] = &Client::Handle_OP_DumpName; + ConnectedOpcodes[OP_Dye] = &Client::Handle_OP_Dye; + ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; + ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; + ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; + ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; + ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; + ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; + ConnectedOpcodes[OP_Fishing] = &Client::Handle_OP_Fishing; + ConnectedOpcodes[OP_FloatListThing] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_Forage] = &Client::Handle_OP_Forage; + ConnectedOpcodes[OP_FriendsWho] = &Client::Handle_OP_FriendsWho; + ConnectedOpcodes[OP_GetGuildMOTD] = &Client::Handle_OP_GetGuildMOTD; + ConnectedOpcodes[OP_GetGuildsList] = &Client::Handle_OP_GetGuildsList; + ConnectedOpcodes[OP_GMBecomeNPC] = &Client::Handle_OP_GMBecomeNPC; + ConnectedOpcodes[OP_GMDelCorpse] = &Client::Handle_OP_GMDelCorpse; + ConnectedOpcodes[OP_GMEmoteZone] = &Client::Handle_OP_GMEmoteZone; + ConnectedOpcodes[OP_GMEndTraining] = &Client::Handle_OP_GMEndTraining; + ConnectedOpcodes[OP_GMFind] = &Client::Handle_OP_GMFind; + ConnectedOpcodes[OP_GMGoto] = &Client::Handle_OP_GMGoto; + ConnectedOpcodes[OP_GMHideMe] = &Client::Handle_OP_GMHideMe; + ConnectedOpcodes[OP_GMKick] = &Client::Handle_OP_GMKick; + ConnectedOpcodes[OP_GMKill] = &Client::Handle_OP_GMKill; + ConnectedOpcodes[OP_GMLastName] = &Client::Handle_OP_GMLastName; + ConnectedOpcodes[OP_GMNameChange] = &Client::Handle_OP_GMNameChange; + ConnectedOpcodes[OP_GMSearchCorpse] = &Client::Handle_OP_GMSearchCorpse; + ConnectedOpcodes[OP_GMServers] = &Client::Handle_OP_GMServers; + ConnectedOpcodes[OP_GMSummon] = &Client::Handle_OP_GMSummon; + ConnectedOpcodes[OP_GMToggle] = &Client::Handle_OP_GMToggle; + ConnectedOpcodes[OP_GMTraining] = &Client::Handle_OP_GMTraining; + ConnectedOpcodes[OP_GMTrainSkill] = &Client::Handle_OP_GMTrainSkill; + ConnectedOpcodes[OP_GMZoneRequest] = &Client::Handle_OP_GMZoneRequest; + ConnectedOpcodes[OP_GMZoneRequest2] = &Client::Handle_OP_GMZoneRequest2; + ConnectedOpcodes[OP_GroundSpawn] = &Client::Handle_OP_CreateObject; + ConnectedOpcodes[OP_GroupAcknowledge] = &Client::Handle_OP_GroupAcknowledge; + ConnectedOpcodes[OP_GroupCancelInvite] = &Client::Handle_OP_GroupCancelInvite; + ConnectedOpcodes[OP_GroupDelete] = &Client::Handle_OP_GroupDelete; + ConnectedOpcodes[OP_GroupDisband] = &Client::Handle_OP_GroupDisband; + ConnectedOpcodes[OP_GroupFollow] = &Client::Handle_OP_GroupFollow; + ConnectedOpcodes[OP_GroupFollow2] = &Client::Handle_OP_GroupFollow2; + ConnectedOpcodes[OP_GroupInvite] = &Client::Handle_OP_GroupInvite; + ConnectedOpcodes[OP_GroupInvite2] = &Client::Handle_OP_GroupInvite2; + ConnectedOpcodes[OP_GroupMakeLeader] = &Client::Handle_OP_GroupMakeLeader; + ConnectedOpcodes[OP_GroupMentor] = &Client::Handle_OP_GroupMentor; + ConnectedOpcodes[OP_GroupRoles] = &Client::Handle_OP_GroupRoles; + ConnectedOpcodes[OP_GroupUpdate] = &Client::Handle_OP_GroupUpdate; + ConnectedOpcodes[OP_GuildBank] = &Client::Handle_OP_GuildBank; + ConnectedOpcodes[OP_GuildCreate] = &Client::Handle_OP_GuildCreate; + ConnectedOpcodes[OP_GuildDelete] = &Client::Handle_OP_GuildDelete; + ConnectedOpcodes[OP_GuildDemote] = &Client::Handle_OP_GuildDemote; + ConnectedOpcodes[OP_GuildInvite] = &Client::Handle_OP_GuildInvite; + ConnectedOpcodes[OP_GuildInviteAccept] = &Client::Handle_OP_GuildInviteAccept; + ConnectedOpcodes[OP_GuildLeader] = &Client::Handle_OP_GuildLeader; + ConnectedOpcodes[OP_GuildManageBanker] = &Client::Handle_OP_GuildManageBanker; + ConnectedOpcodes[OP_GuildPeace] = &Client::Handle_OP_GuildPeace; + ConnectedOpcodes[OP_GuildPromote] = &Client::Handle_OP_GuildPromote; + ConnectedOpcodes[OP_GuildPublicNote] = &Client::Handle_OP_GuildPublicNote; + ConnectedOpcodes[OP_GuildRemove] = &Client::Handle_OP_GuildRemove; + ConnectedOpcodes[OP_GuildStatus] = &Client::Handle_OP_GuildStatus; + ConnectedOpcodes[OP_GuildUpdateURLAndChannel] = &Client::Handle_OP_GuildUpdateURLAndChannel; + ConnectedOpcodes[OP_GuildWar] = &Client::Handle_OP_GuildWar; + ConnectedOpcodes[OP_Heartbeat] = &Client::Handle_OP_Heartbeat; + ConnectedOpcodes[OP_Hide] = &Client::Handle_OP_Hide; + ConnectedOpcodes[OP_HideCorpse] = &Client::Handle_OP_HideCorpse; + ConnectedOpcodes[OP_Illusion] = &Client::Handle_OP_Illusion; + ConnectedOpcodes[OP_InspectAnswer] = &Client::Handle_OP_InspectAnswer; + ConnectedOpcodes[OP_InspectMessageUpdate] = &Client::Handle_OP_InspectMessageUpdate; + ConnectedOpcodes[OP_InspectRequest] = &Client::Handle_OP_InspectRequest; + ConnectedOpcodes[OP_InstillDoubt] = &Client::Handle_OP_InstillDoubt; + ConnectedOpcodes[OP_ItemLinkClick] = &Client::Handle_OP_ItemLinkClick; + ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; + ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; + ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; + ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; + ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; + ConnectedOpcodes[OP_KeyRing] = &Client::Handle_OP_KeyRing; + ConnectedOpcodes[OP_LDoNButton] = &Client::Handle_OP_LDoNButton; + ConnectedOpcodes[OP_LDoNDisarmTraps] = &Client::Handle_OP_LDoNDisarmTraps; + ConnectedOpcodes[OP_LDoNInspect] = &Client::Handle_OP_LDoNInspect; + ConnectedOpcodes[OP_LDoNOpen] = &Client::Handle_OP_LDoNOpen; + ConnectedOpcodes[OP_LDoNPickLock] = &Client::Handle_OP_LDoNPickLock; + ConnectedOpcodes[OP_LDoNSenseTraps] = &Client::Handle_OP_LDoNSenseTraps; + ConnectedOpcodes[OP_LeadershipExpToggle] = &Client::Handle_OP_LeadershipExpToggle; + ConnectedOpcodes[OP_LeaveAdventure] = &Client::Handle_OP_LeaveAdventure; + ConnectedOpcodes[OP_LeaveBoat] = &Client::Handle_OP_LeaveBoat; + ConnectedOpcodes[OP_LFGCommand] = &Client::Handle_OP_LFGCommand; + ConnectedOpcodes[OP_LFGGetMatchesRequest] = &Client::Handle_OP_LFGGetMatchesRequest; + ConnectedOpcodes[OP_LFGuild] = &Client::Handle_OP_LFGuild; + ConnectedOpcodes[OP_LFPCommand] = &Client::Handle_OP_LFPCommand; + ConnectedOpcodes[OP_LFPGetMatchesRequest] = &Client::Handle_OP_LFPGetMatchesRequest; + ConnectedOpcodes[OP_LoadSpellSet] = &Client::Handle_OP_LoadSpellSet; + ConnectedOpcodes[OP_Logout] = &Client::Handle_OP_Logout; + ConnectedOpcodes[OP_LootItem] = &Client::Handle_OP_LootItem; + ConnectedOpcodes[OP_LootRequest] = &Client::Handle_OP_LootRequest; + ConnectedOpcodes[OP_ManaChange] = &Client::Handle_OP_ManaChange; + ConnectedOpcodes[OP_MemorizeSpell] = &Client::Handle_OP_MemorizeSpell; + ConnectedOpcodes[OP_Mend] = &Client::Handle_OP_Mend; + ConnectedOpcodes[OP_MercenaryCommand] = &Client::Handle_OP_MercenaryCommand; + ConnectedOpcodes[OP_MercenaryDataRequest] = &Client::Handle_OP_MercenaryDataRequest; + ConnectedOpcodes[OP_MercenaryDataUpdateRequest] = &Client::Handle_OP_MercenaryDataUpdateRequest; + ConnectedOpcodes[OP_MercenaryDismiss] = &Client::Handle_OP_MercenaryDismiss; + ConnectedOpcodes[OP_MercenaryHire] = &Client::Handle_OP_MercenaryHire; + ConnectedOpcodes[OP_MercenarySuspendRequest] = &Client::Handle_OP_MercenarySuspendRequest; + ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest; + ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin; + ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem; + ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer; + ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster; + ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory; + ConnectedOpcodes[OP_OpenTributeMaster] = &Client::Handle_OP_OpenTributeMaster; + ConnectedOpcodes[OP_PDeletePetition] = &Client::Handle_OP_PDeletePetition; + ConnectedOpcodes[OP_PetCommands] = &Client::Handle_OP_PetCommands; + ConnectedOpcodes[OP_Petition] = &Client::Handle_OP_Petition; + ConnectedOpcodes[OP_PetitionBug] = &Client::Handle_OP_PetitionBug; + ConnectedOpcodes[OP_PetitionCheckIn] = &Client::Handle_OP_PetitionCheckIn; + ConnectedOpcodes[OP_PetitionCheckout] = &Client::Handle_OP_PetitionCheckout; + ConnectedOpcodes[OP_PetitionDelete] = &Client::Handle_OP_PetitionDelete; + ConnectedOpcodes[OP_PetitionQue] = &Client::Handle_OP_PetitionQue; + ConnectedOpcodes[OP_PetitionRefresh] = &Client::Handle_OP_PetitionRefresh; + ConnectedOpcodes[OP_PetitionResolve] = &Client::Handle_OP_PetitionResolve; + ConnectedOpcodes[OP_PetitionUnCheckout] = &Client::Handle_OP_PetitionUnCheckout; + ConnectedOpcodes[OP_PickPocket] = &Client::Handle_OP_PickPocket; + ConnectedOpcodes[OP_PopupResponse] = &Client::Handle_OP_PopupResponse; + ConnectedOpcodes[OP_PotionBelt] = &Client::Handle_OP_PotionBelt; + ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA; + ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest; + ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; + ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; + ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; + ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; + ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; + ConnectedOpcodes[OP_RecipeDetails] = &Client::Handle_OP_RecipeDetails; + ConnectedOpcodes[OP_RecipesFavorite] = &Client::Handle_OP_RecipesFavorite; + ConnectedOpcodes[OP_RecipesSearch] = &Client::Handle_OP_RecipesSearch; + ConnectedOpcodes[OP_ReloadUI] = &Client::Handle_OP_ReloadUI; + ConnectedOpcodes[OP_RemoveBlockedBuffs] = &Client::Handle_OP_RemoveBlockedBuffs; + ConnectedOpcodes[OP_Report] = &Client::Handle_OP_Report; + ConnectedOpcodes[OP_RequestDuel] = &Client::Handle_OP_RequestDuel; + ConnectedOpcodes[OP_RequestTitles] = &Client::Handle_OP_RequestTitles; + ConnectedOpcodes[OP_RespawnWindow] = &Client::Handle_OP_RespawnWindow; + ConnectedOpcodes[OP_Rewind] = &Client::Handle_OP_Rewind; + ConnectedOpcodes[OP_RezzAnswer] = &Client::Handle_OP_RezzAnswer; + ConnectedOpcodes[OP_Sacrifice] = &Client::Handle_OP_Sacrifice; + ConnectedOpcodes[OP_SafeFallSuccess] = &Client::Handle_OP_SafeFallSuccess; + ConnectedOpcodes[OP_SafePoint] = &Client::Handle_OP_SafePoint; + ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save; + ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq; + ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute; + ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps; + ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD; + ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode; + ConnectedOpcodes[OP_SetServerFilter] = &Client::Handle_OP_SetServerFilter; + ConnectedOpcodes[OP_SetStartCity] = &Client::Handle_OP_SetStartCity; + ConnectedOpcodes[OP_SetTitle] = &Client::Handle_OP_SetTitle; + ConnectedOpcodes[OP_Shielding] = &Client::Handle_OP_Shielding; + ConnectedOpcodes[OP_ShopEnd] = &Client::Handle_OP_ShopEnd; + ConnectedOpcodes[OP_ShopPlayerBuy] = &Client::Handle_OP_ShopPlayerBuy; + ConnectedOpcodes[OP_ShopPlayerSell] = &Client::Handle_OP_ShopPlayerSell; + ConnectedOpcodes[OP_ShopRequest] = &Client::Handle_OP_ShopRequest; + ConnectedOpcodes[OP_Sneak] = &Client::Handle_OP_Sneak; + ConnectedOpcodes[OP_SpawnAppearance] = &Client::Handle_OP_SpawnAppearance; + ConnectedOpcodes[OP_Split] = &Client::Handle_OP_Split; + ConnectedOpcodes[OP_Surname] = &Client::Handle_OP_Surname; + ConnectedOpcodes[OP_SwapSpell] = &Client::Handle_OP_SwapSpell; + ConnectedOpcodes[OP_TargetCommand] = &Client::Handle_OP_TargetCommand; + ConnectedOpcodes[OP_TargetMouse] = &Client::Handle_OP_TargetMouse; + ConnectedOpcodes[OP_TaskHistoryRequest] = &Client::Handle_OP_TaskHistoryRequest; + ConnectedOpcodes[OP_Taunt] = &Client::Handle_OP_Taunt; + ConnectedOpcodes[OP_TestBuff] = &Client::Handle_OP_TestBuff; + ConnectedOpcodes[OP_TGB] = &Client::Handle_OP_TGB; + ConnectedOpcodes[OP_Track] = &Client::Handle_OP_Track; + ConnectedOpcodes[OP_TrackTarget] = &Client::Handle_OP_TrackTarget; + ConnectedOpcodes[OP_TrackUnknown] = &Client::Handle_OP_TrackUnknown; + ConnectedOpcodes[OP_TradeAcceptClick] = &Client::Handle_OP_TradeAcceptClick; + ConnectedOpcodes[OP_TradeBusy] = &Client::Handle_OP_TradeBusy; + ConnectedOpcodes[OP_Trader] = &Client::Handle_OP_Trader; + ConnectedOpcodes[OP_TraderBuy] = &Client::Handle_OP_TraderBuy; + ConnectedOpcodes[OP_TradeRequest] = &Client::Handle_OP_TradeRequest; + ConnectedOpcodes[OP_TradeRequestAck] = &Client::Handle_OP_TradeRequestAck; + ConnectedOpcodes[OP_TraderShop] = &Client::Handle_OP_TraderShop; + ConnectedOpcodes[OP_TradeSkillCombine] = &Client::Handle_OP_TradeSkillCombine; + ConnectedOpcodes[OP_Translocate] = &Client::Handle_OP_Translocate; + ConnectedOpcodes[OP_TributeItem] = &Client::Handle_OP_TributeItem; + ConnectedOpcodes[OP_TributeMoney] = &Client::Handle_OP_TributeMoney; + ConnectedOpcodes[OP_TributeNPC] = &Client::Handle_OP_TributeNPC; + ConnectedOpcodes[OP_TributeToggle] = &Client::Handle_OP_TributeToggle; + ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; + ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; + ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; + ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; + ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; + ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; + ConnectedOpcodes[OP_XTargetAutoAddHaters] = &Client::Handle_OP_XTargetAutoAddHaters; + ConnectedOpcodes[OP_XTargetRequest] = &Client::Handle_OP_XTargetRequest; + ConnectedOpcodes[OP_YellForHelp] = &Client::Handle_OP_YellForHelp; + ConnectedOpcodes[OP_ZoneChange] = &Client::Handle_OP_ZoneChange; } -void ClearMappedOpcode(EmuOpcode op) { +void ClearMappedOpcode(EmuOpcode op) +{ if(op >= _maxEmuOpcode) return; @@ -403,6 +403,7 @@ void ClearMappedOpcode(EmuOpcode op) { } } +// client methods int Client::HandlePacket(const EQApplicationPacket *app) { if(is_log_enabled(CLIENT__NET_IN_TRACE)) { @@ -498,121 +499,526 @@ int Client::HandlePacket(const EQApplicationPacket *app) return(true); } -void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) +// Finish client connecting state +void Client::CompleteConnect() { - if(app->size != sizeof(ClientZoneEntry_Struct)) - return; - ClientZoneEntry_Struct *cze = (ClientZoneEntry_Struct *) app->pBuffer; + UpdateWho(); + client_state = CLIENT_CONNECTED; - if(strlen(cze->char_name) > 63) - return; + hpupdate_timer.Start(); + position_timer.Start(); + autosave_timer.Start(); + SetDuelTarget(0); + SetDueling(false); - conn_state = ReceivedZoneEntry; + EnteringMessages(this); + LoadZoneFlags(); + /* Sets GM Flag if needed & Sends Petition Queue */ + UpdateAdmin(false); - ClientVersion = Connection()->ClientVersion(); - ClientVersionBit = 1 << (ClientVersion - 1); - - // Antighost code - // tmp var is so the search doesnt find this object - Client* client = entity_list.GetClientByName(cze->char_name); - if (!zone->GetAuth(ip, cze->char_name, &WID, &account_id, &character_id, &admin, lskey, &tellsoff)) { - LogFile->write(EQEMuLog::Error, "GetAuth() returned false kicking client"); - if (client != 0) + if (IsInAGuild()){ + uint8 rank = GuildRank(); + if (GetClientVersion() >= EQClientRoF) { - client->Save(); - client->Kick(); + 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 + default: { break; } // GUILD_NONE + } } - //ret = false; // TODO: Can we tell the client to get lost in a good way - client_state = CLIENT_KICKED; + SendAppearancePacket(AT_GuildID, GuildID(), false); + SendAppearancePacket(AT_GuildRank, rank, false); + } + for (uint32 spellInt = 0; spellInt < MAX_PP_REF_SPELLBOOK; spellInt++) { + if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) + m_pp.spell_book[spellInt] = 0xFFFFFFFF; + } + //SendAATable(); + + if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients"); + + uint32 raidid = database.GetRaidID(GetName()); + Raid *raid = nullptr; + if (raidid > 0){ + raid = entity_list.GetRaidByID(raidid); + if (!raid){ + raid = new Raid(raidid); + if (raid->GetID() != 0){ + entity_list.AddRaid(raid, raidid); + raid->LoadLeadership(); // Recreating raid in new zone, get leadership from DB + } + else + raid = nullptr; + } + if (raid){ + SetRaidGrouped(true); + raid->LearnMembers(); + raid->VerifyRaid(); + raid->GetRaidDetails(); + /* + Only leader should get this; send to all for now till + I figure out correct creation; can probably also send a no longer leader packet for non leaders + but not important for now. + */ + raid->SendRaidCreate(this); + raid->SendMakeLeaderPacketTo(raid->leadername, this); + raid->SendRaidAdd(GetName(), this); + raid->SendBulkRaid(this); + raid->SendGroupUpdate(this); + raid->SendRaidMOTD(this); + if (raid->IsLeader(this)) { // We're a raid leader, lets update just in case! + raid->UpdateRaidAAs(); + raid->SendAllRaidLeadershipAA(); + } + uint32 grpID = raid->GetGroup(GetName()); + if (grpID < 12){ + raid->SendRaidGroupRemove(GetName(), grpID); + raid->SendRaidGroupAdd(GetName(), grpID); + raid->CheckGroupMentor(grpID, this); + if (raid->IsGroupLeader(GetName())) { // group leader same thing! + raid->UpdateGroupAAs(raid->GetGroup(this)); + raid->GroupUpdate(grpID, false); + } + } + raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ... + if (raid->IsLocked()) + raid->SendRaidLockTo(this); + } + } + + //bulk raid send in here eventually + + //reapply some buffs + uint32 buff_count = GetMaxTotalSlots(); + for (uint32 j1 = 0; j1 < buff_count; j1++) { + 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: + case SE_Illusion: { + if (spell.base[x1] == -1) { + if (gender == 1) + gender = 0; + else if (gender == 0) + gender = 1; + SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); + } + else if (spell.base[x1] == -2) + { + if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) + SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); + } + else if (spell.max[x1] > 0) + { + SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); + } + else + { + SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); + } + switch (spell.base[x1]){ + case OGRE: + SendAppearancePacket(AT_Size, 9); + break; + case TROLL: + SendAppearancePacket(AT_Size, 8); + break; + case VAHSHIR: + case BARBARIAN: + SendAppearancePacket(AT_Size, 7); + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + SendAppearancePacket(AT_Size, 5); + break; + case DWARF: + SendAppearancePacket(AT_Size, 4); + break; + case HALFLING: + case GNOME: + SendAppearancePacket(AT_Size, 3); + break; + default: + SendAppearancePacket(AT_Size, 6); + break; + } + break; + } + case SE_SummonHorse: { + SummonHorse(buffs[j1].spellid); + //hasmount = true; //this was false, is that the correct thing? + break; + } + case SE_Silence: + { + Silence(true); + break; + } + case SE_Amnesia: + { + Amnesia(true); + break; + } + case SE_DivineAura: + { + invulnerable = true; + break; + } + case SE_Invisibility2: + case SE_Invisibility: + { + invisible = true; + SendAppearancePacket(AT_Invis, 1); + break; + } + case SE_Levitate: + { + if (!zone->CanLevitate()) + { + if (!GetGM()) + { + SendAppearancePacket(AT_Levitate, 0); + BuffFadeByEffect(SE_Levitate); + Message(13, "You can't levitate in this zone."); + } + } + else{ + SendAppearancePacket(AT_Levitate, 2); + } + break; + } + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = true; + break; + } + case SE_InvisVsAnimals: + { + invisible_animals = true; + break; + } + case SE_AddMeleeProc: + case SE_WeaponProc: + { + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_DefensiveProc: + { + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + case SE_RangedProc: + { + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); + break; + } + } + } + } + + /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ + entity_list.SendZoneAppearance(this); + /* Sends the Nimbus particle effects (up to 3) for any mob using them */ + entity_list.SendNimbusEffects(this); + + entity_list.SendUntargetable(this); + + int x; + for (x = 0; x < 8; x++) + SendWearChange(x); + Mob *pet = GetPet(); + if (pet != nullptr) { + for (x = 0; x < 8; x++) + pet->SendWearChange(x); + } + + entity_list.SendTraders(this); + + zoneinpacket_timer.Start(); + + if (GetPet()){ + GetPet()->SendPetBuffsToClient(); + } + + if (GetGroup()) + database.RefreshGroupFromDB(this); + + if (RuleB(TaskSystem, EnableTaskSystem)) + TaskPeriodic_Timer.Start(); + else + TaskPeriodic_Timer.Disable(); + + conn_state = ClientConnectFinished; + + //enforce some rules.. + if (!CanBeInZone()) { + _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); + GoToSafeCoords(database.GetZoneID("arena"), 0); return; } - strcpy(name, cze->char_name); - if (client != 0) { - struct in_addr ghost_addr; - ghost_addr.s_addr = eqs->GetRemoteIP(); + if (zone) + zone->weatherSend(); - LogFile->write(EQEMuLog::Error,"Ghosting client: Account ID:%i Name:%s Character:%s IP:%s", - client->AccountID(), client->AccountName(), client->GetName(), inet_ntoa(ghost_addr)); - client->Save(); - client->Disconnect(); + TotalKarma = database.GetKarma(AccountID()); + SendDisciplineTimers(); + + parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); + + /* This sub event is for if a player logs in for the first time since entering world. */ + if (firstlogon == 1){ + parse->EventPlayer(EVENT_CONNECT, this, "", 0); + /* QS: PlayerLogConnectDisconnect */ + if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ + std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); + } } - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Entity; - workpt.w2_3() = GetID(); - workpt.b1() = DBA_b1_Entity_Client_InfoForLogin; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read); - dbaw->AddQuery(1, &query, MakeAnyLenString(&query, - "SELECT status,name,lsaccount_id,gmspeed,revoked,hideme,time_creation FROM account WHERE id=%i", - account_id)); - //DO NOT FORGET TO EDIT ZoneDatabase::GetCharacterInfoForLogin if you change this - dbaw->AddQuery(2, &query, MakeAnyLenString(&query, - "SELECT id,profile,zonename,x,y,z,guild_id,rank,extprofile,class,level,lfp,lfg,instanceid,xtargets,firstlogon" - " FROM character_ LEFT JOIN guild_members ON id=char_id WHERE id=%i", - character_id)); - dbaw->AddQuery(3, &query, MakeAnyLenString(&query, - "SELECT faction_id,current_value FROM faction_values WHERE temp = 0 AND char_id = %i", - character_id)); - if (!(pDBAsyncWorkID = dbasync->AddWork(&dbaw))) { - safe_delete(dbaw); - LogFile->write(EQEMuLog::Error,"dbasync->AddWork() returned false, client crash"); - client_state = CLIENT_KICKED; + if (zone) { + if (zone->GetInstanceTimer()) { + uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); + uint32 day = (ttime / 86400000); + uint32 hour = (ttime / 3600000) % 24; + uint32 minute = (ttime / 60000) % 60; + uint32 second = (ttime / 1000) % 60; + if (day) { + Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); + } + else if (hour) { + Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); + } + else if (minute) { + Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), minute, second); + } + else { + Message(15, "%s(%u) will expire in in %u seconds.", + zone->GetLongName(), zone->GetInstanceID(), second); + } + } + } + + SendRewards(); + SendAltCurrencies(); + database.LoadAltCurrencyValues(CharacterID(), alternate_currency); + SendAlternateCurrencyValues(); + alternate_currency_loaded = true; + ProcessAlternateCurrencyQueue(); + + /* This needs to be set, this determines whether or not data was loaded properly before a save */ + client_data_loaded = true; + + CalcItemScale(); + DoItemEnterZone(); + + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + GuildBanks->SendGuildBank(this); + + if (GetClientVersion() >= EQClientSoD) + entity_list.SendFindableNPCList(this); + + if (IsInAGuild()) { + SendGuildRanks(); + guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); + guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); + } + + /** Request adventure info **/ + ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); + strcpy((char*)pack->pBuffer, GetName()); + worldserver.SendPacket(pack); + delete pack; + + if (IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { + EQApplicationPacket *outapp = MakeBuffsPacket(false); + CastToClient()->FastQueuePacket(&outapp); + } + + entity_list.RefreshClientXTargets(this); + + worldserver.RequestTellQueue(GetName()); +} + +void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) +{ + //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. + + switch (CheatType) + { + case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + Message(13, "Large warp detected."); + char hString[250]; + sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQWarpShadowStep: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + break; + case MQWarpKnockBack: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + break; + + case MQWarpLight: + if (RuleB(Zone, EnableMQWarpDetector) + && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) + || (RuleI(Zone, MQWarpExemptStatus)) == -1))) + { + if (RuleB(Zone, MarkMQWarpLT)) + { + char *hString = nullptr; + MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + } + } + break; + + case MQZone: + if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + { + char hString[250]; + sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQZoneUnknownDest: + if (RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + { + char hString[250]; + sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + } + break; + case MQGate: + if (RuleB(Zone, EnableMQGateDetector) && ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) { + Message(13, "Illegal gate request."); + char hString[250]; + sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + if (zone) + { + this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + } + else + { + this->SetZone(this->GetZoneID(), 0); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + + } + } + break; + case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified + if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) { + database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName()); + } + break; + default: + char *hString = nullptr; + MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ()); + database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + safe_delete_array(hString); + break; + } +} + +// connecting opcode handlers +/* +void Client::Handle_Connect_0x3e33(const EQApplicationPacket *app) +{ +//OP_0x0380 = 0x642c +EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0380, sizeof(uint32)); // Dunno +QueuePacket(outapp); +safe_delete(outapp); +return; +} +*/ + +void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ApproveZone_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", + sizeof(ApproveZone_Struct), app->size); return; } + ApproveZone_Struct* azone = (ApproveZone_Struct*)app->pBuffer; + azone->approve = 1; + QueuePacket(app); return; } -void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) +void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) { - if(app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_SetServerFilter"); - DumpPacket(app); + if (app->size != sizeof(ClientError_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", + sizeof(ClientError_Struct), app->size); return; } - SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); + // Client reporting error to server + ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; + LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); + LogFile->write(EQEMuLog::Error, "Error message: %s", error->message); + Message(13, error->message); +#if (EQDEBUG>=5) + DumpPacket(app); +#endif return; } -void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) +void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) { - SendAAList(); - return; + conn_state = ClientReadyReceived; + + CompleteConnect(); + SendHPUpdate(); } -void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) +void Client::Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app) { - SendTributes(); - return; -} + //Once we get this, the client thinks it is connected + //So give it the benefit of the doubt and move to connected -void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) -{ - SendGuildTributes(); - return; + Handle_Connect_OP_ClientReady(app); } -void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) -{ - SendAATimers(); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -//void Client::Handle_Connect_0x3e33(const EQApplicationPacket *app) -//{ -/*OP_0x0380 = 0x642c*/ - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_0x0380, sizeof(uint32)); // Dunno - //QueuePacket(outapp); - //safe_delete(outapp); - //return; -//} - void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) { conn_state = ClientSpawnRequested; @@ -620,7 +1026,7 @@ void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) EQApplicationPacket* outapp = new EQApplicationPacket; // Send Zone Doors - if(entity_list.MakeDoorSpawnPacket(outapp, this)) + if (entity_list.MakeDoorSpawnPacket(outapp, this)) { QueuePacket(outapp); } @@ -639,7 +1045,7 @@ void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) outapp = new EQApplicationPacket(OP_SendExpZonein, 0); FastQueuePacket(&outapp); - if(GetClientVersion() >= EQClientRoF) + if (GetClientVersion() >= EQClientRoF) { outapp = new EQApplicationPacket(OP_ClientReady, 0); FastQueuePacket(&outapp); @@ -650,7 +1056,7 @@ void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp); - if(strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) + if (strncasecmp(zone->GetShortName(), "bazaar", 6) == 0) SendBazaarWelcome(); conn_state = ZoneContentsSent; @@ -676,6 +1082,21 @@ void Client::Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app) return; } +void Client::Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app) +{ + SendAATimers(); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAAStats, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; +} + +void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app) +{ + SendAAList(); + return; +} + void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) { ////////////////////////////////////////////////////// @@ -695,22 +1116,22 @@ void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) outapp->priority = 6; if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); safe_delete(outapp); - if(GetPVP()) //force a PVP update until we fix the spawn struct + if (GetPVP()) //force a PVP update until we fix the spawn struct SendAppearancePacket(AT_PVP, GetPVP(), true, false); //Send AA Exp packet: - if(GetLevel() >= 51) + if (GetLevel() >= 51) SendAAStats(); // Send exp packets outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); uint32 tmpxp2 = GetEXPForLevel(GetLevel()); // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float) ( (float) m_pp.exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); eu->exp = (uint32)(330.0f * tmpxp); outapp->priority = 6; QueuePacket(outapp); @@ -724,10 +1145,10 @@ void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) safe_delete(outapp); outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname=(ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name,m_pp.name); - strcpy(zonesendname->name2,m_pp.name); - zonesendname->unknown0=0x0A; + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; QueuePacket(outapp); safe_delete(outapp); @@ -738,7 +1159,7 @@ void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp);*/ - if(IsInAGuild()) { + if (IsInAGuild()) { SendGuildMembers(); SendGuildURL(); SendGuildChannel(); @@ -753,6 +1174,57 @@ void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) return; } +void Client::Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app) +{ + SendGuildTributes(); + return; +} + +void Client::Handle_Connect_OP_SendTributes(const EQApplicationPacket *app) +{ + SendTributes(); + return; +} + +void Client::Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetServerFilter_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_SetServerFilter"); + DumpPacket(app); + return; + } + SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; + ServerFilter(filter); + return; +} + +void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TGB: Expected %i, Got %i", + sizeof(uint32), app->size); + return; + } + OPTGB(app); + return; +} + +void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) +{ + SendAATable(); +} + +void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) +{ + //not sure what these are supposed to mean to us. + return; +} + void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) { //This is a copy of SendExpZonein created for SoF due to packet order change @@ -775,22 +1247,22 @@ void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) outapp->priority = 6; if (!GetHideMe()) entity_list.QueueClients(this, outapp, true); safe_delete(outapp); - if(GetPVP()) //force a PVP update until we fix the spawn struct + if (GetPVP()) //force a PVP update until we fix the spawn struct SendAppearancePacket(AT_PVP, GetPVP(), true, false); //Send AA Exp packet: - if(GetLevel() >= 51) + if (GetLevel() >= 51) SendAAStats(); // Send exp packets outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct)); ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer; - uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1); + uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1); uint32 tmpxp2 = GetEXPForLevel(GetLevel()); // Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc) if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) { - float tmpxp = (float) ( (float) m_pp.exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 ); + float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2); eu->exp = (uint32)(330.0f * tmpxp); outapp->priority = 6; QueuePacket(outapp); @@ -805,14 +1277,14 @@ void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app) safe_delete(outapp); outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct)); - ZoneInSendName_Struct* zonesendname=(ZoneInSendName_Struct*)outapp->pBuffer; - strcpy(zonesendname->name,m_pp.name); - strcpy(zonesendname->name2,m_pp.name); - zonesendname->unknown0=0x0A; + ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer; + strcpy(zonesendname->name, m_pp.name); + strcpy(zonesendname->name2, m_pp.name); + zonesendname->unknown0 = 0x0A; QueuePacket(outapp); safe_delete(outapp); - if(IsInAGuild()) { + if (IsInAGuild()) { SendGuildMembers(); SendGuildURL(); SendGuildChannel(); @@ -836,177 +1308,3046 @@ void Client::Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app) return; } -void Client::Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app) +void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) { - return; -} + if(app->size != sizeof(ClientZoneEntry_Struct)) + return; + ClientZoneEntry_Struct *cze = (ClientZoneEntry_Struct *) app->pBuffer; -void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app) -{ - //not sure what these are supposed to mean to us. - return; -} + if(strlen(cze->char_name) > 63) + return; -void Client::Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app) -{ - //Once we get this, the client thinks it is connected - //So give it the benefit of the doubt and move to connected + conn_state = ReceivedZoneEntry; - Handle_Connect_OP_ClientReady(app); -} + ClientVersion = Connection()->ClientVersion(); + if (ClientVersion != EQClientUnknown) + ClientVersionBit = 1 << (ClientVersion - 1); + else + ClientVersionBit = 0; -void Client::Handle_Connect_OP_ClientReady(const EQApplicationPacket *app) -{ - conn_state = ClientReadyReceived; + bool siv = m_inv.SetInventoryVersion(ClientVersion); + LogFile->write(EQEMuLog::Debug, "%s inventory version to %s(%i)", (siv ? "Succeeded in setting" : "Failed to set"), EQClientVersionName(ClientVersion), ClientVersion); - CompleteConnect(); - SendHPUpdate(); -} - -void Client::Handle_Connect_OP_ClientError(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientError_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClientError: Expected %i, Got %i", - sizeof(ClientError_Struct), app->size); + /* Antighost code + tmp var is so the search doesnt find this object + */ + Client* client = entity_list.GetClientByName(cze->char_name); + if (!zone->GetAuth(ip, cze->char_name, &WID, &account_id, &character_id, &admin, lskey, &tellsoff)) { + LogFile->write(EQEMuLog::Error, "GetAuth() returned false kicking client"); + if (client != 0) { + client->Save(); + client->Kick(); + } + //ret = false; // TODO: Can we tell the client to get lost in a good way + client_state = CLIENT_KICKED; return; } - // Client reporting error to server - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message: %s", error->message); - Message(13, error->message); -#if (EQDEBUG>=5) - DumpPacket(app); + + strcpy(name, cze->char_name); + /* Check for Client Spoofing */ + if (client != 0) { + struct in_addr ghost_addr; + ghost_addr.s_addr = eqs->GetRemoteIP(); + + LogFile->write(EQEMuLog::Error,"Ghosting client: Account ID:%i Name:%s Character:%s IP:%s", + client->AccountID(), client->AccountName(), client->GetName(), inet_ntoa(ghost_addr)); + client->Save(); + client->Disconnect(); + } + + uint32 pplen = 0; + EQApplicationPacket* outapp = 0; + MYSQL_RES* result = 0; + bool loaditems = 0; + uint32 i; + std::string query; + unsigned long* lengths; + + uint32 cid = CharacterID(); + character_id = cid; /* Global character_id reference */ + + /* Flush and reload factions */ + database.RemoveTempFactions(this); + database.LoadCharacterFactionValues(cid, factionvalues); + + /* Load Character Account Data: Temp until I move */ + query = StringFormat("SELECT `status`, `name`, `lsaccount_id`, `gmspeed`, `revoked`, `hideme` FROM `account` WHERE `id` = %u", this->AccountID()); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + if (admin){ admin = atoi(row[0]); } + if (account_name){ strcpy(account_name, row[1]); } + if (lsaccountid && atoi(row[2]) > 0){ lsaccountid = atoi(row[2]); } + else{ lsaccountid = 0; } + gmspeed = atoi(row[3]); + revoked = atoi(row[4]); + gmhideme = atoi(row[5]); + if (account_creation){ account_creation = atoul(row[6]); } + } + + /* Load Character Data */ + query = StringFormat("SELECT `lfp`, `lfg`, `xtargets`, `firstlogon`, `guild_id`, `rank` FROM `character_data` LEFT JOIN `guild_members` ON `id` = `char_id` WHERE `id` = %i", cid); + results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + if (row[4] && atoi(row[4]) > 0){ + guild_id = atoi(row[4]); + if (row[5] != nullptr){ guildrank = atoi(row[5]); } + else{ guildrank = GUILD_RANK_NONE; } + } + + if (LFP){ LFP = atoi(row[0]); } + if (LFG){ LFG = atoi(row[1]); } + if (firstlogon){ firstlogon = atoi(row[3]); } + } + + if (RuleB(Character, SharedBankPlat)) + m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID()); + + loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ + database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ + database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ + database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */ + database.LoadCharacterPotions(cid, &m_pp); /* Load Character Potion Belt */ + database.LoadCharacterCurrency(cid, &m_pp); /* Load Character Currency into PP */ + database.LoadCharacterData(cid, &m_pp, &m_epp); /* Load Character Data from DB into PP as well as E_PP */ + database.LoadCharacterSkills(cid, &m_pp); /* Load Character Skills */ + database.LoadCharacterInspectMessage(cid, &m_inspect_message); /* Load Character Inspect Message */ + database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */ + database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */ + database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */ + database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */ + database.LoadCharacterLeadershipAA(cid, &m_pp); /* Load Character Leadership AA's */ + database.LoadCharacterTribute(cid, &m_pp); /* Load CharacterTribute */ + + /* Load AdventureStats */ + AdventureStats_Struct as; + if(database.GetAdventureStats(cid, &as)) + { + m_pp.ldon_wins_guk = as.success.guk; + m_pp.ldon_wins_mir = as.success.mir; + m_pp.ldon_wins_mmc = as.success.mmc; + m_pp.ldon_wins_ruj = as.success.ruj; + m_pp.ldon_wins_tak = as.success.tak; + m_pp.ldon_losses_guk = as.failure.guk; + m_pp.ldon_losses_mir = as.failure.mir; + m_pp.ldon_losses_mmc = as.failure.mmc; + m_pp.ldon_losses_ruj = as.failure.ruj; + m_pp.ldon_losses_tak = as.failure.tak; + } + + /* Set item material tint */ + for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) + if (m_pp.item_tint[i].rgb.use_tint == 1 || m_pp.item_tint[i].rgb.use_tint == 255) + m_pp.item_tint[i].rgb.use_tint = 0xFF; + + if (level){ level = m_pp.level; } + + /* If GM, not trackable */ + if (gmhideme) { trackable = false; } + /* Set Con State for Reporting */ + conn_state = PlayerProfileLoaded; + + m_pp.zone_id = zone->GetZoneID(); + m_pp.zoneInstance = zone->GetInstanceID(); + + /* Set Total Seconds Played */ + TotalSecondsPlayed = m_pp.timePlayedMin * 60; + /* Set Max AA XP */ + max_AAXP = RuleI(AA, ExpPerPoint); + /* If we can maintain intoxication across zones, check for it */ + if (!RuleB(Character, MaintainIntoxicationAcrossZones)) + m_pp.intoxication = 0; + + strcpy(name, m_pp.name); + strcpy(lastname, m_pp.last_name); + /* If PP is set to weird coordinates */ + if ((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1) || (m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { + m_pp.x = zone->safe_x(); + m_pp.y = zone->safe_y(); + m_pp.z = zone->safe_z(); + } + /* If too far below ground, then fix */ + // float ground_z = GetGroundZ(m_pp.x, m_pp.y, m_pp.z); + // if (m_pp.z < (ground_z - 500)) + // m_pp.z = ground_z; + + /* Set Mob variables for spawn */ + class_ = m_pp.class_; + level = m_pp.level; + x_pos = m_pp.x; + y_pos = m_pp.y; + z_pos = m_pp.z; + heading = m_pp.heading; + race = m_pp.race; + base_race = m_pp.race; + gender = m_pp.gender; + base_gender = m_pp.gender; + deity = m_pp.deity; + haircolor = m_pp.haircolor; + beardcolor = m_pp.beardcolor; + eyecolor1 = m_pp.eyecolor1; + eyecolor2 = m_pp.eyecolor2; + hairstyle = m_pp.hairstyle; + luclinface = m_pp.face; + beard = m_pp.beard; + drakkin_heritage = m_pp.drakkin_heritage; + drakkin_tattoo = m_pp.drakkin_tattoo; + drakkin_details = m_pp.drakkin_details; + + /* If GM not set in DB, and does not meet min status to be GM, reset */ + if (m_pp.gm && admin < minStatusToBeGM) + m_pp.gm = 0; + + /* Load Guild */ + if (!IsInAGuild()) { m_pp.guild_id = GUILD_NONE; } + else { + m_pp.guild_id = GuildID(); + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID)) + GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); + } + m_pp.guildbanker = GuildBanker; + + switch (race) + { + case OGRE: + size = 9; break; + case TROLL: + size = 8; break; + case VAHSHIR: case BARBARIAN: + size = 7; break; + case HUMAN: case HIGH_ELF: case ERUDITE: case IKSAR: case DRAKKIN: + size = 6; break; + case HALF_ELF: + size = 5.5; break; + case WOOD_ELF: case DARK_ELF: case FROGLOK: + size = 5; break; + case DWARF: + size = 4; break; + case HALFLING: + size = 3.5; break; + case GNOME: + size = 3; break; + default: + size = 0; + } + + /* Check for Invalid points */ + if (m_pp.ldon_points_guk < 0 || m_pp.ldon_points_guk > 2000000000){ m_pp.ldon_points_guk = 0; } + if (m_pp.ldon_points_mir < 0 || m_pp.ldon_points_mir > 2000000000){ m_pp.ldon_points_mir = 0; } + if (m_pp.ldon_points_mmc < 0 || m_pp.ldon_points_mmc > 2000000000){ m_pp.ldon_points_mmc = 0; } + if (m_pp.ldon_points_ruj < 0 || m_pp.ldon_points_ruj > 2000000000){ m_pp.ldon_points_ruj = 0; } + if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000){ m_pp.ldon_points_tak = 0; } + if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000){ m_pp.ldon_points_available = 0; } + + /* Set Swimming Skill 100 by default if under 100 */ + if (GetSkill(SkillSwimming) < 100) + SetSkill(SkillSwimming, 100); + + /* Initialize AA's : Move to function eventually */ + for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ aa[a] = &m_pp.aa_array[a]; } + query = StringFormat( + "SELECT " + "slot, " + "aa_id, " + "aa_value " + "FROM " + "`character_alternate_abilities` " + "WHERE `id` = %u ORDER BY `slot`", this->CharacterID()); + results = database.QueryDatabase(query); i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + m_pp.aa_array[i].AA = atoi(row[1]); + m_pp.aa_array[i].value = atoi(row[2]); + aa[i]->AA = atoi(row[1]); + aa[i]->value = atoi(row[2]); + } + for (uint32 a = 0; a < MAX_PP_AA_ARRAY; a++){ + uint32 id = aa[a]->AA; + //watch for invalid AA IDs + if (id == aaNone) + continue; + if (id >= aaHighestID) { + aa[a]->AA = aaNone; + aa[a]->value = 0; + continue; + } + if (aa[a]->value == 0) { + aa[a]->AA = aaNone; + continue; + } + if (aa[a]->value > HIGHEST_AA_VALUE) { + aa[a]->AA = aaNone; + aa[a]->value = 0; + continue; + } + + if (aa[a]->value > 1) /* hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) */ + aa_points[(id - aa[a]->value + 1)] = aa[a]->value; + else + aa_points[id] = aa[a]->value; + } + + if (SPDAT_RECORDS > 0) { + for (uint32 z = 0; z= (uint32)SPDAT_RECORDS) + UnmemSpell(z, false); + } + + database.LoadBuffs(this); + uint32 max_slots = GetMaxBuffSlots(); + for (int i = 0; i < max_slots; i++) { + if (buffs[i].spellid != SPELL_UNKNOWN) { + m_pp.buffs[i].spellid = buffs[i].spellid; + m_pp.buffs[i].bard_modifier = 10; + m_pp.buffs[i].slotid = 2; + m_pp.buffs[i].player_id = 0x2211; + m_pp.buffs[i].level = buffs[i].casterlevel; + m_pp.buffs[i].effect = 0; + m_pp.buffs[i].duration = buffs[i].ticsremaining; + m_pp.buffs[i].counters = buffs[i].counters; + } + else { + m_pp.buffs[i].spellid = SPELLBOOK_UNKNOWN; + m_pp.buffs[i].bard_modifier = 10; + m_pp.buffs[i].slotid = 0; + m_pp.buffs[i].player_id = 0; + m_pp.buffs[i].level = 0; + m_pp.buffs[i].effect = 0; + m_pp.buffs[i].duration = 0; + m_pp.buffs[i].counters = 0; + } + } + } + + /* Load Character Key Ring */ + KeyRingLoad(); + + /* Send Group Members via PP */ + uint32 groupid = database.GetGroupID(GetName()); + Group* group = nullptr; + if (groupid > 0){ + group = entity_list.GetGroupByID(groupid); + if (!group) { //nobody from our is here... start a new group + group = new Group(groupid); + if (group->GetID() != 0) + entity_list.AddGroup(group, groupid); + else //error loading group members... + { + delete group; + group = nullptr; + } + } //else, somebody from our group is already here... + + if (!group) + database.SetGroupID(GetName(), 0, CharacterID()); //cannot re-establish group, kill it + + } + else { //no group id + //clear out the group junk in our PP + uint32 xy = 0; + for (xy = 0; xy < MAX_GROUP_MEMBERS; xy++) + memset(m_pp.groupMembers[xy], 0, 64); + } + + if (group){ + // If the group leader is not set, pull the group leader infomrmation from the database. + if (!group->GetLeader()){ + char ln[64]; + char MainTankName[64]; + char AssistName[64]; + char PullerName[64]; + char NPCMarkerName[64]; + char mentoree_name[64]; + int mentor_percent; + GroupLeadershipAA_Struct GLAA; + memset(ln, 0, 64); + strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA)); + Client *c = entity_list.GetClientByName(ln); + if (c) + group->SetLeader(c); + + group->SetMainTank(MainTankName); + group->SetMainAssist(AssistName); + group->SetPuller(PullerName); + group->SetNPCMarker(NPCMarkerName); + group->SetGroupAAs(&GLAA); + group->SetGroupMentor(mentor_percent, mentoree_name); + + //group->NotifyMainTank(this, 1); + //group->NotifyMainAssist(this, 1); + //group->NotifyPuller(this, 1); + + // If we are the leader, force an update of our group AAs to other members in the zone, in case + // we purchased a new one while out-of-zone. + if (group->IsLeader(this)) + group->SendLeadershipAAUpdate(); + + } + group->UpdatePlayer(this); + LFG = false; + } + +#ifdef BOTS + Bot::LoadAndSpawnAllZonedBots(this); #endif + + CalcBonuses(); + if (RuleB(Zone, EnableLoggedOffReplenishments) && + time(nullptr) - m_pp.lastlogin >= RuleI(Zone, MinOfflineTimeToReplenishments)) { + m_pp.cur_hp = GetMaxHP(); + m_pp.mana = GetMaxMana(); + m_pp.endurance = GetMaxEndurance(); + } + + if (m_pp.cur_hp <= 0) + m_pp.cur_hp = GetMaxHP(); + + SetHP(m_pp.cur_hp); + Mob::SetMana(m_pp.mana); // mob function doesn't send the packet + SetEndurance(m_pp.endurance); + + /* Update LFP in case any (or all) of our group disbanded while we were zoning. */ + if (IsLFP()) { UpdateLFP(); } + + /* Get Expansions from variables table and ship via PP */ + char val[20] = { 0 }; + if (database.GetVariable("Expansions", val, 20)){ m_pp.expansions = atoi(val); } + else{ m_pp.expansions = 0x3FF; } + + p_timers.SetCharID(CharacterID()); + if (!p_timers.Load(&database)) { + LogFile->write(EQEMuLog::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID()); + } + + /* Load Spell Slot Refresh from Currently Memoried Spells */ + for (unsigned int i = 0; i < MAX_PP_MEMSPELL; ++i) + if (IsValidSpell(m_pp.mem_spells[i])) + m_pp.spellSlotRefresh[i] = p_timers.GetRemainingTime(pTimerSpellStart + m_pp.mem_spells[i]) * 1000; + + /* Ability slot refresh send SK/PAL */ + if (m_pp.class_ == SHADOWKNIGHT || m_pp.class_ == PALADIN) { + uint32 abilitynum = 0; + if (m_pp.class_ == SHADOWKNIGHT){ abilitynum = pTimerHarmTouch; } + else{ abilitynum = pTimerLayHands; } + + uint32 remaining = p_timers.GetRemainingTime(abilitynum); + if (remaining > 0 && remaining < 15300) + m_pp.abilitySlotRefresh = remaining * 1000; + else + m_pp.abilitySlotRefresh = 0; + } + +#ifdef _EQDEBUG + printf("Dumping inventory on load:\n"); + m_inv.dumpEntireInventory(); +#endif + + /* Reset to max so they dont drown on zone in if its underwater */ + m_pp.air_remaining = 60; + /* Check for PVP Zone status*/ + if (zone->IsPVPZone()) + m_pp.pvp = 1; + /* Time entitled on Account: Move to account */ + m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; + /* Reset rest timer if the durations have been lowered in the database */ + if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) + m_pp.RestTimer = 0; + + /* This checksum should disappear once dynamic structs are in... each struct strategy will do it */ + CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4); + + outapp = new EQApplicationPacket(OP_PlayerProfile, sizeof(PlayerProfile_Struct)); + + /* The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA */ + m_pp.entityid = GetID(); + memcpy(outapp->pBuffer, &m_pp, outapp->size); + outapp->priority = 6; + FastQueuePacket(&outapp); + + if (m_pp.RestTimer) + rest_timer.Start(m_pp.RestTimer * 1000); + + database.LoadPetInfo(this); + /* + This was moved before the spawn packets are sent + in hopes that it adds more consistency... + Remake pet + */ + if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) { + MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); + if (GetPet() && GetPet()->IsNPC()) { + NPC *pet = GetPet()->CastToNPC(); + pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items); + pet->CalcBonuses(); + pet->SetHP(m_petinfo.HP); + pet->SetMana(m_petinfo.Mana); + } + m_petinfo.SpellID = 0; + } + /* Moved here so it's after where we load the pet data. */ + if (!GetAA(aaPersistentMinion)) + memset(&m_suspendedminion, 0, sizeof(PetInfo)); + + /* Server Zone Entry Packet */ + outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); + ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; + + FillSpawnStruct(&sze->player, CastToMob()); + sze->player.spawn.curHp = 1; + sze->player.spawn.NPC = 0; + sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. + outapp->priority = 6; + FastQueuePacket(&outapp); + + /* Zone Spawns Packet */ + entity_list.SendZoneSpawnsBulk(this); + entity_list.SendZoneCorpsesBulk(this); + entity_list.SendZonePVPUpdates(this); //hack until spawn struct is fixed. + + /* Time of Day packet */ + outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); + TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; + zone->zone_time.getEQTimeOfDay(time(0), tod); + outapp->priority = 6; + FastQueuePacket(&outapp); + + /* Tribute Packets */ + DoTributeUpdate(); + if (m_pp.tribute_active) { + //restart the tribute timer where we left off + tribute_timer.Start(m_pp.tribute_time_remaining); + } + + /* + Character Inventory Packet + this is not quite where live sends inventory, they do it after tribute + */ + if (loaditems) { /* Dont load if a length error occurs */ + BulkSendInventoryItems(); + /* Send stuff on the cursor which isnt sent in bulk */ + iter_queue it; + for (it = m_inv.cursor_begin(); it != m_inv.cursor_end(); ++it) { + /* First item cursor is sent in bulk inventory packet */ + if (it == m_inv.cursor_begin()) + continue; + const ItemInst *inst = *it; + SendItemPacket(MainCursor, inst, ItemPacketSummonItem); + } + } + + /* Task Packets */ + LoadClientTaskState(); + + if (GetClientVersion() >= EQClientRoF) { + outapp = new EQApplicationPacket(OP_ReqNewZone, 0); + Handle_Connect_OP_ReqNewZone(outapp); + safe_delete(outapp); + } + + if (ClientVersionBit & BIT_UnderfootAndLater) { + outapp = new EQApplicationPacket(OP_XTargetResponse, 8); + outapp->WriteUInt32(GetMaxXTargets()); + outapp->WriteUInt32(0); + FastQueuePacket(&outapp); + } + + /* + Weather Packet + This shouldent be moved, this seems to be what the client + uses to advance to the next state (sending ReqNewZone) + */ + outapp = new EQApplicationPacket(OP_Weather, 12); + Weather_Struct *ws = (Weather_Struct *)outapp->pBuffer; + ws->val1 = 0x000000FF; + if (zone->zone_weather == 1){ ws->type = 0x31; } // Rain + if (zone->zone_weather == 2) { + outapp->pBuffer[8] = 0x01; + ws->type = 0x02; + } + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + SetAttackTimer(); + conn_state = ZoneInfoSent; + return; } -void Client::Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app) +// connected opcode handlers +void Client::Handle_0x0193(const EQApplicationPacket *app) { - if (app->size != sizeof(ApproveZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ApproveZone: Expected %i, Got %i", - sizeof(ApproveZone_Struct), app->size); + // Not sure what this opcode does. It started being sent when OP_ClientUpdate was + // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate + // 2 bytes: 00 00 + + return; +} + +void Client::Handle_OP_AAAction(const EQApplicationPacket *app) +{ + mlog(AA__IN, "Received OP_AAAction"); + mpkt(AA__IN, app); + + if (app->size != sizeof(AA_Action)){ + printf("Error! OP_AAAction size didnt match!\n"); + return; + } + AA_Action* action = (AA_Action*)app->pBuffer; + + if (action->action == aaActionActivate) {//AA Hotkey + mlog(AA__MESSAGE, "Activating AA %d", action->ability); + ActivateAA((aaID)action->ability); + } + else if (action->action == aaActionBuy) { + BuyAA(action); + } + else if (action->action == aaActionDisableEXP){ //Turn Off AA Exp + if (m_epp.perAA > 0) + Message_StringID(0, AA_OFF); + m_epp.perAA = 0; + SendAAStats(); + } + else if (action->action == aaActionSetEXP) { + if (m_epp.perAA == 0) + Message_StringID(0, AA_ON); + m_epp.perAA = action->exp_value; + if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA = 0; // stop exploit with sanity check + // send an update + SendAAStats(); + SendAATable(); + } + else { + printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value); + } + + return; +} + +void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(AcceptNewTask_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AcceptNewTask expected %i got %i", + sizeof(AcceptNewTask_Struct), app->size); + DumpPacket(app); + return; + } + AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; + + if (ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id); +} + +void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(EntityId_Struct)) + { + LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); + return; + } + EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; + Mob * m = entity_list.GetMob(ent->entity_id); + if (m && m->IsNPC()) + { + std::map::iterator it; + it = zone->adventure_entry_list_flavor.find(m->CastToNPC()->GetAdventureTemplate()); + if (it != zone->adventure_entry_list_flavor.end()) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (it->second.size() + 2)); + strn0cpy((char*)outapp->pBuffer, it->second.c_str(), it->second.size()); + FastQueuePacket(&outapp); + } + else + { + if (m->CastToNPC()->GetAdventureTemplate() != 0) + { + std::string text = "Choose your difficulty and preferred adventure type."; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (text.size() + 2)); + strn0cpy((char*)outapp->pBuffer, text.c_str(), text.size()); + FastQueuePacket(&outapp); + } + } + } +} + +void Client::Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(AdventureLeaderboardRequest_Struct)) + { + return; + } + + if (adventure_leaderboard_timer) + { + return; + } + + adventure_leaderboard_timer = new Timer(4000); + ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, sizeof(ServerLeaderboardRequest_Struct)); + ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; + strcpy(lr->player, GetName()); + + AdventureLeaderboardRequest_Struct *lrs = (AdventureLeaderboardRequest_Struct*)app->pBuffer; + lr->type = 1 + (lrs->theme * 2) + lrs->type; + worldserver.SendPacket(pack); + delete pack; +} + +void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Adventure_Purchase_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); + return; + } + + Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer; + /* + Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes) + if(ldon_points_available >= item ldonpointcost) + { + give item (67 00 00 00 for the packettype using opcode 0x02c5) + ldon_points_available -= ldonpointcost; + } + */ + uint32 merchantid = 0; + Mob* tmp = entity_list.GetMob(aps->npcid); + if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && + (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + const Item_Struct* item = nullptr; + bool found = false; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + + for (itr = merlist.begin(); itr != merlist.end(); ++itr){ + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + if (item->ID == aps->itemid) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... + found = true; + break; + } + } + if (!item || !found) { + Message(13, "Error: The item you purchased does not exist!"); + return; + } + + if (aps->Type == LDoNMerchant) + { + if (m_pp.ldon_points_available < int32(item->LDoNPrice)) { + Message(13, "You cannot afford that item."); + return; + } + + if (item->LDoNTheme <= 16) + { + if (item->LDoNTheme & 16) + { + if (m_pp.ldon_points_tak < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in tak to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 8) + { + if (m_pp.ldon_points_ruj < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in ruj to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 4) + { + if (m_pp.ldon_points_mmc < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in mmc to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 2) + { + if (m_pp.ldon_points_mir < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in mir to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (item->LDoNTheme & 1) + { + if (m_pp.ldon_points_guk < int32(item->LDoNPrice)) + { + Message(13, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + } + } + else if (aps->Type == DiscordMerchant) + { + if (GetPVPPoints() < item->LDoNPrice) + { + Message(13, "You need at least %u PVP points to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (aps->Type == NorrathsKeepersMerchant) + { + if (GetRadiantCrystals() < item->LDoNPrice) + { + Message(13, "You need at least %u Radiant Crystals to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else if (aps->Type == DarkReignMerchant) + { + if (GetEbonCrystals() < item->LDoNPrice) + { + Message(13, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); + return; + } + } + else + { + Message(13, "Unknown Adventure Merchant type."); + return; + } + + + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + + if (aps->Type == LDoNMerchant) + { + int32 requiredpts = (int32)item->LDoNPrice*-1; + + if (!UpdateLDoNPoints(requiredpts, 6)) + return; + } + else if (aps->Type == DiscordMerchant) + { + SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); + SendPVPStats(); + } + else if (aps->Type == NorrathsKeepersMerchant) + { + SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); + SendCrystalCounts(); + } + else if (aps->Type == DarkReignMerchant) + { + SetEbonCrystals(GetEbonCrystals() - (int32)item->LDoNPrice); + SendCrystalCounts(); + } + int16 charges = 1; + if (item->MaxCharges != 0) + charges = item->MaxCharges; + + ItemInst *inst = database.CreateItem(item, charges); + if (!AutoPutLootInInventory(*inst, true, true)) + { + PutLootInInventory(MainCursor, *inst); + } + Save(1); +} + +void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(AdventureMerchant_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); + return; + } + std::stringstream ss(std::stringstream::in | std::stringstream::out); + + uint8 count = 0; + AdventureMerchant_Struct* eid = (AdventureMerchant_Struct*)app->pBuffer; + uint32 merchantid = 0; + + Mob* tmp = entity_list.GetMob(eid->entity_id); + if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && + (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + tmp->CastToNPC()->FaceTarget(this->CastToMob()); + + const Item_Struct *item = 0; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end() && count<255; ++itr){ + const MerchantList &ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (item) + { + uint32 theme; + if (item->LDoNTheme > 16) + { + theme = 0; + } + else if (item->LDoNTheme & 16) + { + theme = 5; + } + else if (item->LDoNTheme & 8) + { + theme = 4; + } + else if (item->LDoNTheme & 4) + { + theme = 3; + } + else if (item->LDoNTheme & 2) + { + theme = 2; + } + else if (item->LDoNTheme & 1) + { + theme = 1; + } + else + { + theme = 0; + } + ss << "^" << item->Name << "|"; + ss << item->ID << "|"; + ss << item->LDoNPrice << "|"; + ss << theme << "|"; + ss << "0|"; + ss << "1|"; + ss << item->Races << "|"; + ss << item->Classes; + count++; + } + } + //Count + //^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse, ss.str().size() + 2); + outapp->pBuffer[0] = count; + strn0cpy((char*)&outapp->pBuffer[1], ss.str().c_str(), ss.str().size()); + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Adventure_Sell_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", + app->size, sizeof(Adventure_Sell_Struct)); + DumpPacket(app); + return; + } + + Adventure_Sell_Struct *ams_in = (Adventure_Sell_Struct*)app->pBuffer; + + Mob* vendor = entity_list.GetMob(ams_in->npcid); + if (vendor == 0 || !vendor->IsNPC() || ((vendor->GetClass() != ADVENTUREMERCHANT) && + (vendor->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (vendor->GetClass() != DARK_REIGN_MERCHANT))) + { + Message(13, "Vendor was not found."); + return; + } + + if (DistNoRoot(*vendor) > USE_NPC_RANGE2) + { + Message(13, "Vendor is out of range."); + return; + } + + uint32 itemid = GetItemIDAt(ams_in->slot); + + if (itemid == 0) + { + Message(13, "Found no item at that slot."); + return; + } + + const Item_Struct* item = database.GetItem(itemid); + ItemInst* inst = GetInv().GetItem(ams_in->slot); + if (!item || !inst){ + Message(13, "You seemed to have misplaced that item..."); + return; + } + + // Note that Lucy has ldonsold values of 4 and 5 for items sold by Norrath's Keepers and Dark Reign, whereas 13th Floor + // has ldonsold = 0 for these items, so some manual editing of the items DB will be required to support sell back of the + // items. + // + // The Merchant seems to have some other way of knowing whether he will accept the item, other than the ldonsold field, + // e.g. if you summon items 76036 and 76053 (good and evil versions of Spell: Ward Of Vengeance), if you are interacting + // with a Norrath's Keeper merchant and click on 76036 in your inventory, he says he will give you radiant crystals for + // it, but he will refuse for item 76053. + // + // Similarly, just giving a cloth cap an ldonsold value of 4 will not make the Merchant buy it. + // + // Note that the the Client will not allow you to sell anything back to a Discord merchant, so there is no need to handle + // that case here. + if (item->LDoNSold == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + if (item->LDoNPrice == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + int32 price = item->LDoNPrice * 70 / 100; + + if (price == 0) + { + Message(13, "The merchant does not want that item."); + return; + } + + if (RuleB(EventLog, RecordSellToMerchant)) + LogMerchant(this, vendor, ams_in->charges, price, item, false); + + if (!inst->IsStackable()) + { + DeleteItemInInventory(ams_in->slot, 0, false); + } + else + { + if (inst->GetCharges() < ams_in->charges) + { + ams_in->charges = inst->GetCharges(); + } + + if (ams_in->charges == 0) + { + Message(13, "Charge mismatch error."); + return; + } + + DeleteItemInInventory(ams_in->slot, ams_in->charges, false); + price *= ams_in->charges; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantSell, sizeof(Adventure_Sell_Struct)); + Adventure_Sell_Struct *ams = (Adventure_Sell_Struct*)outapp->pBuffer; + ams->slot = ams_in->slot; + ams->unknown000 = 1; + ams->npcid = ams->npcid; + ams->charges = ams_in->charges; + ams->sell_price = price; + FastQueuePacket(&outapp); + + switch (vendor->GetClass()) + { + case ADVENTUREMERCHANT: + { + UpdateLDoNPoints(price, 6); + break; + } + case NORRATHS_KEEPERS_MERCHANT: + { + SetRadiantCrystals(GetRadiantCrystals() + price); + break; + } + case DARK_REIGN_MERCHANT: + { + SetEbonCrystals(GetEbonCrystals() + price); + break; + } + + default: + break; + } + + Save(1); +} + +void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(AdventureRequest_Struct)) + { + LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureRequest had a packet that was too small."); + return; + } + + if (IsOnAdventure()) + { + return; + } + + if (!p_timers.Expired(&database, pTimerStartAdventureTimer, false)) + { + return; + } + + if (GetPendingAdventureRequest()) + { + return; + } + + AdventureRequest_Struct* ars = (AdventureRequest_Struct*)app->pBuffer; + uint8 group_members = 0; + Raid *r = nullptr; + Group *g = nullptr; + + if (IsRaidGrouped()) + { + r = GetRaid(); + group_members = r->RaidCount(); + } + else if (IsGrouped()) + { + g = GetGroup(); + group_members = g->GroupCount(); + } + else + { + return; + } + + if (group_members < RuleI(Adventure, MinNumberForGroup) || group_members > RuleI(Adventure, MaxNumberForGroup)) + { + return; + } + + Mob* m = entity_list.GetMob(ars->entity_id); + uint32 template_id = 0; + if (m && m->IsNPC()) + { + template_id = m->CastToNPC()->GetAdventureTemplate(); + } + else + { + return; + } + + ServerPacket *packet = new ServerPacket(ServerOP_AdventureRequest, sizeof(ServerAdventureRequest_Struct)+(64 * group_members)); + ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)packet->pBuffer; + sar->member_count = group_members; + sar->risk = ars->risk; + sar->type = ars->type; + sar->template_id = template_id; + strcpy(sar->leader, GetName()); + + if (IsRaidGrouped()) + { + int i = 0; + for (int x = 0; x < 72; ++x) + { + if (i == group_members) + { + break; + } + + const char *c_name = nullptr; + c_name = r->GetClientNameByIndex(x); + if (c_name) + { + memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); + ++i; + } + } + } + else + { + int i = 0; + for (int x = 0; x < 6; ++x) + { + if (i == group_members) + { + break; + } + + const char *c_name = nullptr; + c_name = g->GetClientNameByIndex(x); + if (c_name) + { + memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct)+(64 * i)), c_name, strlen(c_name)); + ++i; + } + } + } + + packet->Deflate(); + worldserver.SendPacket(packet); + delete packet; + p_timers.Start(pTimerStartAdventureTimer, 5); +} + +void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) +{ + if (adventure_stats_timer) + { + return; + } + + adventure_stats_timer = new Timer(8000); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); + AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; + + if (database.GetAdventureStats(CharacterID(), as)) + { + m_pp.ldon_wins_guk = as->success.guk; + m_pp.ldon_wins_mir = as->success.mir; + m_pp.ldon_wins_mmc = as->success.mmc; + m_pp.ldon_wins_ruj = as->success.ruj; + m_pp.ldon_wins_tak = as->success.tak; + m_pp.ldon_losses_guk = as->failure.guk; + m_pp.ldon_losses_mir = as->failure.mir; + m_pp.ldon_losses_mmc = as->failure.mmc; + m_pp.ldon_losses_ruj = as->failure.ruj; + m_pp.ldon_losses_tak = as->failure.tak; + } + + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); + + NPC* tar = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + std::list::iterator altc_iter = zone->AlternateCurrencies.begin(); + bool found = false; + while (altc_iter != zone->AlternateCurrencies.end()) { + if ((*altc_iter).id == alt_cur_id) { + found = true; + break; + } + ++altc_iter; + } + + if (!found) { + return; + } + + std::stringstream ss(std::stringstream::in | std::stringstream::out); + std::stringstream item_ss(std::stringstream::in | std::stringstream::out); + ss << alt_cur_id << "|1|" << alt_cur_id; + uint32 count = 0; + uint32 merchant_id = tar->MerchantType; + const Item_Struct *item = nullptr; + + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end() && count < 255; ++itr){ + const MerchantList &ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (item) + { + item_ss << "^" << item->Name << "|"; + item_ss << item->ID << "|"; + item_ss << ml.alt_currency_cost << "|"; + item_ss << "0|"; + item_ss << "1|"; + item_ss << item->Races << "|"; + item_ss << item->Classes; + count++; + } + } + + if (count > 0) { + ss << "|" << count << item_ss.str(); + } + else { + ss << "|0"; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); + memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyPurchase, app, AltCurrencyPurchaseItem_Struct); + AltCurrencyPurchaseItem_Struct *purchase = (AltCurrencyPurchaseItem_Struct*)app->pBuffer; + NPC* tar = entity_list.GetNPCByID(purchase->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == purchase->item_id) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!item || !found) { + Message(13, "Error: The item you purchased does not exist!"); + return; + } + + if (cost > current_currency) { + Message(13, "You cannot afford that item right now."); + return; + } + + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + + /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + + AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); + int16 charges = 1; + if (item->MaxCharges != 0) + charges = item->MaxCharges; + + ItemInst *inst = database.CreateItem(item, charges); + if (!AutoPutLootInInventory(*inst, true, true)) + { + PutLootInInventory(MainCursor, *inst); + } + + Save(1); + } +} + +void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencyReclaim, app, AltCurrencyReclaim_Struct); + AltCurrencyReclaim_Struct *reclaim = (AltCurrencyReclaim_Struct*)app->pBuffer; + uint32 item_id = 0; + std::list::iterator iter = zone->AlternateCurrencies.begin(); + while (iter != zone->AlternateCurrencies.end()) { + if ((*iter).id == reclaim->currency_id) { + item_id = (*iter).item_id; + } + ++iter; + } + + if (item_id == 0) { + return; + } + + /* Item to Currency Storage */ + if (reclaim->reclaim_flag == 1) { + uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); + if (removed > 0) { + AddAlternateCurrencyValue(reclaim->currency_id, removed); + + /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } + } + /* Cursor to Item storage */ + else { + uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); + + /* If you input more than you have currency wise, just give the max of the currency you currently have */ + if (reclaim->count > max_currency) { + SummonItem(item_id, max_currency); + SetAlternateCurrencyValue(reclaim->currency_id, 0); + } + else { + SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); + AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); + } + /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } +} + +void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencySell, app, AltCurrencySellItem_Struct); + EQApplicationPacket *outapp = app->Copy(); + AltCurrencySellItem_Struct *sell = (AltCurrencySellItem_Struct*)outapp->pBuffer; + + NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + ItemInst* inst = GetInv().GetItem(sell->slot_id); + if (!inst) { + return; + } + + if (!RuleB(Merchant, EnableAltCurrencySell)) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + uint32 npc_id = tar->GetNPCTypeID(); + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!found) { + return; + } + + if (!inst->IsStackable()) + { + DeleteItemInInventory(sell->slot_id, 0, false); + } + else + { + if (inst->GetCharges() < sell->charges) + { + sell->charges = inst->GetCharges(); + } + + if (sell->charges == 0) + { + Message(13, "Charge mismatch error."); + return; + } + + DeleteItemInInventory(sell->slot_id, sell->charges, false); + cost *= sell->charges; + } + + sell->cost = cost; + + /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + + FastQueuePacket(&outapp); + AddAlternateCurrencyValue(alt_cur_id, cost); + Save(1); + } +} + +void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); + + AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; + NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); + if (tar) { + if (DistNoRoot(*tar) > USE_NPC_RANGE2) + return; + + if (tar->GetClass() != ALT_CURRENCY_MERCHANT) { + return; + } + + uint32 alt_cur_id = tar->GetAltCurrencyType(); + if (alt_cur_id == 0) { + return; + } + + ItemInst *inst = m_inv.GetItem(select->slot_id); + if (!inst) { + return; + } + + const Item_Struct* item = nullptr; + uint32 cost = 0; + uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); + uint32 merchant_id = tar->MerchantType; + + if (RuleB(Merchant, EnableAltCurrencySell)) { + bool found = false; + std::list merlist = zone->merchanttable[merchant_id]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr) { + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tar->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + item = database.GetItem(ml.item); + if (!item) + continue; + + if (item->ID == inst->GetItem()->ID) { + cost = ml.alt_currency_cost; + found = true; + break; + } + } + + if (!found) { + cost = 0; + } + } + else { + cost = 0; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencySellSelection, sizeof(AltCurrencySelectItemReply_Struct)); + AltCurrencySelectItemReply_Struct *reply = (AltCurrencySelectItemReply_Struct*)outapp->pBuffer; + reply->unknown004 = 0xFF; + reply->unknown005 = 0xFF; + reply->unknown006 = 0xFF; + reply->unknown007 = 0xFF; + strcpy(reply->item_name, inst->GetItem()->Name); + reply->cost = cost; + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_Animation(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Animation_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_Animation: got %d, expected %d", app->size, + sizeof(Animation_Struct)); + DumpPacket(app); + return; + } + + Animation_Struct *s = (Animation_Struct *)app->pBuffer; + + //might verify spawn ID, but it wouldent affect anything + + DoAnim(s->action, s->value); + + return; +} + +void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ApplyPoison_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); + DumpPacket(app); + return; + } + uint32 ApplyPoisonSuccessResult = 0; + ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; + const ItemInst* PrimaryWeapon = GetInv().GetItem(MainPrimary); + const ItemInst* SecondaryWeapon = GetInv().GetItem(MainSecondary); + const ItemInst* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; + + bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == ItemTypePoison); + + if (!IsPoison) + { + mlog(SPELLS__CASTING_ERR, "Item used to cast spell effect from a poison item was missing from inventory slot %d " + "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); + + Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); + } + else if (GetClass() == ROGUE) + { + if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == ItemType1HPiercing) || + (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemType1HPiercing)) { + float SuccessChance = (GetSkill(SkillApplyPoison) + GetLevel()) / 400.0f; + double ChanceRoll = MakeRandomFloat(0, 1); + + CheckIncreaseSkill(SkillApplyPoison, nullptr, 10); + + if (ChanceRoll < SuccessChance) { + ApplyPoisonSuccessResult = 1; + // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. + // My thinking was that DEX should be apart of the calculation. + AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX() / 100) + 103); + } + + DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); + + LogFile->write(EQEMuLog::Debug, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); + ApplyPoison_Struct* ApplyPoisonResult = (ApplyPoison_Struct*)outapp->pBuffer; + ApplyPoisonResult->success = ApplyPoisonSuccessResult; + ApplyPoisonResult->inventorySlot = ApplyPoisonData->inventorySlot; + + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_Assist(const EQApplicationPacket *app) +{ + if (app->size != sizeof(EntityId_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); + return; + } + + EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; + Entity* entity = entity_list.GetID(eid->entity_id); + + EQApplicationPacket* outapp = app->Copy(); + eid = (EntityId_Struct*)outapp->pBuffer; + if (RuleB(Combat, AssistNoTargetSelf)) + eid->entity_id = GetID(); + if (entity && entity->IsMob()) { + Mob *assistee = entity->CastToMob(); + if (assistee->GetTarget()) { + Mob *new_target = assistee->GetTarget(); + if (new_target && (GetGM() || + Dist(*assistee) <= TARGETING_RANGE)) { + SetAssistExemption(true); + eid->entity_id = new_target->GetID(); + } + } + } + + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) +{ + if (app->size != sizeof(EntityId_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); return; } - ApproveZone_Struct* azone =(ApproveZone_Struct*)app->pBuffer; - azone->approve=1; QueuePacket(app); return; } -void Client::Handle_Connect_OP_TGB(const EQApplicationPacket *app) +void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) { - if (app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TGB: Expected %i, Got %i", - sizeof(uint32), app->size); + + // This packet is sent by the client when an Augment item information window is opened. + // We respond with an OP_ReadBook containing the type of distiller required to remove the augment. + // The OP_Augment packet includes a window parameter to determine which Item window in the UI the + // text is to be displayed in. out->type = 2 indicates the BookText_Struct contains item information. + // + + if (app->size != sizeof(AugmentInfo_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", + sizeof(AugmentInfo_Struct), app->size); + + DumpPacket(app); + return; } - OPTGB(app); - return; -} + AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*)app->pBuffer; -void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) { - SendAATable(); -} + char *outstring = nullptr; -void Client::CheatDetected(CheatTypes CheatType, float x, float y, float z) -{ //ToDo: Break warp down for special zones. Some zones have special teleportation pads or bad .map files which can trigger the detector without a legit zone request. - switch (CheatType) + const Item_Struct * item = database.GetItem(AugInfo->itemid); + + if (item) { - case MQWarp: //Some zones may still have issues. Database updates will eliminate most if not all problems. - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - Message(13, "Large warp detected."); - char hString[250]; - sprintf(hString, "/MQWarp with location %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQWarpShadowStep: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(SS) with location %.2f, %.2f, %.2f, the target was shadow step exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; - case MQWarpKnockBack: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(KB) with location %.2f, %.2f, %.2f, the target was Knock Back exempt but we still found this suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - break; + MakeAnyLenString(&outstring, "You must use the solvent %s to remove this augment safely.", item->Name); - case MQWarpLight: - if(RuleB(Zone, EnableMQWarpDetector) - && ((this->Admin() < RuleI(Zone, MQWarpExemptStatus) - || (RuleI(Zone, MQWarpExemptStatus)) == -1))) - { - if(RuleB(Zone, MarkMQWarpLT)) - { - char *hString = nullptr; - MakeAnyLenString(&hString, "/MQWarp(LT) with location %.2f, %.2f, %.2f, running fast but not fast enough to get killed, possibly: small warp, speed hack, excessive lag, marked as suspicious.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - } - } - break; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, strlen(outstring) + sizeof(BookText_Struct)); - case MQZone: - if(RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + BookText_Struct *out = (BookText_Struct *)outapp->pBuffer; + + out->window = AugInfo->window; + + out->type = 2; + + out->invslot = 0; + + strcpy(out->booktext, outstring); + + safe_delete_array(outstring); + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(AugmentItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", + sizeof(AugmentItem_Struct), app->size); + return; + } + + // Delegate to tradeskill object to perform combine + AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; + bool deleteItems = false; + if (GetClientVersion() >= EQClientRoF) + { + ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; + + //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); + + // Adding augment + if (in_augment->augment_action == 0) + { + ItemInst *tobe_auged, *auged_with = nullptr; + int8 slot = -1; + Inventory& user_inv = GetInv(); + + uint16 slot_id = in_augment->container_slot; + uint16 aug_slot_id = in_augment->augment_slot; + //Message(13, "%i AugSlot", aug_slot_id); + if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f to %.2f %.2f %.2f", GetX(), GetY(), GetZ(), x, y, z); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); + Message(13, "Error: Invalid Aug Index."); + return; } - break; - case MQZoneUnknownDest: - if(RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + + tobe_auged = user_inv.GetItem(slot_id); + auged_with = user_inv.GetItem(MainCursor); + + if (tobe_auged && auged_with) { - char hString[250]; - sprintf(hString, "/MQZone used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - } - break; - case MQGate: - if (RuleB(Zone, EnableMQGateDetector)&& ((this->Admin() < RuleI(Zone, MQGateExemptStatus) || (RuleI(Zone, MQGateExemptStatus)) == -1))) { - Message(13, "Illegal gate request."); - char hString[250]; - sprintf(hString, "/MQGate used at %.2f, %.2f, %.2f", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - if(zone) + if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && + (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) { - this->SetZone(this->GetZoneID(), zone->GetInstanceID()); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. + tobe_auged->PutAugment(in_augment->augment_index, *auged_with); + + ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); + return; + } + + itemOneToPush = tobe_auged->Clone(); + // Must push items after the items in inventory are deleted - necessary due to lore items... + if (itemOneToPush) + { + DeleteItemInInventory(slot_id, 0, true); + DeleteItemInInventory(MainCursor, 0, true); + if (PutItemInInventory(slot_id, *itemOneToPush, true)) + { + CalcBonuses(); + //Message(13, "Sucessfully added an augment to your item!"); + return; + } + else + { + Message(13, "Error: No available slot for end result. Please free up some bag space."); + } + } + else + { + Message(13, "Error in cloning item for augment. Aborted."); + } + } else { - this->SetZone(this->GetZoneID(), 0); //Prevent the player from zoning, place him back in the zone where he tried to originally /gate. - + Message(13, "Error: No available slot for augment in that item."); } } - break; - case MQGhost: //Not currently implemented, but the framework is in place - just needs detection scenarios identified - if (RuleB(Zone, EnableMQGhostDetector) && ((this->Admin() < RuleI(Zone, MQGhostExemptStatus) || (RuleI(Zone, MQGhostExemptStatus)) == -1))) { - database.SetMQDetectionFlag(this->account_name, this->name, "/MQGhost", zone->GetShortName()); + } + else if (in_augment->augment_action == 1) + { + ItemInst *tobe_auged, *auged_with = nullptr; + int8 slot = -1; + Inventory& user_inv = GetInv(); + + uint16 slot_id = in_augment->container_slot; + uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot + if (slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) + { + Message(13, "Error: Invalid Aug Index."); + return; } - break; - default: - char *hString = nullptr; - MakeAnyLenString(&hString, "Unhandled HackerDetection flag with location %.2f, %.2f, %.2f.", GetX(), GetY(), GetZ()); - database.SetMQDetectionFlag(this->account_name, this->name, hString, zone->GetShortName()); - safe_delete_array(hString); - break; + + tobe_auged = user_inv.GetItem(slot_id); + auged_with = user_inv.GetItem(aug_slot_id); + + ItemInst *old_aug = nullptr; + if (!auged_with) + return; + const uint32 id = auged_with->GetID(); + ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); + if (aug) { + std::vector args; + args.push_back(aug); + parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); + + args.assign(1, tobe_auged); + + args.push_back(false); + + parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); + } + else + { + Message(13, "Error: Could not find augmentation at index %i. Aborting."); + return; + } + old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); + + itemOneToPush = tobe_auged->Clone(); + if (old_aug) + itemTwoToPush = old_aug->Clone(); + if (itemOneToPush && itemTwoToPush && auged_with) + { + DeleteItemInInventory(slot_id, 0, true); + DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); + if (!PutItemInInventory(slot_id, *itemOneToPush, true)) + { + Message(15, "Shouldn't happen, contact an admin!"); + } + + if (PutItemInInventory(MainCursor, *itemTwoToPush, true)) + { + CalcBonuses(); + //Message(15, "Successfully removed an augmentation!"); + } + } + } } + else + { + Object::HandleAugmentation(this, in_augment, m_tradeskill_object); + } + return; +} + +void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) +{ + if (app->size != 4) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); + return; + } + + if (app->pBuffer[0] == 0) + { + auto_attack = false; + if (IsAIControlled()) + return; + attack_timer.Disable(); + ranged_timer.Disable(); + attack_dw_timer.Disable(); + + aa_los_me.x = 0; + aa_los_me.y = 0; + aa_los_me.z = 0; + aa_los_me_heading = 0; + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = nullptr; + } + else if (app->pBuffer[0] == 1) + { + auto_attack = true; + auto_fire = false; + if (IsAIControlled()) + return; + SetAttackTimer(); + + if (GetTarget()) + { + aa_los_them_mob = GetTarget(); + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); + aa_los_them.x = aa_los_them_mob->GetX(); + aa_los_them.y = aa_los_them_mob->GetY(); + aa_los_them.z = aa_los_them_mob->GetZ(); + los_status = CheckLosFN(aa_los_them_mob); + los_status_facing = IsFacingMob(aa_los_them_mob); + } + else + { + aa_los_me.x = GetX(); + aa_los_me.y = GetY(); + aa_los_me.z = GetZ(); + aa_los_me_heading = GetHeading(); + aa_los_them.x = 0; + aa_los_them.y = 0; + aa_los_them.z = 0; + aa_los_them_mob = nullptr; + los_status = false; + los_status_facing = false; + } + } +} + +void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) +{ + if (app->size != sizeof(bool)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); + DumpPacket(app); + return; + } + bool *af = (bool*)app->pBuffer; + auto_fire = *af; + auto_attack = false; + SetAttackTimer(); +} + +void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) +{ + + // Although there are three different structs for OP_Bandolier, they are all the same size. + // + if (app->size != sizeof(BandolierCreate_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Bandolier expected %i got %i", + sizeof(BandolierCreate_Struct), app->size); + DumpPacket(app); + return; + } + + BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; + + switch (bs->action) { + case BandolierCreate: + CreateBandolier(app); + break; + case BandolierRemove: + RemoveBandolier(app); + break; + case BandolierSet: + SetBandolier(app); + break; + default: + LogFile->write(EQEMuLog::Debug, "Uknown Bandolier action %i", bs->action); + + } +} + +void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BankerChange_Struct) && app->size != 4) //Titanium only sends 4 Bytes for this + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); + DumpPacket(app); + return; + } + + uint32 distance = 0; + NPC *banker = entity_list.GetClosestBanker(this, distance); + + if (!banker || distance > USE_NPC_RANGE2) + { + char *hacked_string = nullptr; + MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(money) but %s is non-existant or too far away (%u units).", + banker ? banker->GetName() : "UNKNOWN NPC", distance); + database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); + safe_delete_array(hacked_string); + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BankerChange, nullptr, sizeof(BankerChange_Struct)); + BankerChange_Struct *bc = (BankerChange_Struct *)outapp->pBuffer; + + if (m_pp.platinum < 0) + m_pp.platinum = 0; + if (m_pp.gold < 0) + m_pp.gold = 0; + if (m_pp.silver < 0) + m_pp.silver = 0; + if (m_pp.copper < 0) + m_pp.copper = 0; + + if (m_pp.platinum_bank < 0) + m_pp.platinum_bank = 0; + if (m_pp.gold_bank < 0) + m_pp.gold_bank = 0; + if (m_pp.silver_bank < 0) + m_pp.silver_bank = 0; + if (m_pp.copper_bank < 0) + m_pp.copper_bank = 0; + + uint64 cp = static_cast(m_pp.copper) + + (static_cast(m_pp.silver) * 10) + + (static_cast(m_pp.gold) * 100) + + (static_cast(m_pp.platinum) * 1000); + + m_pp.copper = cp % 10; + cp /= 10; + m_pp.silver = cp % 10; + cp /= 10; + m_pp.gold = cp % 10; + cp /= 10; + m_pp.platinum = cp; + + cp = static_cast(m_pp.copper_bank) + + (static_cast(m_pp.silver_bank) * 10) + + (static_cast(m_pp.gold_bank) * 100) + + (static_cast(m_pp.platinum_bank) * 1000); + + m_pp.copper_bank = cp % 10; + cp /= 10; + m_pp.silver_bank = cp % 10; + cp /= 10; + m_pp.gold_bank = cp % 10; + cp /= 10; + m_pp.platinum_bank = cp; + + bc->copper = m_pp.copper; + bc->silver = m_pp.silver; + bc->gold = m_pp.gold; + bc->platinum = m_pp.platinum; + + bc->copper_bank = m_pp.copper_bank; + bc->silver_bank = m_pp.silver_bank; + bc->gold_bank = m_pp.gold_bank; + bc->platinum_bank = m_pp.platinum_bank; + + FastQueuePacket(&outapp); + + return; +} + +void Client::Handle_OP_Barter(const EQApplicationPacket *app) +{ + + if (app->size < 4) + { + LogFile->write(EQEMuLog::Debug, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); + DumpPacket(app); + return; + } + + char* Buf = (char *)app->pBuffer; + + // The first 4 bytes of the packet determine the action. A lot of Barter packets require the + // packet the client sent, sent back to it as an acknowledgement. + // + uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf); + + _pkt(TRADING__BARTER, app); + + switch (Action) + { + + case Barter_BuyerSearch: + { + BuyerItemSearch(app); + break; + } + + case Barter_SellerSearch: + { + BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer; + SendBuyerResults(bsr->SearchString, bsr->SearchID); + break; + } + + case Barter_BuyerModeOn: + { + if (!Trader) { + ToggleBuyerMode(true); + } + else { + Buf = (char *)app->pBuffer; + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff); + Message(13, "You cannot be a Trader and Buyer at the same time."); + } + QueuePacket(app); + break; + } + + case Barter_BuyerModeOff: + { + QueuePacket(app); + ToggleBuyerMode(false); + break; + } + + case Barter_BuyerItemUpdate: + { + UpdateBuyLine(app); + break; + } + + case Barter_BuyerItemRemove: + { + BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer; + database.RemoveBuyLine(CharacterID(), bris->BuySlot); + QueuePacket(app); + break; + } + + case Barter_SellItem: + { + SellToBuyer(app); + break; + } + + case Barter_BuyerInspectBegin: + { + ShowBuyLines(app); + break; + } + + case Barter_BuyerInspectEnd: + { + BuyerInspectRequest_Struct* bir = (BuyerInspectRequest_Struct*)app->pBuffer; + Client *Buyer = entity_list.GetClientByID(bir->BuyerID); + if (Buyer) + Buyer->WithCustomer(0); + + break; + } + + case Barter_BarterItemInspect: + { + BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bislr->ItemID); + + if (!item) + Message(13, "Error: This item does not exist!"); + else + { + ItemInst* inst = database.CreateItem(item); + if (inst) + { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + } + break; + } + + case Barter_Welcome: + { + SendBazaarWelcome(); + break; + } + + case Barter_WelcomeMessageUpdate: + { + BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer; + SetBuyerWelcomeMessage(bwmu->WelcomeMessage); + break; + } + + case Barter_BuyerItemInspect: + { + BuyerItemSearchLinkRequest_Struct* bislr = (BuyerItemSearchLinkRequest_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bislr->ItemID); + + if (!item) + Message(13, "Error: This item does not exist!"); + else + { + ItemInst* inst = database.CreateItem(item); + if (inst) + { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + } + break; + } + + case Barter_Unknown23: + { + // Sent by SoD client for no discernible reason. + break; + } + + default: + Message(13, "Unrecognised Barter action."); + _log(TRADING__BARTER, "Unrecognised Barter Action %i", Action); + + } +} + +void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BazaarInspect_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", + sizeof(BazaarInspect_Struct), app->size); + return; + } + + BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer; + + const Item_Struct* item = database.GetItem(bis->ItemID); + + if (!item) { + Message(13, "Error: This item does not exist!"); + return; + } + + ItemInst* inst = database.CreateItem(item); + + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + + return; +} + +void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) +{ + _pkt(TRADING__PACKETS, app); + + if (app->size == sizeof(BazaarSearch_Struct)) { + + BazaarSearch_Struct* bss = (BazaarSearch_Struct*)app->pBuffer; + + this->SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type, + bss->Name, bss->MinPrice * 1000, bss->MaxPrice * 1000); + } + else if (app->size == sizeof(BazaarWelcome_Struct)) { + + BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer; + + if (bws->Beginning.Action == BazaarWelcome) + SendBazaarWelcome(); + } + else if (app->size == sizeof(NewBazaarInspect_Struct)) { + + NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer; + + Client *c = entity_list.GetClientByName(nbis->Name); + if (c) { + ItemInst* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber); + if (inst) + SendItemPacket(0, inst, ItemPacketViewLink); + } + return; + } + else { + _log(TRADING__CLIENT, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); + LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); + } + + return; +} + +void Client::Handle_OP_Begging(const EQApplicationPacket *app) +{ + if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) + { + Message(13, "Ability recovery time not yet met."); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); + BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; + brs->Result = 0; + FastQueuePacket(&outapp); + return; + } + + if (!HasSkill(SkillBegging) || !GetTarget()) + return; + + if (GetTarget()->GetClass() == LDON_TREASURE) + return; + + p_timers.Start(pTimerBeggingPickPocket, 8); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); + BeggingResponse_Struct *brs = (BeggingResponse_Struct*)outapp->pBuffer; + + brs->Result = 0; // Default, Fail. + if (GetTarget() == this) + { + FastQueuePacket(&outapp); + return; + } + + int RandomChance = MakeRandomInt(0, 100); + + int ChanceToAttack = 0; + + if (GetLevel() > GetTarget()->GetLevel()) + ChanceToAttack = MakeRandomInt(0, 15); + else + ChanceToAttack = MakeRandomInt(((this->GetTarget()->GetLevel() - this->GetLevel()) * 10) - 5, ((this->GetTarget()->GetLevel() - this->GetLevel()) * 10)); + + if (ChanceToAttack < 0) + ChanceToAttack = -ChanceToAttack; + + if (RandomChance < ChanceToAttack) + { + GetTarget()->Attack(this); + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + uint16 CurrentSkill = GetSkill(SkillBegging); + + float ChanceToBeg = ((float)(CurrentSkill / 700.0f) + 0.15f) * 100; + + if (RandomChance < ChanceToBeg) + { + brs->Amount = MakeRandomInt(1, 10); + // This needs some work to determine how much money they can beg, based on skill level etc. + if (CurrentSkill < 50) + { + brs->Result = 4; // Copper + AddMoneyToPP(brs->Amount, false); + } + else + { + brs->Result = 3; // Silver + AddMoneyToPP(brs->Amount * 10, false); + } + + } + QueuePacket(outapp); + safe_delete(outapp); + CheckIncreaseSkill(SkillBegging, nullptr, -10); +} + +void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BindWound_Struct)){ + LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet"); + DumpPacket(app); + } + BindWound_Struct* bind_in = (BindWound_Struct*)app->pBuffer; + Mob* bindmob = entity_list.GetMob(bind_in->to); + if (!bindmob){ + LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); + } + else { + LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); + BindWound(bindmob, true); + } + return; +} + +void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != sizeof(BlockedBuffs_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BlockedBuffs expected %i got %i", + sizeof(BlockedBuffs_Struct), app->size); + + DumpPacket(app); + + return; + } + + std::set::iterator Iterator; + + BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; + + std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; + + if (bbs->Initialise == 1) + { + BlockedBuffs->clear(); + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + { + if ((bbs->SpellID[i] > 0) && IsBeneficialSpell(bbs->SpellID[i])) + { + if (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end()) + BlockedBuffs->insert(bbs->SpellID[i]); + } + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = -1; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 1; + obbs->Flags = 0x54; + obbs->Count = BlockedBuffs->size(); + + unsigned int Element = 0; + + Iterator = BlockedBuffs->begin(); + + while (Iterator != BlockedBuffs->end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + return; + } + + if ((bbs->Initialise == 0) && (bbs->Count > 0)) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = -1; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 0; + obbs->Flags = 0x54; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + { + if (!IsBeneficialSpell(bbs->SpellID[i])) + continue; + + if ((BlockedBuffs->size() < BLOCKED_BUFF_COUNT) && (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end())) + BlockedBuffs->insert(bbs->SpellID[i]); + } + obbs->Count = BlockedBuffs->size(); + + Iterator = BlockedBuffs->begin(); + + unsigned int Element = 0; + + while (Iterator != BlockedBuffs->end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) +{ + + if (app->size <= 5) + return; + + char *boatname; + boatname = new char[app->size - 3]; + memset(boatname, 0, app->size - 3); + memcpy(boatname, app->pBuffer, app->size - 4); + + Mob* boat = entity_list.GetMob(boatname); + if (boat) + this->BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat + safe_delete_array(boatname); + return; +} + +void Client::Handle_OP_Buff(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SpellBuffFade_Struct)) + { + LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size); + DumpPacket(app); + return; + } + + SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*)app->pBuffer; + uint32 spid = sbf->spellid; + mlog(SPELLS__BUFFS, "Client requested that buff with spell id %d be canceled.", spid); + + //something about IsDetrimentalSpell() crashes this portion of code.. + //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and + //isdetrimentalspell() is much more complex + if (spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].goodEffect == 0))) + QueuePacket(app); + else + BuffFadeBySpellID(spid); + + return; +} + +void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) +{ + // In SoD, this is used for clicking off Pet Buffs only. In Underfoot, it is used both for Client and Pets + // The payload contains buffslot and EntityID only, so we must check if the EntityID is ours or our pets. + // + VERIFY_PACKET_LENGTH(OP_BuffRemoveRequest, app, BuffRemoveRequest_Struct); + + BuffRemoveRequest_Struct *brrs = (BuffRemoveRequest_Struct*)app->pBuffer; + + Mob *m = nullptr; + + if (brrs->EntityID == GetID()) + m = this; + else if (brrs->EntityID == GetPetID()) + m = GetPet(); + + if (!m) + return; + + if (brrs->SlotID > (uint32)m->GetMaxTotalSlots()) + return; + + uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); + + if (SpellID && IsBeneficialSpell(SpellID)) + m->BuffFadeBySlot(brrs->SlotID, true); +} + +void Client::Handle_OP_Bug(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BugStruct)) + printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); + else{ + BugStruct* bug = (BugStruct*)app->pBuffer; + database.UpdateBug(bug); + } + return; +} + +void Client::Handle_OP_Camp(const EQApplicationPacket *app) +{ +#ifdef BOTS + // This block is necessary to clean up any bot objects owned by a Client + Bot::BotHealRotationsClear(this); + Bot::BotOrderCampAll(this); +#endif + if (IsLFP()) + worldserver.StopLFP(CharacterID()); + + if (GetGM()) + { + OnDisconnect(true); + return; + } + camp_timer.Start(29000, true); + return; +} + +void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(CancelTask_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_CancelTask expected %i got %i", + sizeof(CancelTask_Struct), app->size); + DumpPacket(app); + return; + } + CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; + + if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->CancelTask(this, cts->SequenceNumber); +} + +void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CancelTrade_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); + return; + } + Mob* with = trade->With(); + if (with && with->IsClient()) { + CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; + + // Forward cancel packet to other client + msg->fromid = with->GetID(); + //msg->action = 1; + + with->CastToClient()->QueuePacket(app); + + // Put trade items/cash back into inventory + FinishTrade(this); + trade->Reset(); + } + else if (with){ + CancelTrade_Struct* msg = (CancelTrade_Struct*)app->pBuffer; + msg->fromid = with->GetID(); + QueuePacket(app); + FinishTrade(this); + trade->Reset(); + } + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CastSpell_Struct)) { + std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; + return; + } + if (IsAIControlled()) { + this->Message_StringID(13, NOT_IN_CONTROL); + //Message(13, "You cant cast right now, you arent in control of yourself!"); + return; + } + + CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; + +#ifdef _EQDEBUG + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(uint32*)castspell->cs_unknown); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); + LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(uint16*)castspell->cs_unknown, *(uint16*)castspell->cs_unknown + sizeof(uint16)); +#endif + LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); + + std::cout << "OP_CastSpell " << castspell->slot << " spell " << castspell->spell_id << " inventory slot " << castspell->inventoryslot << "\n" << std::endl; + + /* Memorized Spell */ + if (m_pp.mem_spells[castspell->slot] && m_pp.mem_spells[castspell->slot] == castspell->spell_id){ + + uint16 spell_to_cast = 0; + if (castspell->slot < MAX_PP_MEMSPELL) { + spell_to_cast = m_pp.mem_spells[castspell->slot]; + if (spell_to_cast != castspell->spell_id) { + InterruptSpell(castspell->spell_id); //CHEATER!!! + return; + } + } + else if (castspell->slot >= MAX_PP_MEMSPELL) { + InterruptSpell(); + return; + } + + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + /* Spell Slot or Potion Belt Slot */ + else if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast + { + //discipline, using the item spell slot + if (castspell->inventoryslot == INVALID_INDEX) { + if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { + LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); + InterruptSpell(castspell->spell_id); + } + return; + } + else if (m_inv.SupportsClickCasting(castspell->inventoryslot) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check + { + // packet field types will be reviewed as packet transistions occur -U + const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field + //bool cancast = true; + if (inst && inst->IsType(ItemClassCommon)) + { + const Item_Struct* item = inst->GetItem(); + if (item->Click.Effect != (uint32)castspell->spell_id) + { + database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName()); + InterruptSpell(castspell->spell_id); //CHEATER!! + return; + } + + if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) + { + if (item->Click.Level2 > 0) + { + if (GetLevel() >= item->Click.Level2) + { + ItemInst* p_inst = (ItemInst*)inst; + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); + + if (i == 0) { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + else { + InterruptSpell(castspell->spell_id); + return; + } + } + else + { + database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName()); + Message(0, "Error: level not high enough.", castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + else + { + ItemInst* p_inst = (ItemInst*)inst; + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); + + if (i == 0) { + CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); + } + else { + InterruptSpell(castspell->spell_id); + return; + } + } + } + else + { + Message(0, "Error: unknown item->Click.Type (0x%02x)", item->Click.Type); + } + } + else + { + Message(0, "Error: item not found in inventory slot #%i", castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + else + { + Message(0, "Error: castspell->inventoryslot >= %i (0x%04x)", MainCursor, castspell->inventoryslot); + InterruptSpell(castspell->spell_id); + } + } + /* Discipline */ + else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { + if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { + printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); + InterruptSpell(castspell->spell_id); + return; + } + } + /* ABILITY cast (LoH and Harm Touch) */ + else if (castspell->slot == ABILITY_SPELL_SLOT) { + uint16 spell_to_cast = 0; + + if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { + if (!p_timers.Expired(&database, pTimerLayHands)) { + Message(13, "Ability recovery time not yet met."); + InterruptSpell(castspell->spell_id); + return; + } + spell_to_cast = SPELL_LAY_ON_HANDS; + p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); + } + else if ((castspell->spell_id == SPELL_HARM_TOUCH + || castspell->spell_id == SPELL_HARM_TOUCH2) && GetClass() == SHADOWKNIGHT) { + if (!p_timers.Expired(&database, pTimerHarmTouch)) { + Message(13, "Ability recovery time not yet met."); + InterruptSpell(castspell->spell_id); + return; + } + + // determine which version of HT we are casting based on level + if (GetLevel() < 40) + spell_to_cast = SPELL_HARM_TOUCH; + else + spell_to_cast = SPELL_HARM_TOUCH2; + + p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); + } + + if (spell_to_cast > 0) // if we've matched LoH or HT, cast now + CastSpell(spell_to_cast, castspell->target_id, castspell->slot); + } + return; +} + +void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) +{ + ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app->pBuffer; + + if (app->size < sizeof(ChannelMessage_Struct)) { + std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl; + return; + } + if (IsAIControlled()) { + Message(13, "You try to speak but cant move your mouth!"); + return; + } + + ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); + return; +} + +void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != 1) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); + + DumpPacket(app); + + return; + } + + bool Pet = app->pBuffer[0]; + + if (Pet) + PetBlockedBuffs.clear(); + else + PlayerBlockedBuffs.clear(); + + QueuePacket(app); +} + +void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) +{ + + if (app->size != 0) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", + app->size); + + DumpPacket(app); + + return; + } + + Group *g = GetGroup(); + + if (g) + g->ClearAllNPCMarks(); +} + +void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) +{ + ChangeLastName(""); +} + +void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClickDoor_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); + return; + } + ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; + Doors* currentdoor = entity_list.FindDoor(cd->doorid); + if (!currentdoor) + { + Message(0, "Unable to find door, please notify a GM (DoorID: %i).", cd->doorid); + return; + } + + char buf[20]; + snprintf(buf, 19, "%u", cd->doorid); + buf[19] = '\0'; + std::vector args; + args.push_back(currentdoor); + parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); + + currentdoor->HandleClick(this, 0); + return; +} + +void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClickObject_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", + sizeof(ClickObject_Struct), app->size); + return; + } + + ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer; + Entity* entity = entity_list.GetID(click_object->drop_id); + if (entity && entity->IsObject()) { + Object* object = entity->CastToObject(); + + object->HandleClick(this, click_object); + + std::vector args; + args.push_back(object); + + char buf[10]; + snprintf(buf, 9, "%u", click_object->drop_id); + buf[9] = '\0'; + parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); + } + + // Observed in RoF after OP_ClickObjectAction: + //EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + //QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) +{ + if (app->size == 0) { + // RoF sends this packet 0 sized when switching from auto-combine to experiment windows. + // Not completely sure if 0 sized is for this or for closing objects as commented out below + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + + return; + + // RoF sends a 0 sized packet for closing objects + /* + Object* object = GetTradeskillObject(); + if (object) { + object->CastToObject()->Close(); + } + */ + } + else + { + if (app->size != sizeof(ClickObjectAction_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", + sizeof(ClickObjectAction_Struct), app->size); + return; + } + + ClickObjectAction_Struct* oos = (ClickObjectAction_Struct*)app->pBuffer; + Entity* entity = entity_list.GetEntityObject(oos->drop_id); + if (entity && entity->IsObject()) { + Object* object = entity->CastToObject(); + if (oos->open == 0) { + object->Close(); + } + else { + LogFile->write(EQEMuLog::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); + } + } + else { + LogFile->write(EQEMuLog::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); + } + } + + SetTradeskillObject(nullptr); + + EQApplicationPacket end_trade1(OP_FinishWindow, 0); + QueuePacket(&end_trade1); + + EQApplicationPacket end_trade2(OP_FinishWindow2, 0); + QueuePacket(&end_trade2); + return; +} + +void Client::Handle_OP_ClientError(const EQApplicationPacket *app) +{ + ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; + LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); + LogFile->write(EQEMuLog::Error, "Error message:%s", error->message); + return; +} + +void Client::Handle_OP_ClientTimeStamp(const EQApplicationPacket *app) +{ + return; } void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) @@ -1290,69 +4631,38 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) return; } -void Client::Handle_OP_AutoAttack(const EQApplicationPacket *app) +/* +void Client::Handle_OP_CloseContainer(const EQApplicationPacket *app) { - if (app->size != 4) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size); - return; - } - - if (app->pBuffer[0] == 0) - { - auto_attack = false; - if (IsAIControlled()) - return; - attack_timer.Disable(); - ranged_timer.Disable(); - attack_dw_timer.Disable(); - - aa_los_me.x = 0; - aa_los_me.y = 0; - aa_los_me.z = 0; - aa_los_me_heading = 0; - aa_los_them.x = 0; - aa_los_them.y = 0; - aa_los_them.z = 0; - aa_los_them_mob = nullptr; - } - else if (app->pBuffer[0] == 1) - { - auto_attack = true; - auto_fire = false; - if (IsAIControlled()) - return; - SetAttackTimer(); - - if(GetTarget()) - { - aa_los_them_mob = GetTarget(); - aa_los_me.x = GetX(); - aa_los_me.y = GetY(); - aa_los_me.z = GetZ(); - aa_los_me_heading = GetHeading(); - aa_los_them.x = aa_los_them_mob->GetX(); - aa_los_them.y = aa_los_them_mob->GetY(); - aa_los_them.z = aa_los_them_mob->GetZ(); - los_status = CheckLosFN(aa_los_them_mob); - los_status_facing = IsFacingMob(aa_los_them_mob); - } - else - { - aa_los_me.x = GetX(); - aa_los_me.y = GetY(); - aa_los_me.z = GetZ(); - aa_los_me_heading = GetHeading(); - aa_los_them.x = 0; - aa_los_them.y = 0; - aa_los_them.z = 0; - aa_los_them_mob = nullptr; - los_status = false; - los_status_facing = false; - } - } +if (app->size != sizeof(CloseContainer_Struct)) { +LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i", +sizeof(CloseContainer_Struct), app->size); +return; } -void Client::Handle_OP_AutoAttack2(const EQApplicationPacket *app) +SetTradeskillObject(nullptr); + +ClickObjectAck_Struct* oos = (ClickObjectAck_Struct*)app->pBuffer; +Entity* entity = entity_list.GetEntityObject(oos->drop_id); +if (entity && entity->IsObject()) { +Object* object = entity->CastToObject(); +object->Close(); +} +return; +} +*/ + +void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CombatAbility_Struct)) { + std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl; + return; + } + OPCombatAbility(app); + return; +} + +void Client::Handle_OP_ConfirmDelete(const EQApplicationPacket* app) { return; } @@ -1400,494 +4710,158 @@ void Client::Handle_OP_ConsentDeny(const EQApplicationPacket *app) return; } -void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) +void Client::Handle_OP_Consider(const EQApplicationPacket *app) { - Handle_OP_TargetCommand(app); -} + if (app->size != sizeof(Consider_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider expected %i got %i", sizeof(Consider_Struct), app->size); + return; + } + Consider_Struct* conin = (Consider_Struct*)app->pBuffer; + Mob* tmob = entity_list.GetMob(conin->targetid); + if (tmob == 0) + return; -void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); + if (tmob->GetClass() == LDON_TREASURE) + { + Message(15, "%s", tmob->GetCleanName()); return; } - if(GetTarget()) - { - GetTarget()->IsTargeted(-1); - } - - // Locate and cache new target - ClientTarget_Struct* ct=(ClientTarget_Struct*)app->pBuffer; - pClientSideTarget = ct->new_target; - if(!IsAIControlled()) - { - Mob *nt = entity_list.GetMob(ct->new_target); - if(nt) - { - SetTarget(nt); - if ((nt->IsClient() && !nt->CastToClient()->GetPVP()) || - (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || - (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) - nt->SendBuffsToClient(this); - } - else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - - Group *g = GetGroup(); - - if(g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(0); - - if(g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(0); - - if(g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(0); - - return; - } - } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Consider, sizeof(Consider_Struct)); + Consider_Struct* con = (Consider_Struct*)outapp->pBuffer; + con->playerid = GetID(); + con->targetid = conin->targetid; + if (tmob->IsNPC()) + con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity, (tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction() : 0, tmob); // rembrant, Dec. 20, 2001; TODO: Send the players proper deity else - { - SetTarget(nullptr); - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); - return; + con->faction = 1; + con->level = GetLevelCon(tmob->GetLevel()); + if (zone->IsPVPZone()) { + if (!tmob->IsNPC()) + con->pvpcon = tmob->CastToClient()->GetPVP(); } - // HoTT - if (GetTarget() && GetTarget()->GetTarget()) + // Mongrel: If we're feigned show NPC as indifferent + if (tmob->IsNPC()) { - SetHoTT(GetTarget()->GetTarget()->GetID()); - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - } - else - { - SetHoTT(0); - UpdateXTargetType(TargetsTarget, nullptr); + if (GetFeigned()) + con->faction = FACTION_INDIFFERENT; } - Group *g = GetGroup(); - - if(g && g->HasRole(this, RoleAssist)) - g->SetGroupAssistTarget(GetTarget()); - - if(g && g->HasRole(this, RoleTank)) - g->SetGroupTankTarget(GetTarget()); - - if(g && g->HasRole(this, RolePuller)) - g->SetGroupPullerTarget(GetTarget()); - - // For /target, send reject or success packet - if (app->GetOpcode() == OP_TargetCommand) { - if (GetTarget() && !GetTarget()->CastToMob()->IsInvisible(this) && (DistNoRoot(*GetTarget()) <= TARGETING_RANGE*TARGETING_RANGE || GetGM())) { - if(GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - //Targeting something we shouldn't with /target - //but the client allows this without MQ so you don't flag it - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if(GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - QueuePacket(app); - EQApplicationPacket hp_app; - GetTarget()->IsTargeted(1); - GetTarget()->CreateHPPacket(&hp_app); - QueuePacket(&hp_app, false); - } - else + if (!(con->faction == FACTION_SCOWLS)) + { + if (tmob->IsNPC()) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); - outapp->pBuffer[0] = 0x2f; - outapp->pBuffer[1] = 0x01; - outapp->pBuffer[4] = 0x0d; - if(GetTarget()) - { - SetTarget(nullptr); - } - QueuePacket(outapp); - safe_delete(outapp); + if (tmob->CastToNPC()->IsOnHatelist(this)) + con->faction = FACTION_THREATENLY; } } - else - { - if(GetTarget()) - { - if(GetGM()) - { - GetTarget()->IsTargeted(1); - return; - } - else if(IsAssistExempted()) - { - GetTarget()->IsTargeted(1); - SetAssistExemption(false); - return; - } - else if(GetTarget()->IsClient()) - { - //make sure this client is in our raid/group - GetTarget()->IsTargeted(1); - return; - } - else if(GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special - || GetTarget()->GetBodyType() == BT_NoTarget) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something untargetable, %s bodytype: %i\n", - GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget((Mob*)nullptr); - return; - } - else if(IsPortExempted()) - { - GetTarget()->IsTargeted(1); - return; - } - else if(IsSenseExempted()) - { - GetTarget()->IsTargeted(1); - SetSenseExemption(false); - return; - } - else if(IsXTarget(GetTarget())) - { - GetTarget()->IsTargeted(1); - return; - } - else if(GetBindSightTarget()) - { - if(GetBindSightTarget()->DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - if(DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - } - } - else if(DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," - " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), - (zone->newzone_data.maxclip*zone->newzone_data.maxclip), - GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - SetTarget(nullptr); - return; - } - GetTarget()->IsTargeted(1); - } + if (con->faction == FACTION_APPREHENSIVE) { + con->faction = FACTION_SCOWLS; } + else if (con->faction == FACTION_DUBIOUS) { + con->faction = FACTION_THREATENLY; + } + else if (con->faction == FACTION_SCOWLS) { + con->faction = FACTION_APPREHENSIVE; + } + else if (con->faction == FACTION_THREATENLY) { + con->faction = FACTION_DUBIOUS; + } + + mod_consider(tmob, con); + + QueuePacket(outapp); + // only wanted to check raid target once + // and need con to still be around so, do it here! + if (tmob->IsRaidTarget()) { + uint32 color = 0; + switch (con->level) { + case CON_GREEN: + color = 2; + break; + case CON_LIGHTBLUE: + color = 10; + break; + case CON_BLUE: + color = 4; + break; + case CON_WHITE: + color = 10; + break; + case CON_YELLOW: + color = 15; + break; + case CON_RED: + color = 13; + break; + } + SendColoredText(color, std::string("This creature would take an army to defeat!")); + } + safe_delete(outapp); return; } -void Client::Handle_OP_Shielding(const EQApplicationPacket *app) +void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) { - if (app->size != sizeof(Shielding_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); + if (app->size != sizeof(Consider_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); return; } - if(GetClass() != WARRIOR) - { - return; - } - - if (shield_target) - { - entity_list.MessageClose_StringID(this, false, 100, 0, - END_SHIELDING, GetName(), shield_target->GetName()); - for (int y = 0; y < 2; y++) - { - if (shield_target->shielder[y].shielder_id == GetID()) - { - shield_target->shielder[y].shielder_id = 0; - shield_target->shielder[y].shielder_bonus = 0; - } + Consider_Struct* conin = (Consider_Struct*)app->pBuffer; + Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); + if (tcorpse && tcorpse->IsNPCCorpse()) { + uint32 min; uint32 sec; uint32 ttime; + if ((ttime = tcorpse->GetDecayTime()) != 0) { + sec = (ttime / 1000) % 60; // Total seconds + min = (ttime / 60000) % 60; // Total seconds / 60 drop .00 + char val1[20] = { 0 }; + char val2[20] = { 0 }; + Message_StringID(10, CORPSE_DECAY1, ConvertArray(min, val1), ConvertArray(sec, val2)); + } + else { + Message_StringID(10, CORPSE_DECAY_NOW); } } - Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; - shield_target = entity_list.GetMob(shield->target_id); - bool ack = false; - ItemInst* inst = GetInv().GetItem(MainSecondary); - if (!shield_target) - return; - if (inst) - { - const Item_Struct* shield = inst->GetItem(); - if (shield && shield->ItemType == ItemTypeShield) - { - for (int x = 0; x < 2; x++) - { - if (shield_target->shielder[x].shielder_id == 0) - { - entity_list.MessageClose_StringID(this ,false, 100, 0, - START_SHIELDING, GetName(), shield_target->GetName()); - shield_target->shielder[x].shielder_id = GetID(); - int shieldbonus = shield->AC*2; - switch (GetAA(197)) - { - case 1: - shieldbonus = shieldbonus * 115 / 100; - break; - case 2: - shieldbonus = shieldbonus * 125 / 100; - break; - case 3: - shieldbonus = shieldbonus * 150 / 100; - break; - } - shield_target->shielder[x].shielder_bonus = shieldbonus; - shield_timer.Start(); - ack = true; - break; - } + else if (tcorpse && tcorpse->IsPlayerCorpse()) { + uint32 day, hour, min, sec, ttime; + if ((ttime = tcorpse->GetDecayTime()) != 0) { + sec = (ttime / 1000) % 60; // Total seconds + min = (ttime / 60000) % 60; // Total seconds + hour = (ttime / 3600000) % 24; // Total hours + day = ttime / 86400000; // Total Days + if (day) + Message(0, "This corpse will decay in %i days, %i hours, %i minutes and %i seconds.", day, hour, min, sec); + else if (hour) + Message(0, "This corpse will decay in %i hours, %i minutes and %i seconds.", hour, min, sec); + else + Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); + + Message(0, "This corpse %s be resurrected.", tcorpse->Rezzed() ? "cannot" : "can"); + /* + hour = 0; + + if((ttime = tcorpse->GetResTime()) != 0) { + sec = (ttime/1000)%60; // Total seconds + min = (ttime/60000)%60; // Total seconds + hour = (ttime/3600000)%24; // Total hours + if(hour) + Message(0, "This corpse can be resurrected for %i hours, %i minutes and %i seconds.", hour, min, sec); + else + Message(0, "This corpse can be resurrected for %i minutes and %i seconds.", min, sec); } + else { + Message_StringID(0, CORPSE_TOO_OLD); + } + */ } - else - { - Message(0,"You must have a shield equipped to shield a target!"); - shield_target = 0; - return; + else { + Message_StringID(10, CORPSE_DECAY_NOW); } } - else - { - Message(0,"You must have a shield equipped to shield a target!"); - shield_target = 0; - return; - } - if (!ack) - { - Message_StringID(0, ALREADY_SHIELDED); - shield_target = 0; - return; - } - return; -} - -void Client::Handle_OP_Jump(const EQApplicationPacket *app) -{ - SetEndurance(GetEndurance() - (GetLevel()<20?(225*GetLevel()/100):50)); - return; -} - -void Client::Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(EntityId_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureInfoRequest had a packet that was too small."); - return; - } - EntityId_Struct* ent = (EntityId_Struct*)app->pBuffer; - Mob * m = entity_list.GetMob(ent->entity_id); - if(m && m->IsNPC()) - { - std::map::iterator it; - it = zone->adventure_entry_list_flavor.find(m->CastToNPC()->GetAdventureTemplate()); - if(it != zone->adventure_entry_list_flavor.end()) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (it->second.size() + 2)); - strn0cpy((char*)outapp->pBuffer, it->second.c_str(), it->second.size()); - FastQueuePacket(&outapp); - } - else - { - if(m->CastToNPC()->GetAdventureTemplate() != 0) - { - std::string text = "Choose your difficulty and preferred adventure type."; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureInfo, (text.size() + 2)); - strn0cpy((char*)outapp->pBuffer, text.c_str(), text.size()); - FastQueuePacket(&outapp); - } - } - } -} - -void Client::Handle_OP_AdventureRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(AdventureRequest_Struct)) - { - LogFile->write(EQEMuLog::Error, "Handle_OP_AdventureRequest had a packet that was too small."); - return; - } - - if(IsOnAdventure()) - { - return; - } - - if(!p_timers.Expired(&database, pTimerStartAdventureTimer, false)) - { - return; - } - - if(GetPendingAdventureRequest()) - { - return; - } - - AdventureRequest_Struct* ars = (AdventureRequest_Struct*)app->pBuffer; - uint8 group_members = 0; - Raid *r = nullptr; - Group *g = nullptr; - - if(IsRaidGrouped()) - { - r = GetRaid(); - group_members = r->RaidCount(); - } - else if(IsGrouped()) - { - g = GetGroup(); - group_members = g->GroupCount(); - } - else - { - return; - } - - if(group_members < RuleI(Adventure, MinNumberForGroup) || group_members > RuleI(Adventure, MaxNumberForGroup)) - { - return; - } - - Mob* m = entity_list.GetMob(ars->entity_id); - uint32 template_id = 0; - if(m && m->IsNPC()) - { - template_id = m->CastToNPC()->GetAdventureTemplate(); - } - else - { - return; - } - - ServerPacket *packet = new ServerPacket(ServerOP_AdventureRequest, sizeof(ServerAdventureRequest_Struct) + (64 * group_members)); - ServerAdventureRequest_Struct *sar = (ServerAdventureRequest_Struct*)packet->pBuffer; - sar->member_count = group_members; - sar->risk = ars->risk; - sar->type = ars->type; - sar->template_id = template_id; - strcpy(sar->leader, GetName()); - - if(IsRaidGrouped()) - { - int i = 0; - for(int x = 0; x < 72; ++x) - { - if(i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = r->GetClientNameByIndex(x); - if(c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct) + (64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - else - { - int i = 0; - for(int x = 0; x < 6; ++x) - { - if(i == group_members) - { - break; - } - - const char *c_name = nullptr; - c_name = g->GetClientNameByIndex(x); - if(c_name) - { - memcpy((packet->pBuffer + sizeof(ServerAdventureRequest_Struct) + (64 * i)), c_name, strlen(c_name)); - ++i; - } - } - } - - packet->Deflate(); - worldserver.SendPacket(packet); - delete packet; - p_timers.Start(pTimerStartAdventureTimer, 5); -} - -void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) -{ - if(app->size < sizeof(bool)) - { - return; - } - - if(GetPendingAdventureCreate()) - { - return; - } - - if(IsOnAdventure()) - { - return; - } - - bool* p = (bool*)app->pBuffer; - if(*p == true) - { - ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestCreate, sizeof(ServerAdventureRequestCreate_Struct) + (64 * adv_requested_member_count)); - ServerAdventureRequestCreate_Struct *sac = (ServerAdventureRequestCreate_Struct*)pack->pBuffer; - strcpy(sac->leader, GetName()); - sac->id = adv_requested_id; - sac->theme = adv_requested_theme; - sac->member_count = adv_requested_member_count; - memcpy((pack->pBuffer + sizeof(ServerAdventureRequestCreate_Struct)), adv_requested_data, (64 * adv_requested_member_count)); - pack->Deflate(); - worldserver.SendPacket(pack); - delete pack; - PendingAdventureCreate(); - ClearPendingAdventureData(); - } - else - { - ClearPendingAdventureData(); - } -} - -void Client::Handle_OP_LeaveAdventure(const EQApplicationPacket *app) -{ - if(!IsOnAdventure()) - { - return; - } - LeaveAdventure(); } void Client::Handle_OP_Consume(const EQApplicationPacket *app) @@ -1898,9 +4872,9 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) return; } Consume_Struct* pcs = (Consume_Struct*)app->pBuffer; - if(pcs->type == 0x01) + if (pcs->type == 0x01) { - if(m_pp.hunger_level > 6000) + if (m_pp.hunger_level > 6000) { EQApplicationPacket *outapp; outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); @@ -1913,9 +4887,9 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) return; } } - else if(pcs->type == 0x02) + else if (pcs->type == 0x02) { - if(m_pp.thirst_level > 6000) + if (m_pp.thirst_level > 6000) { EQApplicationPacket *outapp; outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); @@ -1930,7 +4904,7 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) } ItemInst *myitem = GetInv().GetItem(pcs->slot); - if(myitem == nullptr) { + if (myitem == nullptr) { LogFile->write(EQEMuLog::Error, "Consuming from empty slot %d", pcs->slot); return; } @@ -1961,905 +4935,470 @@ void Client::Handle_OP_Consume(const EQApplicationPacket *app) return; } -void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) +void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) { - if (app->size != sizeof(ItemVerifyRequest_Struct)) + if (app->size != sizeof(ControlBoat_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); + return; + } + ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; + Mob* boat = entity_list.GetMob(cbs->boatId); + if (boat == 0) + return; // do nothing if the boat isn't valid + + if (!boat->IsNPC() || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); + char *hacked_string = nullptr; + MakeAnyLenString(&hacked_string, "OP_Control Boat was sent against %s which is of race %u", boat->GetName(), boat->GetRace()); + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), hacked_string, zone->GetShortName()); + safe_delete_array(hacked_string); return; } - ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; - int32 slot_id; - int32 target_id; - int32 spell_id = 0; - slot_id = request->slot; - target_id = request->target; - - - EQApplicationPacket *outapp; - outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); - ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; - reply->slot = slot_id; - reply->target = target_id; - - QueuePacket(outapp); - safe_delete(outapp); - - - if (IsAIControlled()) { - this->Message_StringID(13,NOT_IN_CONTROL); - return; - } - - if(slot_id < 0) { - LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i",GetName(),request->slot); - return; - } - - const ItemInst* inst = m_inv[slot_id]; - if (!inst) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id,0,true); - return; - } - - const Item_Struct* item = inst->GetItem(); - if (!item) { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - DeleteItemInInventory(slot_id,0,true); - return; - } - - spell_id = item->Click.Effect; - - if - ( - spell_id > 0 && - ( - !IsValidSpell(spell_id) || - casting_spell_id || - delaytimer || - spellend_timer.Enabled() || - IsStunned() || - IsFeared() || - IsMezzed() || - DivineAura() || - (IsSilenced() && !IsDiscipline(spell_id)) || - (IsAmnesiad() && IsDiscipline(spell_id)) || - (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) - ) - ) - { - SendSpellBarEnable(spell_id); - return; - } - - LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); - - if ((slot_id < MainCursor) || (slot_id == MainPowerSource) || (slot_id > 250 && slot_id < 331 && ((item->ItemType == ItemTypePotion) || item->PotionBelt))) // sanity check - { - ItemInst* p_inst = (ItemInst*)inst; - - parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { + if (cbs->TakeControl) { + // this uses the boat's target to indicate who has control of it. It has to check hate to make sure the boat isn't actually attacking anyone. + if ((boat->GetTarget() == 0) || (boat->GetTarget() == this && boat->GetHateAmount(this) == 0)) { + boat->SetTarget(this); + } + else { + this->Message_StringID(13, IN_USE); return; } + } + else + boat->SetTarget(0); - int r; - bool tryaug = false; - ItemInst* clickaug = 0; - Item_Struct* augitem = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ControlBoat, 0); + FastQueuePacket(&outapp); + safe_delete(outapp); + // have the boat signal itself, so quests can be triggered by boat use + boat->CastToNPC()->SignalNPC(0); +} - for (r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { - const ItemInst* aug_i = inst->GetAugment(r); - if(!aug_i) - continue; - const Item_Struct* aug = aug_i->GetItem(); - if(!aug) - continue; +void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app) +{ + if (DraggedCorpses.size() >= (unsigned int)RuleI(Character, MaxDraggedCorpses)) + { + Message_StringID(13, CORPSEDRAG_LIMIT); + return; + } - if ( (aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2) ) - { - tryaug = true; - clickaug = (ItemInst*)aug_i; - augitem = (Item_Struct*)aug; - spell_id = aug->Click.Effect; - break; - } - } + VERIFY_PACKET_LENGTH(OP_CorpseDrag, app, CorpseDrag_Struct); - if((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell)) + CorpseDrag_Struct *cds = (CorpseDrag_Struct*)app->pBuffer; + + Mob* corpse = entity_list.GetMob(cds->CorpseName); + + if (!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted()) + return; + + Client *c = entity_list.FindCorpseDragger(corpse->GetID()); + + if (c) + { + if (c == this) + Message_StringID(MT_DefaultText, CORPSEDRAG_ALREADY, corpse->GetCleanName()); + else + Message_StringID(MT_DefaultText, CORPSEDRAG_SOMEONE_ELSE, corpse->GetCleanName()); + + return; + } + + if (!corpse->CastToCorpse()->Summon(this, false, true)) + return; + + DraggedCorpses.push_back(std::pair(cds->CorpseName, corpse->GetID())); + + Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName); +} + +void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app) +{ + if (app->size == 1) + { + Message_StringID(MT_DefaultText, CORPSEDRAG_STOPALL); + ClearDraggedCorpses(); + return; + } + + for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) + { + if (!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer)) { - LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s",GetName()); + Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); + Iterator = DraggedCorpses.erase(Iterator); + return; } - else if (inst->IsType(ItemClassCommon)) + } +} + +void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) +{ + DropItem(MainCursor); + return; +} + +void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct); + CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer; + + if (cr->type == 5) { + if (cr->amount > GetEbonCrystals()) { + SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); + m_pp.currentEbonCrystals = 0; + m_pp.careerEbonCrystals = 0; + SaveCurrency(); + SendCrystalCounts(); + } + else { + SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); + m_pp.currentEbonCrystals -= cr->amount; + m_pp.careerEbonCrystals -= cr->amount; + SaveCurrency(); + SendCrystalCounts(); + } + } + else if (cr->type == 4) { + if (cr->amount > GetRadiantCrystals()) { + SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); + m_pp.currentRadCrystals = 0; + m_pp.careerRadCrystals = 0; + SaveCurrency(); + SendCrystalCounts(); + } + else { + SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); + m_pp.currentRadCrystals -= cr->amount; + m_pp.careerRadCrystals -= cr->amount; + SaveCurrency(); + SendCrystalCounts(); + } + } +} + +void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) +{ + uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); + uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); + if ((ebon + radiant) > 0) { + AddCrystals(radiant, ebon); + } +} + +void Client::Handle_OP_Damage(const EQApplicationPacket *app) +{ + if (app->size != sizeof(CombatDamage_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, + sizeof(CombatDamage_Struct)); + DumpPacket(app); + return; + } + + // Broadcast to other clients + CombatDamage_Struct* damage = (CombatDamage_Struct*)app->pBuffer; + //dont send to originator of falling damage packets + entity_list.QueueClients(this, app, (damage->type == DamageTypeFalling)); + return; +} + +void Client::Handle_OP_Death(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Death_Struct)) + return; + + Death_Struct* ds = (Death_Struct*)app->pBuffer; + + //I think this attack_skill value is really a value from SkillDamageTypes... + if (ds->attack_skill > HIGHEST_SKILL) { + mlog(CLIENT__ERROR, "Invalid skill in OP_Death: %d"); + return; + } + + if (GetHP() > 0) + return; + + Mob* killer = entity_list.GetMob(ds->killer_id); + Death(killer, ds->damage, ds->spell_id, (SkillUseTypes)ds->attack_skill); + return; +} + +void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(DelegateAbility_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DelegateAbility expected %i got %i", + sizeof(DelegateAbility_Struct), app->size); + + DumpPacket(app); + + return; + } + + DelegateAbility_Struct* das = (DelegateAbility_Struct*)app->pBuffer; + + Group *g = GetGroup(); + + if (!g) return; + + switch (das->DelegateAbility) + { + case 0: + { + g->DelegateMainAssist(das->Name); + break; + } + case 1: + { + g->DelegateMarkNPC(das->Name); + break; + } + case 2: + { + g->DelegateMainTank(das->Name); + break; + } + case 3: + { + g->DelegatePuller(das->Name); + break; + } + default: + break; + } +} + +void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DeleteItem_Struct)) { + std::cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << std::endl; + return; + } + + DeleteItem_Struct* alc = (DeleteItem_Struct*)app->pBuffer; + const ItemInst *inst = GetInv().GetItem(alc->from_slot); + if (inst && inst->GetItem()->ItemType == ItemTypeAlcohol) { + entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), inst->GetItem()->Name); + CheckIncreaseSkill(SkillAlcoholTolerance, nullptr, 25); + + int16 AlcoholTolerance = GetSkill(SkillAlcoholTolerance); + int16 IntoxicationIncrease; + + if (GetClientVersion() < EQClientSoD) + IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; + else + IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; + + if (IntoxicationIncrease < 0) + IntoxicationIncrease = 1; + + m_pp.intoxication += IntoxicationIncrease; + + if (m_pp.intoxication > 200) + m_pp.intoxication = 200; + } + DeleteItemInInventory(alc->from_slot, 1); + + return; +} + +void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app) +{ + // The client will send this with his id when he zones, maybe when he disconnects too? + //eqs->RemoveData(); // Flushing the queue of packet data to allow for proper zoning + + //just make sure this gets out + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); + FastQueuePacket(&outapp); + + outapp = new EQApplicationPacket(OP_DeleteSpawn, sizeof(EntityId_Struct)); + EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer; + eid->entity_id = GetID(); + + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + + hate_list.RemoveEnt(this->CastToMob()); + + Disconnect(); + return; +} + +void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DeleteSpell_Struct)) + return; + + EQApplicationPacket* outapp = app->Copy(); + DeleteSpell_Struct* dss = (DeleteSpell_Struct*)outapp->pBuffer; + + if (dss->spell_slot < 0 || dss->spell_slot > int(MAX_PP_SPELLBOOK)) + return; + + if (m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { + m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; + dss->success = 1; + } + else + dss->success = 0; + + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillDisarmTraps)) + return; + + if (!p_timers.Expired(&database, pTimerDisarmTraps, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = DisarmTrapsReuseTime; + switch (GetAA(aaAdvTrapNegotiation)) { + case 1: + reuse -= 1; + break; + case 2: + reuse -= 3; + break; + case 3: + reuse -= 5; + break; + } + p_timers.Start(pTimerDisarmTraps, reuse - 1); + + Trap* trap = entity_list.FindNearbyTrap(this, 60); + if (trap && trap->detected) + { + int uskill = GetSkill(SkillDisarmTraps); + if ((MakeRandomInt(0, 49) + uskill) >= (MakeRandomInt(0, 49) + trap->skill)) { - if(item->ItemType == ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) - { - DeleteItemInInventory(slot_id, 1, true); - TrainDiscipline(item->ID); + Message(MT_Skills, "You disarm a trap."); + trap->disarmed = true; + trap->chkarea_timer.Disable(); + trap->respawn_timer.Start((trap->respawn_time + MakeRandomInt(0, trap->respawn_var)) * 1000); + } + else + { + if (MakeRandomInt(0, 99) < 25){ + Message(MT_Skills, "You set off the trap while trying to disarm it!"); + trap->Trigger(this); } - else if(item->ItemType == ItemTypeSpell) - { + else{ + Message(MT_Skills, "You failed to disarm a trap."); + } + } + CheckIncreaseSkill(SkillDisarmTraps, nullptr); + return; + } + Message(MT_Skills, "You did not find any traps close enough to disarm."); + return; +} + +void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(DoGroupLeadershipAbility_Struct)) { + + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", + sizeof(DoGroupLeadershipAbility_Struct), app->size); + + DumpPacket(app); + + return; + } + + DoGroupLeadershipAbility_Struct* dglas = (DoGroupLeadershipAbility_Struct*)app->pBuffer; + + switch (dglas->Ability) + { + case GroupLeadershipAbility_MarkNPC: + { + if (GetTarget()) + { + Group* g = GetGroup(); + if (g) + g->MarkNPC(GetTarget(), dglas->Parameter); + } + break; + } + + case groupAAInspectBuffs: + { + Mob *Target = GetTarget(); + + if (!Target || !Target->IsClient()) + return; + + if (IsRaidGrouped()) { + Raid *raid = GetRaid(); + if (!raid) return; - } - else if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (inst->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if(GetLevel() >= item->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - if(i == 0) { - CastSpell(item->Click.Effect, target_id, 10, item->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else if (tryaug) - { - if (clickaug->GetCharges() == 0) - { - //Message(0, "This item is out of charges."); - Message_StringID(13, ITEM_OUT_OF_CHARGES); - return; - } - if(GetLevel() >= augitem->Click.Level2) - { - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); - inst = m_inv[slot_id]; - if(!inst) - { - return; - } - - if(i == 0) { - CastSpell(augitem->Click.Effect, target_id, 10, augitem->CastTime, 0, 0, slot_id); - } - } - else - { - Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); - return; - } - } - else - { - if(GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(),GetClass())) - { - if(item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol) - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - else - { - //This is food/drink - consume it - if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) - { - Consume(item, item->ItemType, slot_id, false); - } - else if (item->ItemType == ItemTypeAlcohol) - { -#if EQDEBUG >= 1 - LogFile->write(EQEMuLog::Debug, "Drinking Alcohol from slot:%i", slot_id); -#endif - // This Seems to be handled in OP_DeleteItem handling - //DeleteItemInInventory(slot_id, 1, false); - //entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); - //Should add intoxication level to the PP at some point - //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); - } - - if (m_pp.hunger_level > 6000) - m_pp.hunger_level = 6000; - if (m_pp.thirst_level > 6000) - m_pp.thirst_level = 6000; - - EQApplicationPacket *outapp2; - outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); - Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; - sta->food = m_pp.hunger_level; - sta->water = m_pp.thirst_level; - - QueuePacket(outapp2); - safe_delete(outapp2); - } - - } - else - { - LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); - } - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", slot_id); - } - } - else - { - Message(0, "Error: Invalid inventory slot for using effects (inventory slot #%i)", slot_id); - } - - return; -} - -void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AdventureMerchant_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantRequest expected:%i got:%i", sizeof(AdventureMerchant_Struct), app->size); - return; - } - std::stringstream ss(std::stringstream::in | std::stringstream::out); - - uint8 count = 0; - AdventureMerchant_Struct* eid = (AdventureMerchant_Struct*)app->pBuffer; - uint32 merchantid = 0; - - Mob* tmp = entity_list.GetMob(eid->entity_id); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - tmp->CastToNPC()->FaceTarget(this->CastToMob()); - - const Item_Struct *item = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for(itr = merlist.begin();itr != merlist.end() && count<255;++itr){ - const MerchantList &ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(item) - { - uint32 theme; - if(item->LDoNTheme > 16) - { - theme = 0; - } - else if(item->LDoNTheme & 16) - { - theme = 5; - } - else if(item->LDoNTheme & 8) - { - theme = 4; - } - else if(item->LDoNTheme & 4) - { - theme = 3; - } - else if(item->LDoNTheme & 2) - { - theme = 2; - } - else if(item->LDoNTheme & 1) - { - theme = 1; - } - else - { - theme = 0; - } - ss << "^" << item->Name << "|"; - ss << item->ID << "|"; - ss << item->LDoNPrice << "|"; - ss << theme << "|"; - ss << "0|"; - ss << "1|"; - ss << item->Races << "|"; - ss << item->Classes; - count++; - } - } - //Count - //^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,races bit map,classes bitmap - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantResponse,ss.str().size()+2); - outapp->pBuffer[0] = count; - strn0cpy((char*)&outapp->pBuffer[1],ss.str().c_str(),ss.str().size()); - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Adventure_Purchase_Struct)) - { - LogFile->write(EQEMuLog::Error, "OP size error: OP_AdventureMerchantPurchase expected:%i got:%i", sizeof(Adventure_Purchase_Struct), app->size); - return; - } - - Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer; -/* - Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes) - if(ldon_points_available >= item ldonpointcost) - { - give item (67 00 00 00 for the packettype using opcode 0x02c5) - ldon_points_available -= ldonpointcost; - } -*/ - uint32 merchantid = 0; - Mob* tmp = entity_list.GetMob(aps->npcid); - if (tmp == 0 || !tmp->IsNPC() || ((tmp->GetClass() != ADVENTUREMERCHANT) && - (tmp->GetClass() != DISCORD_MERCHANT) && (tmp->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (tmp->GetClass() != DARK_REIGN_MERCHANT))) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid = tmp->CastToNPC()->MerchantType; - - const Item_Struct* item = nullptr; - bool found = false; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - - for(itr = merlist.begin();itr != merlist.end();++itr){ - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - if(item->ID == aps->itemid) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - found = true; - break; - } - } - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if(aps->Type == LDoNMerchant) - { - if(m_pp.ldon_points_available < int32(item->LDoNPrice)) { - Message(13, "You cannot afford that item."); + uint32 group_id = raid->GetGroup(this); + if (group_id > 11 || raid->GroupCount(group_id) < 3) + return; + Target->CastToClient()->InspectBuffs(this, raid->GetLeadershipAA(groupAAInspectBuffs, group_id)); return; } - if(item->LDoNTheme <= 16) - { - if(item->LDoNTheme & 16) - { - if(m_pp.ldon_points_tak < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in tak to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 8) - { - if(m_pp.ldon_points_ruj < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in ruj to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 4) - { - if(m_pp.ldon_points_mmc < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mmc to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 2) - { - if(m_pp.ldon_points_mir < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in mir to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(item->LDoNTheme & 1) - { - if(m_pp.ldon_points_guk < int32(item->LDoNPrice)) - { - Message(13, "You need at least %u points in guk to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - } - } - else if(aps->Type == DiscordMerchant) - { - if(GetPVPPoints() < item->LDoNPrice) - { - Message(13, "You need at least %u PVP points to purchase this item.", int32(item->LDoNPrice)); + Group *g = GetGroup(); + + if (!g || (g->GroupCount() < 3)) return; - } - } - else if(aps->Type == NorrathsKeepersMerchant) - { - if(GetRadiantCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Radiant Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else if(aps->Type == DarkReignMerchant) - { - if(GetEbonCrystals() < item->LDoNPrice) - { - Message(13, "You need at least %u Ebon Crystals to purchase this item.", int32(item->LDoNPrice)); - return; - } - } - else - { - Message(13, "Unknown Adventure Merchant type."); - return; + + Target->CastToClient()->InspectBuffs(this, g->GetLeadershipAA(groupAAInspectBuffs)); + + break; } - - if(CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; + default: + LogFile->write(EQEMuLog::Debug, "Got unhandled OP_DoGroupLeadershipAbility Ability: %d Parameter: %d", + dglas->Ability, dglas->Parameter); + break; } - - if(aps->Type == LDoNMerchant) - { - int32 requiredpts = (int32)item->LDoNPrice*-1; - - if(!UpdateLDoNPoints(requiredpts, 6)) - return; - } - else if(aps->Type == DiscordMerchant) - { - SetPVPPoints(GetPVPPoints() - (int32)item->LDoNPrice); - SendPVPStats(); - } - else if(aps->Type == NorrathsKeepersMerchant) - { - SetRadiantCrystals(GetRadiantCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - else if(aps->Type == DarkReignMerchant) - { - SetEbonCrystals(GetEbonCrystals() - (int32)item->LDoNPrice); - SendCrystalCounts(); - } - int16 charges = 1; - if(item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if(!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - Save(1); -} - -void Client::Handle_OP_ConsiderCorpse(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consider_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size); - return; - } - Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid); - if (tcorpse && tcorpse->IsNPCCorpse()) { - uint32 min; uint32 sec; uint32 ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds / 60 drop .00 - char val1[20]={0}; - char val2[20]={0}; - Message_StringID(10,CORPSE_DECAY1,ConvertArray(min,val1),ConvertArray(sec,val2)); - } - else { - Message_StringID(10,CORPSE_DECAY_NOW); - } - } - else if (tcorpse && tcorpse->IsPlayerCorpse()) { - uint32 day, hour, min, sec, ttime; - if ((ttime = tcorpse->GetDecayTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds - hour = (ttime/3600000)%24; // Total hours - day = ttime/86400000; // Total Days - if(day) - Message(0, "This corpse will decay in %i days, %i hours, %i minutes and %i seconds.", day, hour, min, sec); - else if(hour) - Message(0, "This corpse will decay in %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse will decay in %i minutes and %i seconds.", min, sec); - - Message(0, "This corpse %s be resurrected.", tcorpse->Rezzed()?"cannot":"can"); - /* - hour = 0; - - if((ttime = tcorpse->GetResTime()) != 0) { - sec = (ttime/1000)%60; // Total seconds - min = (ttime/60000)%60; // Total seconds - hour = (ttime/3600000)%24; // Total hours - if(hour) - Message(0, "This corpse can be resurrected for %i hours, %i minutes and %i seconds.", hour, min, sec); - else - Message(0, "This corpse can be resurrected for %i minutes and %i seconds.", min, sec); - } - else { - Message_StringID(0, CORPSE_TOO_OLD); - } - */ - } - else { - Message_StringID(10,CORPSE_DECAY_NOW); - } - } -} - -void Client::Handle_OP_Consider(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Consider_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider expected %i got %i", sizeof(Consider_Struct), app->size); - return; - } - Consider_Struct* conin = (Consider_Struct*)app->pBuffer; - Mob* tmob = entity_list.GetMob(conin->targetid); - if (tmob == 0) - return; - - if(tmob->GetClass() == LDON_TREASURE) - { - Message(15, "%s", tmob->GetCleanName()); - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Consider, sizeof(Consider_Struct)); - Consider_Struct* con = (Consider_Struct*)outapp->pBuffer; - con->playerid = GetID(); - con->targetid = conin->targetid; - if(tmob->IsNPC()) - con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity,(tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction():0, tmob); // rembrant, Dec. 20, 2001; TODO: Send the players proper deity - else - con->faction = 1; - con->level = GetLevelCon(tmob->GetLevel()); - if(zone->IsPVPZone()) { - if (!tmob->IsNPC() ) - con->pvpcon = tmob->CastToClient()->GetPVP(); - } - - // Mongrel: If we're feigned show NPC as indifferent - if (tmob->IsNPC()) - { - if (GetFeigned()) - con->faction = FACTION_INDIFFERENT; - } - - if(!(con->faction == FACTION_SCOWLS)) - { - if(tmob->IsNPC()) - { - if(tmob->CastToNPC()->IsOnHatelist(this)) - con->faction = FACTION_THREATENLY; - } - } - - if(con->faction == FACTION_APPREHENSIVE) { - con->faction = FACTION_SCOWLS; - } else if(con->faction == FACTION_DUBIOUS) { - con->faction = FACTION_THREATENLY; - } else if(con->faction == FACTION_SCOWLS) { - con->faction = FACTION_APPREHENSIVE; - } else if(con->faction == FACTION_THREATENLY) { - con->faction = FACTION_DUBIOUS; - } - - mod_consider(tmob, con); - - QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Begging(const EQApplicationPacket *app) -{ - if(!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) - { - Message(13,"Ability recovery time not yet met."); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*) outapp->pBuffer; - brs->Result = 0; - FastQueuePacket(&outapp); - return; - } - - if(!HasSkill(SkillBegging) || !GetTarget()) - return; - - if(GetTarget()->GetClass() == LDON_TREASURE) - return; - - p_timers.Start(pTimerBeggingPickPocket, 8); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Begging, sizeof(BeggingResponse_Struct)); - BeggingResponse_Struct *brs = (BeggingResponse_Struct*) outapp->pBuffer; - - brs->Result = 0; // Default, Fail. - if(GetTarget() == this) - { - FastQueuePacket(&outapp); - return; - } - - int RandomChance = MakeRandomInt(0 ,100); - - int ChanceToAttack = 0; - - if(GetLevel() > GetTarget()->GetLevel()) - ChanceToAttack = MakeRandomInt(0, 15); - else - ChanceToAttack = MakeRandomInt(((this->GetTarget()->GetLevel() - this->GetLevel())*10)-5,((this->GetTarget()->GetLevel() - this->GetLevel())*10)); - - if(ChanceToAttack < 0) - ChanceToAttack = -ChanceToAttack; - - if(RandomChance < ChanceToAttack) - { - GetTarget()->Attack(this); - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - uint16 CurrentSkill = GetSkill(SkillBegging); - - float ChanceToBeg=((float)(CurrentSkill/700.0f) + 0.15f) * 100; - - if(RandomChance < ChanceToBeg) - { - brs->Amount = MakeRandomInt(1, 10); - // This needs some work to determine how much money they can beg, based on skill level etc. - if(CurrentSkill < 50) - { - brs->Result = 4; // Copper - AddMoneyToPP(brs->Amount, false); - } - else - { - brs->Result = 3; // Silver - AddMoneyToPP(brs->Amount * 10, false); - } - - } - QueuePacket(outapp); - safe_delete(outapp); - CheckIncreaseSkill(SkillBegging, nullptr, -10); -} - -void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_Surname(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Surname_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); - return; - } - - if(!p_timers.Expired(&database, pTimerSurnameChange, false) && !GetGM()) - { - Message(15, "You may only change surnames once every 7 days, your /surname is currently on cooldown."); - return; - } - - if(GetLevel() < 20) - { - Message_StringID(15, SURNAME_LEVEL); - return; - } - - Surname_Struct* surname = (Surname_Struct*) app->pBuffer; - - char *c = nullptr; - bool first = true; - for(c = surname->lastname; *c; c++) - { - if(first) - { - *c = toupper(*c); - first = false; - } - else - { - *c = tolower(*c); - } - } - - if (strlen(surname->lastname) >= 20) { - Message_StringID(15, SURNAME_TOO_LONG); - return; - } - - if(!database.CheckNameFilter(surname->lastname, true)) - { - Message_StringID(15, SURNAME_REJECTED); - return; - } - - ChangeLastName(surname->lastname); - p_timers.Start(pTimerSurnameChange, 604800); - - EQApplicationPacket* outapp = app->Copy(); - outapp = app->Copy(); - surname = (Surname_Struct*) outapp->pBuffer; - surname->unknown0064=1; - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_ClearSurname(const EQApplicationPacket *app) -{ - ChangeLastName(""); -} - -void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) -{ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_YellForHelp, 4); - *(uint32 *)outapp->pBuffer = GetID(); - entity_list.QueueCloseClients(this, outapp, true, 100.0); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Assist(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size); - return; - } - - EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(eid->entity_id); - - EQApplicationPacket* outapp = app->Copy(); - eid = (EntityId_Struct*)outapp->pBuffer; - if (RuleB(Combat, AssistNoTargetSelf)) - eid->entity_id = GetID(); - if (entity && entity->IsMob()) { - Mob *assistee = entity->CastToMob(); - if (assistee->GetTarget()) { - Mob *new_target = assistee->GetTarget(); - if (new_target && (GetGM() || - Dist(*assistee) <= TARGETING_RANGE)) { - SetAssistExemption(true); - eid->entity_id = new_target->GetID(); - } - } - } - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) -{ - if (app->size != sizeof(EntityId_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AssistGroup expected %i got %i", sizeof(EntityId_Struct), app->size); - return; - } - QueuePacket(app); - return; -} - -void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMTrainee_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); - DumpPacket(app); - return; - } - OPGMTraining(app); - return; -} - -void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMTrainEnd_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); - DumpPacket(app); - return; - } - OPGMEndTraining(app); - return; -} - -void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSkillChange_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); - DumpPacket(app); - return; - } - OPGMTrainSkill(app); - return; } void Client::Handle_OP_DuelResponse(const EQApplicationPacket *app) { - if(app->size != sizeof(DuelResponse_Struct)) + if (app->size != sizeof(DuelResponse_Struct)) return; - DuelResponse_Struct* ds = (DuelResponse_Struct*) app->pBuffer; + DuelResponse_Struct* ds = (DuelResponse_Struct*)app->pBuffer; Entity* entity = entity_list.GetID(ds->target_id); Entity* initiator = entity_list.GetID(ds->entity_id); - if(!entity->IsClient() || !initiator->IsClient()) + if (!entity->IsClient() || !initiator->IsClient()) return; entity->CastToClient()->SetDuelTarget(0); entity->CastToClient()->SetDueling(false); initiator->CastToClient()->SetDuelTarget(0); initiator->CastToClient()->SetDueling(false); - if(GetID() == initiator->GetID()) - entity->CastToClient()->Message_StringID(10,DUEL_DECLINE,initiator->GetName()); + if (GetID() == initiator->GetID()) + entity->CastToClient()->Message_StringID(10, DUEL_DECLINE, initiator->GetName()); else - initiator->CastToClient()->Message_StringID(10,DUEL_DECLINE,entity->GetName()); + initiator->CastToClient()->Message_StringID(10, DUEL_DECLINE, entity->GetName()); return; } void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) { - if(app->size != sizeof(Duel_Struct)) + if (app->size != sizeof(Duel_Struct)) return; - Duel_Struct* ds = (Duel_Struct*) app->pBuffer; + Duel_Struct* ds = (Duel_Struct*)app->pBuffer; Entity* entity = entity_list.GetID(ds->duel_target); Entity* initiator = entity_list.GetID(ds->duel_initiator); if (entity && initiator && entity == this && initiator->IsClient()) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_RequestDuel, sizeof(Duel_Struct)); - Duel_Struct* ds2 = (Duel_Struct*) outapp->pBuffer; + Duel_Struct* ds2 = (Duel_Struct*)outapp->pBuffer; ds2->duel_initiator = entity->GetID(); ds2->duel_target = entity->GetID(); @@ -2884,485 +5423,218 @@ void Client::Handle_OP_DuelResponse2(const EQApplicationPacket *app) return; } -void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) +void Client::Handle_OP_DumpName(const EQApplicationPacket *app) { - if(app->size != sizeof(Duel_Struct)) - return; + return; +} - EQApplicationPacket* outapp = app->Copy(); - Duel_Struct* ds = (Duel_Struct*) outapp->pBuffer; - uint32 duel = ds->duel_initiator; - ds->duel_initiator = ds->duel_target; - ds->duel_target = duel; - Entity* entity = entity_list.GetID(ds->duel_target); - if(GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) { - Message_StringID(10,DUEL_CONSIDERING,entity->GetName()); - return; +void Client::Handle_OP_Dye(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DyeStruct)) + printf("Wrong size of DyeStruct, Got: %i, Expected: %zu\n", app->size, sizeof(DyeStruct)); + else{ + DyeStruct* dye = (DyeStruct*)app->pBuffer; + DyeArmor(dye); } - if(IsDueling()) { - Message_StringID(10,DUEL_INPROGRESS); + return; +} + +void Client::Handle_OP_Emote(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Emote_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_Emote: got %d, expected %d", app->size, + sizeof(Emote_Struct)); + DumpPacket(app); return; } - if(GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { - SetDuelTarget(ds->duel_target); - entity->CastToClient()->SetDuelTarget(GetID()); - ds->duel_target = ds->duel_initiator; - entity->CastToClient()->FastQueuePacket(&outapp); - entity->CastToClient()->SetDueling(false); - SetDueling(false); + // Calculate new packet dimensions + Emote_Struct* in = (Emote_Struct*)app->pBuffer; + in->message[1023] = '\0'; + + const char* name = GetName(); + uint32 len_name = strlen(name); + uint32 len_msg = strlen(in->message); + // crash protection -- cheater + if (len_msg > 512) { + in->message[512] = '\0'; + len_msg = 512; + } + uint32 len_packet = sizeof(in->unknown01) + len_name + + len_msg + 1; + + // Construct outgoing packet + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, len_packet); + Emote_Struct* out = (Emote_Struct*)outapp->pBuffer; + out->unknown01 = in->unknown01; + memcpy(out->message, name, len_name); + memcpy(&out->message[len_name], in->message, len_msg); + + /* + if (target && target->IsClient()) { + entity_list.QueueCloseClients(this, outapp, false, 100, target); + + cptr = outapp->pBuffer + 2; + + // not sure if live does this or not. thought it was a nice feature, but would take a lot to + // clean up grammatical and other errors. Maybe with a regex parser... + replacestr((char *)cptr, target->GetName(), "you"); + replacestr((char *)cptr, " he", " you"); + replacestr((char *)cptr, " she", " you"); + replacestr((char *)cptr, " him", " you"); + replacestr((char *)cptr, " her", " you"); + target->CastToClient()->QueuePacket(outapp); + } else - safe_delete(outapp); + */ + entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); + + safe_delete(outapp); return; } -void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) +void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) { - if (app->size != sizeof(SpawnAppearance_Struct)) { - std::cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << std::endl; + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; return; } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - if(sa->spawn_id != GetID()) - return; + SetLooting(false); - if (sa->type == AT_Invis) { - if(sa->parameter != 0) - { - if(!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - if(GetClientVersion() < EQClientSoF) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Invis: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - } - return; - } - invisible = false; - hidden = false; - improved_hidden = false; - entity_list.QueueClients(this, app, true); + Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); + if (entity == 0) { + Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); + if (GetClientVersion() >= EQClientSoD) + Corpse::SendEndLootErrorPacket(this); + else + Corpse::SendLootReqErrorPacket(this); return; } - else if (sa->type == AT_Anim) { - if (IsAIControlled()) - return; - if (sa->parameter == ANIM_STAND) { - SetAppearance(eaStanding); - playeraction = 0; - SetFeigned(false); - BindWound(this, false, true); - camp_timer.Disable(); - } - else if (sa->parameter == ANIM_SIT) { - SetAppearance(eaSitting); - playeraction = 1; - if(!UseBardSpellLogic()) - InterruptSpell(); - SetFeigned(false); - BindWound(this, false, true); - } - else if (sa->parameter == ANIM_CROUCH) { - if(!UseBardSpellLogic()) - InterruptSpell(); - SetAppearance(eaCrouching); - playeraction = 2; - SetFeigned(false); - } - else if (sa->parameter == ANIM_DEATH) { // feign death too - SetAppearance(eaDead); - playeraction = 3; - InterruptSpell(); - } - else if (sa->parameter == ANIM_LOOT) { - SetAppearance(eaLooting); - playeraction = 4; - SetFeigned(false); - } - - // This is from old code - // I have no clue what it's for - /* - else if (sa->parameter == 0x05) { - // Illusion - std::cout << "Illusion packet recv'd:" << std::endl; - DumpPacket(app); - } - */ - else { - std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; - return; - } - - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Anon) { - // For Anon/Roleplay - if (sa->parameter == 1) { // Anon - m_pp.anon = 1; - } - else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp - m_pp.anon = 2; - } - else if (sa->parameter == 0) { // This is Non-Anon - m_pp.anon = 0; - } - else { - std::cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << std::endl; - return; - } - entity_list.QueueClients(this, app, true); - UpdateWho(); - } - else if ((sa->type == AT_HP) && (dead == 0)) { + else if (!entity->IsCorpse()) { + Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); + Corpse::SendLootReqErrorPacket(this); return; } - else if (sa->type == AT_AFK) { - this->AFK = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Split) { - m_pp.autosplit = (sa->parameter == 1); - } - else if (sa->type == AT_Sneak) { - if(sa->parameter != 0) - { - if(!HasSkill(SkillSneak)) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Sneak: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - return; - } - this->sneaking = 0; - entity_list.QueueClients(this, app, true); - } - else if (sa->type == AT_Size) - { - char *hack_str = nullptr; - MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Size: %i", sa->parameter); - database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); - safe_delete_array(hack_str); - } - else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) - { - entity_list.QueueClients(this, app, false); - } - else if (sa->type == AT_Levitate) - { - // don't do anything with this, we tell the client when it's - // levitating, not the other way around - } - else if (sa->type == AT_ShowHelm) - { - m_pp.showhelm = (sa->parameter == 1); - entity_list.QueueClients(this, app, true); - } else { - std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec - << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; + entity->CastToCorpse()->EndLoot(this, app); } return; } -void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app) +void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) { - if (app->size != sizeof(BazaarInspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i", - sizeof(BazaarInspect_Struct), app->size); + if (!ClientFinishedLoading()) + { + SetHP(GetHP() - 1); return; } - BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bis->ItemID); - - if (!item) { - Message(13, "Error: This item does not exist!"); - return; - } - - ItemInst* inst = database.CreateItem(item); - - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - - return; -} - -void Client::Handle_OP_Death(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Death_Struct)) - return; - - Death_Struct* ds = (Death_Struct*)app->pBuffer; - - //I think this attack_skill value is really a value from SkillDamageTypes... - if(ds->attack_skill > HIGHEST_SKILL) { - mlog(CLIENT__ERROR, "Invalid skill in OP_Death: %d"); - return; - } - - if(GetHP() > 0) - return; - - Mob* killer = entity_list.GetMob(ds->killer_id); - Death(killer, ds->damage, ds->spell_id, (SkillUseTypes)ds->attack_skill); - return; -} - -void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) -{ - if(app->size != sizeof(MoveCoin_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); + if (app->size != sizeof(EnvDamage2_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, + sizeof(EnvDamage2_Struct)); DumpPacket(app); return; } - OPMoveCoin(app); - return; -} - -void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) -{ - if(app->size != sizeof(ItemViewRequest_Struct)){ - LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct)); - DumpPacket(app); + EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; + if (admin >= minStatusToAvoidFalling && GetGM()){ + Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + SetHP(GetHP() - 1);//needed or else the client wont acknowledge + return; + } + else if (GetInvul()) { + Message(13, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); + SetHP(GetHP() - 1);//needed or else the client wont acknowledge return; } - DumpPacket(app); - ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer; + int damage = ed->damage; - //todo: verify ivrs->link_hash based on a rule, in case we don't care about people being able to sniff data from the item DB + if (ed->dmgtype == 252) { - const Item_Struct* item = database.GetItem(ivrs->item_id); - if (!item) { - if (ivrs->item_id > 500000) - { - std::string response = ""; - int sayid = ivrs->item_id - 500000; - bool silentsaylink = false; - - if (sayid > 250000) //Silent Saylink - { - sayid = sayid - 250000; - silentsaylink = true; - } - - if (sayid > 0) - { - - std::string query = StringFormat("SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - if (results.RowCount() != 1) { - Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); - return; - } - - auto row = results.begin(); - response = row[0]; - - } - - if((response).size() > 0) - { - if( !mod_saylink(response, silentsaylink) ) { return; } - - if(GetTarget() && GetTarget()->IsNPC()) - { - if(silentsaylink) - { - parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - else - { - if(silentsaylink) - { - parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); - } - else - { - Message(7, "You say, '%s'", response.c_str()); - ChannelMessageReceived(8, 0, 100, response.c_str()); - } - return; - } - } - else - { - Message(13, "Error: Say Link not found or is too long."); - return; - } + switch (GetAA(aaAcrobatics)) { //Don't know what acrobatics effect is yet but it should be done client side via aa effect.. till then + case 1: + damage = damage * 95 / 100; + break; + case 2: + damage = damage * 90 / 100; + break; + case 3: + damage = damage * 80 / 100; + break; } - else { - Message(13, "Error: The item for the link you have clicked on does not exist!"); - return; - } - } - ItemInst* inst = database.CreateItem(item, item->MaxCharges, ivrs->augments[0], ivrs->augments[1], ivrs->augments[2], ivrs->augments[3], ivrs->augments[4]); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} + if (damage < 0) + damage = 31337; -void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) { - if (app->size != sizeof(LDONItemViewRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); + else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) return; - } - LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; - ItemInst* inst = database.CreateItem(item->item_id); - if (inst) { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - return; -} + else + SetHP(GetHP() - damage); -void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) -{ - if(!CharacterID()) + if (GetHP() <= 0) { - return; + mod_client_death_env(); + + Death(0, 32000, SPELL_UNKNOWN, SkillHandtoHand); } - - if (app->size != sizeof(MoveItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); - return; - } - - MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; - if(spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) - { - if(mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) - { - char *detect = nullptr; - const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); - const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); - MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", - mi->from_slot, - itm_from ? itm_from->GetID() : 0, - mi->to_slot, - itm_to ? itm_to->GetID() : 0, - casting_spell_id); - database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); - safe_delete_array(detect); - Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots - return; - } - } - - // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. - bool mi_hack = false; - - if(mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { - if(mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 from_parent = m_inv.CalcSlotId(mi->from_slot); - if(!m_inv[from_parent]) { mi_hack = true; } - else if(!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if(m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if(mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { - if(mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } - else { - int16 to_parent = m_inv.CalcSlotId(mi->to_slot); - if(!m_inv[to_parent]) { mi_hack = true; } - else if(!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } - else if(m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } - } - } - - if(mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } - - if(!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); } - + SendHPUpdate(); return; } -void Client::Handle_OP_Camp(const EQApplicationPacket *app) { -#ifdef BOTS - // This block is necessary to clean up any bot objects owned by a Client - Bot::BotHealRotationsClear(this); - Bot::BotOrderCampAll(this); -#endif - if(IsLFP()) - worldserver.StopLFP(CharacterID()); - - if (GetGM()) - { - OnDisconnect(true); - return; - } - camp_timer.Start(29000,true); - return; -} - -void Client::Handle_OP_Logout(const EQApplicationPacket *app) +void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) { - //LogFile->write(EQEMuLog::Debug, "%s sent a logout packet.", GetName()); - //we will save when we get destroyed soon anyhow - //Save(); + if (app->size != sizeof(FaceChange_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", + sizeof(FaceChange_Struct), app->size); + return; + } - SendLogoutPackets(); + // Notify other clients in zone + entity_list.QueueClients(this, app, false); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - Disconnect(); + FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer; + m_pp.haircolor = fc->haircolor; + m_pp.beardcolor = fc->beardcolor; + m_pp.eyecolor1 = fc->eyecolor1; + m_pp.eyecolor2 = fc->eyecolor2; + m_pp.hairstyle = fc->hairstyle; + m_pp.face = fc->face; + m_pp.beard = fc->beard; + m_pp.drakkin_heritage = fc->drakkin_heritage; + m_pp.drakkin_tattoo = fc->drakkin_tattoo; + m_pp.drakkin_details = fc->drakkin_details; + Save(); + Message_StringID(13, FACE_ACCEPTED); + //Message(13, "Facial features updated."); return; } void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) { - if(GetClass() != MONK) + if (GetClass() != MONK) return; - if(!p_timers.Expired(&database, pTimerFeignDeath, false)) { - Message(13,"Ability recovery time not yet met."); + if (!p_timers.Expired(&database, pTimerFeignDeath, false)) { + Message(13, "Ability recovery time not yet met."); return; } int reuse = FeignDeathReuseTime; switch (GetAA(aaRapidFeign)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 2; - break; - case 3: - reuse -= 5; - break; + case 1: + reuse -= 1; + break; + case 2: + reuse -= 2; + break; + case 3: + reuse -= 5; + break; } - p_timers.Start(pTimerFeignDeath, reuse-1); + p_timers.Start(pTimerFeignDeath, reuse - 1); //BreakInvis(); @@ -3389,213 +5661,663 @@ void Client::Handle_OP_FeignDeath(const EQApplicationPacket *app) return; } -void Client::Handle_OP_Sneak(const EQApplicationPacket *app) +void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) { - if(!HasSkill(SkillSneak) && GetSkill(SkillSneak) == 0) { - return; //You cannot sneak if you do not have sneak - } - - if(!p_timers.Expired(&database, pTimerSneak, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerSneak, SneakReuseTime-1); - - bool was = sneaking; - if (sneaking){ - sneaking = false; - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } + if (app->size != sizeof(FindPersonRequest_Struct)) + printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n", sizeof(FindPersonRequest_Struct), app->size); else { - CheckIncreaseSkill(SkillSneak, nullptr, 5); - } - float hidechance = ((GetSkill(SkillSneak)/300.0f) + .25) * 100; - float random = MakeRandomFloat(0, 99); - if(!was && random < hidechance) { - sneaking = true; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x0F; - sa_out->parameter = sneaking; - QueuePacket(outapp); - safe_delete(outapp); - if(GetClass() == ROGUE){ - outapp = new EQApplicationPacket(OP_SimpleMessage,12); - SimpleMessage_Struct *msg=(SimpleMessage_Struct *)outapp->pBuffer; - msg->color=0x010E; - if (sneaking){ - msg->string_id=347; + FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; + + std::vector points; + Mob* target = entity_list.GetMob(t->npc_id); + + if (target == nullptr) { + //empty length packet == not found. + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; } - else { - msg->string_id=348; + + if (!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || + target->CastToClient()->Buyer)) { + Message(15, "Moving you to Trader %s", target->GetName()); + MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ(), 0.0f); } - FastQueuePacket(&outapp); - } - return; -} -void Client::Handle_OP_Hide(const EQApplicationPacket *app) -{ - if(!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) - { - //Can not be able to train hide but still have it from racial though - return; //You cannot hide if you do not have hide - } - - if(!p_timers.Expired(&database, pTimerHide, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = HideReuseTime - GetAA(209); - p_timers.Start(pTimerHide, reuse-1); - - float hidechance = ((GetSkill(SkillHide)/250.0f) + .25) * 100; - float random = MakeRandomFloat(0, 100); - CheckIncreaseSkill(SkillHide, nullptr, 5); - if (random < hidechance) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 1; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - if(GetAA(aaShroudofStealth)){ - improved_hidden = true; - hidden = true; + if (!RuleB(Pathing, Find) || !zone->pathing) + { + //fill in the path array... + // + points.resize(2); + points[0].x = GetX(); + points[0].y = GetY(); + points[0].z = GetZ(); + points[1].x = target->GetX(); + points[1].y = target->GetY(); + points[1].z = target->GetZ(); } else - hidden = true; - } - if(GetClass() == ROGUE){ - EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage,sizeof(SimpleMessage_Struct)); - SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; - msg->color = 0x010E; - Mob *evadetar = GetTarget(); - if (!auto_attack && (evadetar && evadetar->CheckAggro(this) - && evadetar->IsNPC())) { - if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) { - msg->string_id = EVADE_SUCCESS; - RogueEvade(evadetar); - } else { - msg->string_id = EVADE_FAIL; + { + Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); + Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); + + if (!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End)) + { + points.resize(2); + points[0].x = Start.x; + points[0].y = Start.y; + points[0].z = Start.z; + + points[1].x = End.x; + points[1].y = End.y; + points[1].z = End.z; + } - } else { - if (hidden){ - msg->string_id = HIDE_SUCCESS; - } - else { - msg->string_id = HIDE_FAIL; + else + { + std::list pathlist = zone->pathing->FindRoute(Start, End); + + if (pathlist.size() == 0) + { + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; + } + + //the client seems to have issues with packets larger than this + if (pathlist.size() > 36) + { + EQApplicationPacket outapp(OP_FindPersonReply, 0); + QueuePacket(&outapp); + return; + } + + // Live appears to send the points in this order: + // Final destination. + // Current Position. + // rest of the points. + FindPerson_Point p; + + int PointNumber = 0; + + bool LeadsToTeleporter = false; + + Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back()); + + p.x = v.x; + p.y = v.y; + p.z = v.z; + points.push_back(p); + + p.x = GetX(); + p.y = GetY(); + p.z = GetZ(); + points.push_back(p); + + for (std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) + { + if ((*Iterator) == -1) // Teleporter + { + LeadsToTeleporter = true; + break; + } + + Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false); + p.x = v.x; + p.y = v.y; + p.z = v.z; + points.push_back(p); + ++PointNumber; + } + + if (!LeadsToTeleporter) + { + p.x = target->GetX(); + p.y = target->GetY(); + p.z = target->GetZ(); + + points.push_back(p); + } + } } - FastQueuePacket(&outapp); + + SendPathPacket(points); } return; } -void Client::Handle_OP_ChannelMessage(const EQApplicationPacket *app) +void Client::Handle_OP_Fishing(const EQApplicationPacket *app) { - ChannelMessage_Struct* cm=(ChannelMessage_Struct*)app->pBuffer; - - if (app->size < sizeof(ChannelMessage_Struct)) { - std::cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << std::hex << std::setfill('0') << std::setw(4) << app->GetOpcode() << std::dec << std::endl; - return; - } - if (IsAIControlled()) { - Message(13, "You try to speak but cant move your mouth!"); + if (!p_timers.Expired(&database, pTimerFishing, false)) { + Message(13, "Ability recovery time not yet met."); return; } - ChannelMessageReceived(cm->chan_num, cm->language, cm->skill_in_language, cm->message, cm->targetname); + if (CanFish()) { + parse->EventPlayer(EVENT_FISH_START, this, "", 0); + + //these will trigger GoFish() after a delay if we're able to actually fish, and if not, we won't stop the client from trying again immediately (although we may need to tell it to repop the button) + p_timers.Start(pTimerFishing, FishingReuseTime - 1); + fishing_timer.Start(); + } return; + // Changes made based on Bobs work on foraging. Now can set items in the forage database table to + // forage for. } -void Client::Handle_OP_WearChange(const EQApplicationPacket *app) +void Client::Handle_OP_Forage(const EQApplicationPacket *app) { - if (app->size != sizeof(WearChange_Struct)) { - std::cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << std::endl; + + if (!p_timers.Expired(&database, pTimerForaging, false)) { + Message(13, "Ability recovery time not yet met."); return; } + p_timers.Start(pTimerForaging, ForagingReuseTime - 1); - WearChange_Struct* wc=(WearChange_Struct*)app->pBuffer; - if(wc->spawn_id != GetID()) - return; + ForageItem(); - // we could maybe ignore this and just send our own from moveitem - entity_list.QueueClients(this, app, true); - return; -} - -//in zoning.cpp -//void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { -//} - -void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app) -{ - // The client will send this with his id when he zones, maybe when he disconnects too? - //eqs->RemoveData(); // Flushing the queue of packet data to allow for proper zoning - - //just make sure this gets out - EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); - FastQueuePacket(&outapp); - - outapp = new EQApplicationPacket(OP_DeleteSpawn, sizeof(EntityId_Struct)); - EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer; - eid->entity_id = GetID(); - - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - - hate_list.RemoveEnt(this->CastToMob()); - - Disconnect(); - return; -} - -void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) -{ - Handle_OP_Save(app); -} - -void Client::Handle_OP_Save(const EQApplicationPacket *app) -{ - // The payload is 192 bytes - Not sure what is contained in payload - Save(); - return; -} - -void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Who_All_Struct)) { - std::cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << std::endl; - return; - } - Who_All_Struct* whoall = (Who_All_Struct*) app->pBuffer; - - if(whoall->type == 0) // SoF only, for regular /who - entity_list.ZoneWho(this, whoall); - else - WhoAll(whoall); return; } void Client::Handle_OP_FriendsWho(const EQApplicationPacket *app) { - char *FriendsString = (char*) app->pBuffer; + char *FriendsString = (char*)app->pBuffer; FriendsWho(FriendsString); return; } +void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildMOTD"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SendGuildMOTD(true); + + if (IsInAGuild()) + { + SendGuildURL(); + SendGuildChannel(); + } +} + +void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildsList"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SendGuildList(); +} + +void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/becomenpc"); + return; + } + if (app->size != sizeof(BecomeNPC_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); + return; + } + //entity_list.QueueClients(this, app, false); + BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer; + + Mob* cli = (Mob*)entity_list.GetMob(bnpc->id); + if (cli == 0) + return; + + if (cli->IsClient()) + cli->CastToClient()->QueuePacket(app); + cli->SendAppearancePacket(AT_NPCName, 1, true); + cli->CastToClient()->SetBecomeNPC(true); + cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel); + cli->Message_StringID(0, TOGGLE_OFF); + cli->CastToClient()->tellsoff = true; + //TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this. + return; +} + +void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMDelCorpse_Struct)) + return; + if (this->Admin() < commandEditPlayerCorpses) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/delcorpse"); + return; + } + GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer; + Mob* corpse = entity_list.GetMob(dc->corpsename); + if (corpse == 0) { + return; + } + if (corpse->IsCorpse() != true) { + return; + } + corpse->CastToCorpse()->Delete(); + std::cout << name << " deleted corpse " << dc->corpsename << std::endl; + Message(13, "Corpse %s deleted.", dc->corpsename); + return; +} + +void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/emote"); + return; + } + if (app->size != sizeof(GMEmoteZone_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); + return; + } + GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; + char* newmessage = 0; + if (strstr(gmez->text, "^") == 0) + entity_list.Message(0, 15, gmez->text); + else{ + for (newmessage = strtok((char*)gmez->text, "^"); newmessage != nullptr; newmessage = strtok(nullptr, "^")) + entity_list.Message(0, 15, newmessage); + } + return; +} + +void Client::Handle_OP_GMEndTraining(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMTrainEnd_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMEndTraining expected %i got %i", sizeof(GMTrainEnd_Struct), app->size); + DumpPacket(app); + return; + } + OPGMEndTraining(app); + return; +} + +void Client::Handle_OP_GMFind(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/find"); + return; + } + if (app->size != sizeof(GMSummon_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); + return; + } + //Break down incoming + GMSummon_Struct* request = (GMSummon_Struct*)app->pBuffer; + //Create a new outgoing + EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMFind, sizeof(GMSummon_Struct)); + GMSummon_Struct* foundplayer = (GMSummon_Struct*)outapp->pBuffer; + //Copy the constants + strcpy(foundplayer->charname, request->charname); + strcpy(foundplayer->gmname, request->gmname); + //Check if the NPC exits intrazone... + Mob* gt = entity_list.GetMob(request->charname); + if (gt != 0) { + foundplayer->success = 1; + foundplayer->x = (int32)gt->GetX(); + foundplayer->y = (int32)gt->GetY(); + + foundplayer->z = (int32)gt->GetZ(); + foundplayer->zoneID = zone->GetZoneID(); + } + //Send the packet... + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_GMGoto(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSummon_Struct)) { + std::cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; + return; + } + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/goto"); + return; + } + GMSummon_Struct* gmg = (GMSummon_Struct*)app->pBuffer; + Mob* gt = entity_list.GetMob(gmg->charname); + if (gt != nullptr) { + this->MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading()); + } + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected."); + else { + ServerPacket* pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct)); + memset(pack->pBuffer, 0, pack->size); + ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*)pack->pBuffer; + strcpy(wsgmg->myname, this->GetName()); + strcpy(wsgmg->gotoname, gmg->charname); + wsgmg->admin = admin; + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} + +void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/hideme"); + return; + } + if (app->size != sizeof(SpawnAppearance_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); + return; + } + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; + Message(13, "#: %i, %i", sa->type, sa->parameter); + SetHideMe(!sa->parameter); + return; + +} + +void Client::Handle_OP_GMKick(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMKick_Struct)) + return; + if (this->Admin() < minStatusToKick) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/kick"); + return; + } + GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer; + + Client* client = entity_list.GetClientByName(gmk->name); + if (client == 0) { + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)pack->pBuffer; + strcpy(skp->adminname, gmk->gmname); + strcpy(skp->name, gmk->name); + skp->adminrank = this->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + else { + entity_list.QueueClients(this, app); + //client->Kick(); + } + return; +} + +void Client::Handle_OP_GMKill(const EQApplicationPacket *app) +{ + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/kill"); + return; + } + if (app->size != sizeof(GMKill_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); + return; + } + GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; + Mob* obj = entity_list.GetMob(gmk->name); + Client* client = entity_list.GetClientByName(gmk->name); + if (obj != 0) { + if (client != 0) { + entity_list.QueueClients(this, app); + } + else { + obj->Kill(); + } + } + else { + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_KillPlayer, sizeof(ServerKillPlayer_Struct)); + ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*)pack->pBuffer; + strcpy(skp->gmname, gmk->gmname); + strcpy(skp->target, gmk->name); + skp->admin = this->Admin(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + } + return; +} + +void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMLastName_Struct)) { + std::cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << std::endl; + return; + } + GMLastName_Struct* gmln = (GMLastName_Struct*)app->pBuffer; + if (strlen(gmln->lastname) >= 64) { + Message(13, "/LastName: New last name too long. (max=63)"); + } + else { + Client* client = entity_list.GetClientByName(gmln->name); + if (client == 0) { + Message(13, "/LastName: %s not found", gmln->name); + } + else { + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(client->account_name, client->name, "/lastname"); + return; + } + else + + client->ChangeLastName(gmln->lastname); + } + gmln->unknown[0] = 1; + gmln->unknown[1] = 1; + gmln->unknown[2] = 1; + gmln->unknown[3] = 1; + entity_list.QueueClients(this, app, false); + } + return; +} + +void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMName_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); + return; + } + const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; + if (this->Admin() < minStatusToUseGMCommands){ + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/name"); + return; + } + Client* client = entity_list.GetClientByName(gmn->oldname); + LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); + bool usedname = database.CheckUsedName((const char*)gmn->newname); + if (client == 0) { + Message(13, "%s not found for name change. Operation failed!", gmn->oldname); + return; + } + if ((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) { + Message(13, "Invalid number of characters in new name (%s).", gmn->newname); + return; + } + if (!usedname) { + Message(13, "%s is already in use. Operation failed!", gmn->newname); + return; + + } + database.UpdateName(gmn->oldname, gmn->newname); + strcpy(client->name, gmn->newname); + client->Save(); + + if (gmn->badname == 1) { + database.AddToNameFilter(gmn->oldname); + } + EQApplicationPacket* outapp = app->Copy(); + GMName_Struct* gmn2 = (GMName_Struct*)outapp->pBuffer; + gmn2->unknown[0] = 1; + gmn2->unknown[1] = 1; + gmn2->unknown[2] = 1; + entity_list.QueueClients(this, outapp, false); + safe_delete(outapp); + UpdateWho(); + return; +} + +void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) +{ + // Could make this into a rule, although there is a hard limit since we are using a popup, of 4096 bytes that can + // be displayed in the window, including all the HTML formatting tags. + // + const int maxResults = 10; + + if (app->size < sizeof(GMSearchCorpse_Struct)) + { + LogFile->write(EQEMuLog::Debug, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", + app->size, sizeof(GMSearchCorpse_Struct)); + DumpPacket(app); + return; + } + + GMSearchCorpse_Struct *gmscs = (GMSearchCorpse_Struct *)app->pBuffer; + gmscs->Name[63] = '\0'; + + char *escSearchString = new char[129]; + database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name)); + + std::string query = StringFormat("SELECT charname, zoneid, x, y, z, timeofdeath, rezzed, IsBurried " + "FROM player_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i", + escSearchString, maxResults); + safe_delete_array(escSearchString); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Message(0, "Query failed: %s.", results.ErrorMessage().c_str()); + return; + } + + if (results.RowCount() == 0) + return; + + if (results.RowCount() == maxResults) + Message(clientMessageError, "Your search found too many results; some are not displayed."); + else + Message(clientMessageYellow, "There are %i corpse(s) that match the search string '%s'.", results.RowCount(), gmscs->Name); + + char charName[64], timeOfDeath[20]; + + std::string popupText = ""; + + for (auto row = results.begin(); row != results.end(); ++row) { + + strn0cpy(charName, row[0], sizeof(charName)); + + uint32 ZoneID = atoi(row[1]); + float CorpseX = atof(row[2]); + float CorpseY = atof(row[3]); + float CorpseZ = atof(row[4]); + + strn0cpy(timeOfDeath, row[5], sizeof(timeOfDeath)); + + bool corpseRezzed = atoi(row[6]); + bool corpseBuried = atoi(row[7]); + + popupText += StringFormat("", + charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, timeOfDeath, + corpseRezzed ? "Yes" : "No", corpseBuried ? "Yes" : "No"); + + if (popupText.size() > 4000) { + Message(clientMessageError, "Unable to display all the results."); + break; + } + + } + + popupText += "
NameZoneXYZDate" + "RezzedBuried
 " + "
%s%s%8.0f%8.0f%8.0f%s%s%s
"; + + SendPopupToClient("Corpses", popupText.c_str()); + +} + +void Client::Handle_OP_GMServers(const EQApplicationPacket *app) +{ + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(this->GetName()) + 2); + memset(pack->pBuffer, (uint8)admin, 1); + strcpy((char *)&pack->pBuffer[1], this->GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} + +void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSummon_Struct)) { + std::cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; + return; + } + OPGMSummon(app); + return; +} + +void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMToggle_Struct)) { + std::cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << std::endl; + return; + } + if (this->Admin() < minStatusToUseGMCommands) { + Message(13, "Your account has been reported for hacking."); + database.SetHackerFlag(this->account_name, this->name, "/toggle"); + return; + } + GMToggle_Struct *ts = (GMToggle_Struct *)app->pBuffer; + if (ts->toggle == 0) { + this->Message_StringID(0, TOGGLE_OFF); + //Message(0, "Turning tells OFF"); + tellsoff = true; + } + else if (ts->toggle == 1) { + //Message(0, "Turning tells ON"); + this->Message_StringID(0, TOGGLE_ON); + tellsoff = false; + } + else { + Message(0, "Unkown value in /toggle packet"); + } + UpdateWho(); + return; +} + +void Client::Handle_OP_GMTraining(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMTrainee_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTraining expected %i got %i", sizeof(GMTrainee_Struct), app->size); + DumpPacket(app); + return; + } + OPGMTraining(app); + return; +} + +void Client::Handle_OP_GMTrainSkill(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GMSkillChange_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GMTrainSkill expected %i got %i", sizeof(GMSkillChange_Struct), app->size); + DumpPacket(app); + return; + } + OPGMTrainSkill(app); + return; +} + void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) { if (app->size != sizeof(GMZoneRequest_Struct)) { @@ -3618,7 +6340,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) if (gmzr->zone_id == 0) zid = zonesummon_id; const char * zname = database.GetZoneName(zid); - if(zname == nullptr) + if (zname == nullptr) tarzone[0] = 0; else strcpy(tarzone, zname); @@ -3629,7 +6351,7 @@ void Client::Handle_OP_GMZoneRequest(const EQApplicationPacket *app) } EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMZoneRequest, sizeof(GMZoneRequest_Struct)); - GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*) outapp->pBuffer; + GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*)outapp->pBuffer; strcpy(gmzr2->charname, this->GetName()); gmzr2->zone_id = gmzr->zone_id; gmzr2->x = tarx; @@ -3665,2885 +6387,6 @@ void Client::Handle_OP_GMZoneRequest2(const EQApplicationPacket *app) return; } -void Client::Handle_OP_EndLootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - SetLooting(false); - - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); - if(GetClientVersion() >= EQClientSoD) - Corpse::SendEndLootErrorPacket(this); - else - Corpse::SendLootReqErrorPacket(this); - return; - } - else if (!entity->IsCorpse()) { - Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); - Corpse::SendLootReqErrorPacket(this); - return; - } - else { - entity->CastToCorpse()->EndLoot(this, app); - } - return; -} - -void Client::Handle_OP_LootRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - - SetLooting(true); - - Entity* ent = entity_list.GetID(*((uint32*)app->pBuffer)); - if (ent == 0) { - Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)"); - Corpse::SendLootReqErrorPacket(this); - return; - } - if (ent->IsCorpse()) - { - Corpse *ent_corpse = ent->CastToCorpse(); - if(DistNoRootNoZ(ent_corpse->GetX(), ent_corpse->GetY()) > 625) - { - Message(13, "Corpse too far away."); - Corpse::SendLootReqErrorPacket(this); - return; - } - - if(invisible) { - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; - } - if(invisible_undead) { - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } - if(invisible_animals){ - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } - if(hidden || improved_hidden){ - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - ent->CastToCorpse()->MakeLootRequestPackets(this, app); - return; - } - else { - std::cout << "npc == 0 LOOTING FOOKED3" << std::endl; - Message(13, "Error: OP_LootRequest: Corpse not a corpse?"); - Corpse::SendLootReqErrorPacket(this); - } - return; -} - -void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target && target->IsNPC()) - HandleLDoNOpen(target->CastToNPC()); -} - -void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillSenseTraps)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SkillSenseTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the sense traps skill."); - } -} - -void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillDisarmTraps)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNDisarm(target->CastToNPC(), GetSkill(SkillDisarmTraps), LDoNTypeMechanical); - } - else - Message(13, "You do not have the disarm trap skill."); - } -} - -void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target->IsNPC()) - { - if(HasSkill(SkillPickLock)) - { - if(DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) - { - Message(13, "%s is too far away.", target->GetCleanName()); - return; - } - HandleLDoNPickLock(target->CastToNPC(), GetSkill(SkillPickLock), LDoNTypeMechanical); - } - else - Message(13, "You do not have the pick locks skill."); - } -} - -void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) -{ - Mob * target = GetTarget(); - if(target && target->GetClass() == LDON_TREASURE) - Message(15, "%s", target->GetCleanName()); -} - -void Client::Handle_OP_Dye(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(DyeStruct)) - printf("Wrong size of DyeStruct, Got: %i, Expected: %zu\n",app->size,sizeof(DyeStruct)); - else{ - DyeStruct* dye = (DyeStruct*)app->pBuffer; - DyeArmor(dye); - } - return; -} - -void Client::Handle_OP_ConfirmDelete(const EQApplicationPacket* app){ - return; -} - -void Client::Handle_OP_LootItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LootingItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); - return; - } - /* - ** fixed the looting code so that it sends the correct opcodes - ** and now correctly removes the looted item the player selected - ** as well as gives the player the proper item. - ** Also fixed a few UI lock ups that would occur. - */ - - EQApplicationPacket* outapp = 0; - Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); - if (entity == 0) { - Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)"); - outapp = new EQApplicationPacket(OP_LootComplete, 0); - QueuePacket(outapp); - safe_delete(outapp); - return; - } - - if (entity->IsCorpse()) { - entity->CastToCorpse()->LootItem(this, app); - return; - } - else { - Message(13, "Error: Corpse not found! (!ent->IsCorpse())"); - Corpse::SendEndLootErrorPacket(this); - } - - return; -} - -void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDelete"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - Message(0,"You are not a guild leader or not in a guild."); - else { - mlog(GUILDS__ACTIONS, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); - if (!guild_mgr.DeleteGuild(GuildID())) - Message(0, "Guild delete failed."); - else { - Message(0, "Guild successfully deleted."); - } - } -} - -void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPublicNote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < sizeof(GuildUpdate_PublicNote)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n",app->size,sizeof(GuildUpdate_PublicNote)); - return; - } - GuildUpdate_PublicNote* gpn=(GuildUpdate_PublicNote*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(gpn->target, gci)) { - Message(0, "Unable to find '%s'", gpn->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - mlog(GUILDS__ACTIONS, "Setting public note on %s (%d) in guild %s (%d) to: %s", - gpn->target, gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID(), - gpn->note); - - if(!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { - Message(13, "Failed to set public note on %s", gpn->target); - } else { - Message(0, "Successfully changed public note on %s", gpn->target); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildMOTD(true); - - if(IsInAGuild()) - { - SendGuildURL(); - SendGuildChannel(); - } -} - -void Client::Handle_OP_GetGuildsList(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GetGuildsList"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SendGuildList(); -} - -void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_SetGuildMOTD"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildMOTD_Struct)) { - // client calls for a motd on login even if they arent in a guild - printf("Error: app size of %i != size of GuildMOTD_Struct of %zu\n",app->size,sizeof(GuildMOTD_Struct)); - return; - } - if(!IsInAGuild()) { - Message(13, "You are not in a guild!"); - return; - } - if(!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { - Message(13, "You do not have permissions to edit your guild's MOTD."); - return; - } - - GuildMOTD_Struct* gmotd=(GuildMOTD_Struct*)app->pBuffer; - - mlog(GUILDS__ACTIONS, "Setting MOTD for %s (%d) to: %s - %s", - guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); - - if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { - Message(0, "Motd update failed."); - } - - return; -} - -void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) -{ - - mlog(GUILDS__IN_PACKETS, "Got OP_GuildManageBanker of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - if(app->size != sizeof(GuildManageBanker_Struct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); - return; - } - GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*) app->pBuffer; - - if(!IsInAGuild()) { - Message(13, "Your not in a guild!"); - return; - } - - CharGuildInfo gci; - - if(!guild_mgr.GetCharInfo(gmb->member, gci)) - { - Message(0, "Unable to find '%s'", gmb->member); - return; - } - bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); - - bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); - - bool NewBankerStatus = gmb->enabled & 0x01; - - bool NewAltStatus = gmb->enabled & 0x02; - - if((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can assign guild bankers!"); - return; - } - - if(IsCurrentlyAnAlt != NewAltStatus) - { - bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); - - if(!IsAllowed) - { - Message(13, "You are not allowed to change the alt status of %s", gmb->member); - return; - } - } - - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if(IsCurrentlyABanker != NewBankerStatus) - { - if(!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { - Message(13, "Error setting guild banker flag."); - return; - } - - if(NewBankerStatus) - Message(0, "%s has been made a guild banker.", gmb->member); - else - Message(0, "%s is no longer a guild banker.", gmb->member); - } - if(IsCurrentlyAnAlt != NewAltStatus) - { - if(!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { - Message(13, "Error setting guild alt flag."); - return; - } - - if(NewAltStatus) - Message(0, "%s has been marked as an alt.", gmb->member); - else - Message(0, "%s is no longer marked as an alt.", gmb->member); - } -} - -void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildPeace of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Got OP_GuildWar of len %d", app->size); - mpkt(GUILDS__IN_PACKET_TRACE, app); - return; -} - -void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size < 2) { - mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); - return; - } - - app->pBuffer[app->size-1] = 0; - GuildMakeLeader* gml=(GuildMakeLeader*)app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (GuildRank() != GUILD_LEADER) - Message(0, "Error: You arent the guild leader!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //NOTE: we could do cross-zone lookups here... - - Client* newleader = entity_list.GetClientByName(gml->target); - if(newleader) { - - mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", - guild_mgr.GetGuildName(GuildID()), GuildID(), - newleader->GetName(), newleader->CharacterID()); - - if(guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())){ - Message(0,"Successfully Transfered Leadership to %s.",gml->target); - newleader->Message(15,"%s has transfered the guild leadership into your hands.",GetName()); - } - else - Message(0,"Could not change leadership at this time."); - } - else - Message(0,"Failed to change leader, could not find target."); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(app->size != sizeof(GuildDemoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n",app->size,sizeof(GuildDemoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(demote->target, gci)) { - Message(0, "Unable to find '%s'", demote->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - if(gci.rank < 1) { - Message(0, "%s cannot be demoted any further!", demote->target); - return; - } - uint8 rank = gci.rank - 1; - - - mlog(GUILDS__ACTIONS, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - demote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, demote->target); - return; - } - Message(0, "Successfully demoted %s to rank %d", demote->target, rank); - } -// SendGuildMembers(GuildID(), true); - return; -} - - -void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildPromote"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if(app->size != sizeof(GuildPromoteStruct)) { - mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n",app->size,sizeof(GuildPromoteStruct)); - return; - } - - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) - Message(0, "You dont have permission to invite."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; - - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(promote->target, gci)) { - Message(0, "Unable to find '%s'", promote->target); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - - uint8 rank = gci.rank + 1; - - if(rank > GUILD_OFFICER) - return; - - - mlog(GUILDS__ACTIONS, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", - promote->target, gci.char_id, - guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, - guild_mgr.GetRankName(GuildID(), rank), rank, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(gci.char_id, rank)) { - Message(13, "Error while setting rank %d on '%s'.", rank, promote->target); - return; - } - Message(0, "Successfully promoted %s to rank %d", promote->target, rank); - } - return; -} - -void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInvite"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - - GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer; - - if (!IsInAGuild()) - Message(0, "Error: You are not in a guild!"); - else if(gc->officer > GUILD_MAX_RANK) - Message(13, "Invalid rank."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - - //ok, the invite is also used for changing rank as well. - Mob* invitee = entity_list.GetMob(gc->othername); - - if(!invitee) { - Message(13, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); - return; - } - - if(invitee->IsClient()) { - Client* client = invitee->CastToClient(); - - //ok, figure out what they are trying to do. - if(client->GuildID() == GuildID()) { - //they are already in this guild, must be a promotion or demotion - if(gc->officer < client->GuildRank()) { - //demotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - //we could send this to the member and prompt them to see if they want to - //be demoted (I guess), but I dont see a point in that. - - mlog(GUILDS__ACTIONS, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { - Message(13, "There was an error during the demotion, DB may now be inconsistent."); - return; - } - - } else if(gc->officer > client->GuildRank()) { - //promotion - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { - Message(13, "You dont have permission to demote."); - return; - } - - mlog(GUILDS__ACTIONS, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - client->GetName(), client->CharacterID(), - gc->officer, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the promotion with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if(gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->QueuePacket(app); - - } else { - Message(13, "That member is already that rank."); - return; - } - } else if(!client->IsInAGuild()) { - //they are not in this or any other guild, this is an invite - // - if(client->GetPendingGuildInvitation()) - { - Message(13, "That person is already considering a guild invitation."); - return; - } - - if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { - Message(13, "You dont have permission to invite."); - return; - } - - mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - - //record the invite with guild manager so we know its valid when we get the reply - guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); - - if(gc->guildeqid == 0) - gc->guildeqid = GuildID(); - - mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); - mpkt(GUILDS__OUT_PACKET_TRACE, app); - client->SetPendingGuildInvitation(true); - client->QueuePacket(app); - - } else { - //they are in some other guild - Message(13,"Player is in a guild."); - return; - } - } -#ifdef BOTS - else if (invitee->IsBot()) { - // The guild system is too tightly coupled with the character_ table so we have to avoid using much of the system - Bot::ProcessGuildInvite(this, invitee->CastToBot()); - return; - } -#endif - } -} - -void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildRemove"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - if (app->size != sizeof(GuildCommand_Struct)) { - std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; - return; - } - GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer; - if (!IsInAGuild()) - Message(0, "Error: You arent in a guild!"); - // we can always remove ourself, otherwise, our rank needs remove permissions - else if (strcasecmp(gc->othername,GetName()) != 0 && - !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) - Message(0, "You dont have permission to remove guild members."); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { -#ifdef BOTS - if(Bot::ProcessGuildRemoval(this, gc->othername)) - return; -#endif - uint32 char_id; - Client* client = entity_list.GetClientByName(gc->othername); - - if(client) { - if(!client->IsInGuild(GuildID())) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = client->CharacterID(); - - mlog(GUILDS__ACTIONS, "Removing %s (%d) from guild %s (%d)", - client->GetName(), client->CharacterID(), - guild_mgr.GetGuildName(GuildID()), GuildID()); - } else { - CharGuildInfo gci; - if(!guild_mgr.GetCharInfo(gc->othername, gci)) { - Message(0, "Unable to find '%s'", gc->othername); - return; - } - if(gci.guild_id != GuildID()) { - Message(0, "You aren't in the same guild, what do you think you are doing?"); - return; - } - char_id = gci.char_id; - - mlog(GUILDS__ACTIONS, "Removing remote/offline %s (%d) into guild %s (%d)", - gci.char_name.c_str(), gci.char_id, - guild_mgr.GetGuildName(GuildID()), GuildID()); - } - - if(!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); - GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*) outapp->pBuffer; - gm->guildeqid = GuildID(); - strcpy(gm->member, gc->othername); - Message(0,"%s successfully removed from your guild.",gc->othername); - entity_list.QueueClientsGuild(this, outapp, false, GuildID()); - safe_delete(outapp); - } - else - Message(0,"Unable to remove %s from your guild.",gc->othername); - } -// SendGuildMembers(GuildID(), true); - return; -} - -void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) -{ - mlog(GUILDS__IN_PACKETS, "Received OP_GuildInviteAccept"); - mpkt(GUILDS__IN_PACKET_TRACE, app); - - SetPendingGuildInvitation(false); - - if (app->size != sizeof(GuildInviteAccept_Struct)) { - std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; - return; - } - - GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*) app->pBuffer; - - if(GetClientVersion() >= EQClientRoF) - { - if(gj->response > 9) - { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - return; - } - } - if (gj->response == 5 || gj->response == 4) { - //dont care if the check fails (since we dont know the rank), just want to clear the entry. - guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); - - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); - - return; - } - - //uint32 tmpeq = gj->guildeqid; - if (IsInAGuild() && gj->response==GuildRank()) - Message(0, "Error: You're already in a guild!"); - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - mlog(GUILDS__ACTIONS, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", - gj->guildeqid, gj->response, gj->inviter, gj->newmember); - - //we dont really care a lot about what this packet means, as long as - //it has been authorized with the guild manager - if(!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { - worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); - Message(13, "Invalid invite response packet!"); - return; - } - - if(gj->guildeqid == GuildID()) { - //only need to change rank. - - mlog(GUILDS__ACTIONS, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", - GetName(), CharacterID(), - gj->response, - guild_mgr.GetGuildName(GuildID()), GuildID()); - - if(!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { - Message(13, "There was an error during the rank change, DB may now be inconsistent."); - return; - } - } else { - - mlog(GUILDS__ACTIONS, "Adding %s (%d) to guild %s (%d) at rank %d", - GetName(), CharacterID(), - guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, - gj->response); - - //change guild and rank - - uint32 guildrank = gj->response; - - if(GetClientVersion() == EQClientRoF) - { - if(gj->response == 8) - { - guildrank = 0; - } - } - - if(!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { - Message(13, "There was an error during the invite, DB may now be inconsistent."); - return; - } - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - } - } -} - -void Client::Handle_OP_ManaChange(const EQApplicationPacket *app) -{ - if(app->size == 0) { - // i think thats the sign to stop the songs - if(IsBardSong(casting_spell_id) || bardsong != 0) - InterruptSpell(SONG_ENDS, 0x121); - else - InterruptSpell(INTERRUPT_SPELL, 0x121); - - return; - } - else // I don't think the client sends proper manachanges - { // with a length, just the 0 len ones for stopping songs - //ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer; - printf("OP_ManaChange from client:\n"); - DumpPacket(app); - } - return; -} - -void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) -{ - OPMemorizeSpell(app); - return; -} - -void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SwapSpell_Struct)) { - std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; - return; - } - const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*) app->pBuffer; - int swapspelltemp; - - if(swapspell->from_slot < 0 || swapspell->from_slot > MAX_PP_SPELLBOOK || swapspell->to_slot < 0 || swapspell->to_slot > MAX_PP_SPELLBOOK) - return; - - swapspelltemp = m_pp.spell_book[swapspell->from_slot]; - m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; - m_pp.spell_book[swapspell->to_slot] = swapspelltemp; - - QueuePacket(app); - return; -} - -void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CastSpell_Struct)) { - std::cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << std::endl; - return; - } - if (IsAIControlled()) { - this->Message_StringID(13, NOT_IN_CONTROL); - //Message(13, "You cant cast right now, you arent in control of yourself!"); - return; - } - - CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; - -#ifdef _EQDEBUG - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*) castspell->cs_unknown ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(uint32*) castspell->cs_unknown ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*) castspell->cs_unknown, *(uint16*) castspell->cs_unknown+sizeof(uint16) ); - LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(uint16*) castspell->cs_unknown, *(uint16*) castspell->cs_unknown+sizeof(uint16) ); -#endif - LogFile->write(EQEMuLog::Debug, "OP CastSpell: slot=%d, spell=%d, target=%d, inv=%lx", castspell->slot, castspell->spell_id, castspell->target_id, (unsigned long)castspell->inventoryslot); - - if ((castspell->slot == USE_ITEM_SPELL_SLOT) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // ITEM or POTION cast - { - //discipline, using the item spell slot - if (castspell->inventoryslot == 0xFFFFFFFF) { - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - LogFile->write(EQEMuLog::Debug, "Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - } - return; - } - else if ((castspell->inventoryslot <= EmuConstants::GENERAL_END) || (castspell->slot == POTION_BELT_SPELL_SLOT)) // sanity check - { - const ItemInst* inst = m_inv[castspell->inventoryslot]; //slot values are int16, need to check packet on this field - //bool cancast = true; - if (inst && inst->IsType(ItemClassCommon)) - { - const Item_Struct* item = inst->GetItem(); - if (item->Click.Effect != (uint32)castspell->spell_id) - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, tried to cast a different spell.", zone->GetShortName()); - InterruptSpell(castspell->spell_id); //CHEATER!! - return; - } - - if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) - { - if (item->Click.Level2 > 0) - { - if (GetLevel() >= item->Click.Level2) - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - else - { - database.SetMQDetectionFlag(account_name, name, "OP_CastSpell with item, did not meet req level.", zone->GetShortName()); - Message(0, "Error: level not high enough.", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - ItemInst* p_inst = (ItemInst*)inst; - int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", castspell->inventoryslot); - - if (i == 0) { - CastSpell(item->Click.Effect, castspell->target_id, castspell->slot, item->CastTime, 0, 0, castspell->inventoryslot); - } - else { - InterruptSpell(castspell->spell_id); - return; - } - } - } - else - { - Message(0, "Error: unknown item->Click.Type (0x%02x)", item->Click.Type); - } - } - else - { - Message(0, "Error: item not found in inventory slot #%i", castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else - { - Message(0, "Error: castspell->inventoryslot >= %i (0x%04x)", MainCursor, castspell->inventoryslot); - InterruptSpell(castspell->spell_id); - } - } - else if (castspell->slot == DISCIPLINE_SPELL_SLOT) { // DISCIPLINE cast - if (!UseDiscipline(castspell->spell_id, castspell->target_id)) { - printf("Unknown ability being used by %s, spell being cast is: %i\n", GetName(), castspell->spell_id); - InterruptSpell(castspell->spell_id); - return; - } - } - else if (castspell->slot == ABILITY_SPELL_SLOT) { // ABILITY cast (LoH and Harm Touch) - uint16 spell_to_cast = 0; - - if (castspell->spell_id == SPELL_LAY_ON_HANDS && GetClass() == PALADIN) { - if (!p_timers.Expired(&database, pTimerLayHands)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - spell_to_cast = SPELL_LAY_ON_HANDS; - p_timers.Start(pTimerLayHands, LayOnHandsReuseTime); - } - else if ((castspell->spell_id == SPELL_HARM_TOUCH - || castspell->spell_id == SPELL_HARM_TOUCH2) && GetClass() == SHADOWKNIGHT) { - if (!p_timers.Expired(&database, pTimerHarmTouch)) { - Message(13, "Ability recovery time not yet met."); - InterruptSpell(castspell->spell_id); - return; - } - - // determine which version of HT we are casting based on level - if (GetLevel() < 40) - spell_to_cast = SPELL_HARM_TOUCH; - else - spell_to_cast = SPELL_HARM_TOUCH2; - - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - - if (spell_to_cast > 0) // if we've matched LoH or HT, cast now - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - else // MEMORIZED SPELL (first confirm that it's a valid memmed spell slot, then validate that the spell is currently memorized) - { - uint16 spell_to_cast = 0; - - if(castspell->slot < MAX_PP_MEMSPELL) - { - spell_to_cast = m_pp.mem_spells[castspell->slot]; - if(spell_to_cast != castspell->spell_id) - { - InterruptSpell(castspell->spell_id); //CHEATER!!! - return; - } - } - else if (castspell->slot >= MAX_PP_MEMSPELL) { - InterruptSpell(); - return; - } - - CastSpell(spell_to_cast, castspell->target_id, castspell->slot); - } - return; -} - -void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(DeleteItem_Struct)) { - std::cout << "Wrong size on OP_DeleteItem. Got: " << app->size << ", Expected: " << sizeof(DeleteItem_Struct) << std::endl; - return; - } - - DeleteItem_Struct* alc = (DeleteItem_Struct*) app->pBuffer; - const ItemInst *inst = GetInv().GetItem(alc->from_slot); - if (inst && inst->GetItem()->ItemType == ItemTypeAlcohol) { - entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), inst->GetItem()->Name); - CheckIncreaseSkill(SkillAlcoholTolerance, nullptr, 25); - - int16 AlcoholTolerance = GetSkill(SkillAlcoholTolerance); - int16 IntoxicationIncrease; - - if(GetClientVersion() < EQClientSoD) - IntoxicationIncrease = (200 - AlcoholTolerance) * 30 / 200 + 10; - else - IntoxicationIncrease = (270 - AlcoholTolerance) * 0.111111108 + 10; - - if(IntoxicationIncrease < 0) - IntoxicationIncrease = 1; - - m_pp.intoxication += IntoxicationIncrease; - - if(m_pp.intoxication > 200) - m_pp.intoxication = 200; - } - DeleteItemInInventory(alc->from_slot, 1); - - return; -} - -void Client::Handle_OP_CombatAbility(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CombatAbility_Struct)) { - std::cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << std::endl; - return; - } - OPCombatAbility(app); - return; -} - -void Client::Handle_OP_Taunt(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClientTarget_Struct)) { - std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: "<< sizeof(ClientTarget_Struct) << std::endl; - return; - } - - if(!p_timers.Expired(&database, pTimerTaunt, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerTaunt, TauntReuseTime-1); - - if(GetTarget() == nullptr || !GetTarget()->IsNPC()) - return; - - Taunt(GetTarget()->CastToNPC(), false); - return; -} - -void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) -{ - //packet is empty as of 12/14/04 - - if(!p_timers.Expired(&database, pTimerInstillDoubt, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerInstillDoubt, InstillDoubtReuseTime-1); - - InstillDoubt(GetTarget()); - return; -} - -void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct); - - const Resurrect_Struct* ra = (const Resurrect_Struct*) app->pBuffer; - - _log(SPELLS__REZ, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", - PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); - - _pkt(SPELLS__REZ, app); - - OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z); - - if(ra->action == 1) - { - EQApplicationPacket* outapp = app->Copy(); - // Send the OP_RezzComplete to the world server. This finds it's way to the zone that - // the rezzed corpse is in to mark the corpse as rezzed. - outapp->SetOpcode(OP_RezzComplete); - worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_GMSummon(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - OPGMSummon(app); - return; -} - -void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Client requesting a trade session from an npc/client - // Trade session not started until OP_TradeRequestAck is sent - - BreakInvis(); - - // Pass trade request on to recipient - TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } -#ifndef BOTS - else if (tradee && tradee->IsNPC()) { -#else - else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { -#endif - //npcs always accept - trade->Start(msg->to_mob_id); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); - TradeRequest_Struct* acc = (TradeRequest_Struct*) outapp->pBuffer; - acc->from_mob_id = msg->to_mob_id; - acc->to_mob_id = msg->from_mob_id; - FastQueuePacket(&outapp); - safe_delete(outapp); - } - return; -} - -void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); - return; - } - // Trade request recipient is acknowledging they are able to trade - // After this, the trade session has officially started - // Send ack on to trade initiator if client - TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - trade->Start(msg->to_mob_id); - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_CancelTrade(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CancelTrade_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_CancelTrade, size=%i, expected %i", app->size, sizeof(CancelTrade_Struct)); - return; - } - Mob* with = trade->With(); - if (with && with->IsClient()) { - CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer; - - // Forward cancel packet to other client - msg->fromid = with->GetID(); - //msg->action = 1; - - with->CastToClient()->QueuePacket(app); - - // Put trade items/cash back into inventory - FinishTrade(this); - trade->Reset(); - } - else if(with){ - CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer; - msg->fromid = with->GetID(); - QueuePacket(app); - FinishTrade(this); - trade->Reset(); - } - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) -{ - Mob* with = trade->With(); - trade->state = TradeAccepted; - - if (with && with->IsClient()) { - //finish trade... - // Have both accepted? - Client* other = with->CastToClient(); - other->QueuePacket(app); - - if (other->trade->state == trade->state) { - other->trade->state = TradeCompleting; - trade->state = TradeCompleting; - - // should we do this for NoDrop items as well? - if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { - Message_StringID(13, TRADE_CANCEL_LORE); - other->Message_StringID(13, TRADE_CANCEL_LORE); - this->FinishTrade(this); - other->FinishTrade(other); - other->trade->Reset(); - trade->Reset(); - } - else { - // Audit trade to database for both trade streams - other->trade->LogTrade(); - trade->LogTrade(); - - // start QS code - if(RuleB(QueryServ, PlayerLogTrades)) { - QSPlayerLogTrade_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct)); - - // Perform actual trade - this->FinishTrade(other, true, &event_entry, &event_details); - other->FinishTrade(this, false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct)+(sizeof(QSTradeItems_Struct)* event_entry._detail_count)); - QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSTradeItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if(worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - // end QS code - } - else { - this->FinishTrade(other); - other->FinishTrade(this); - } - - other->trade->Reset(); - trade->Reset(); - } - // All done - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - other->QueuePacket(outapp); - this->FastQueuePacket(&outapp); - } - } - // Trading with a Mob object that is not a Client. - else if(with) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); - QueuePacket(outapp); - safe_delete(outapp); - if(with->IsNPC()) { - // Audit trade to database for player trade stream - if(RuleB(QueryServ, PlayerLogHandins)) { - QSPlayerLogHandin_Struct event_entry; - std::list event_details; - - memset(&event_entry, 0, sizeof(QSPlayerLogHandin_Struct)); - - FinishTrade(with->CastToNPC(), false, &event_entry, &event_details); - - event_entry._detail_count = event_details.size(); - - ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct)+(sizeof(QSHandinItems_Struct)* event_entry._detail_count)); - QSPlayerLogHandin_Struct* qs_buf = (QSPlayerLogHandin_Struct*)qs_pack->pBuffer; - - memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogHandin_Struct)); - - int offset = 0; - - for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { - QSHandinItems_Struct* detail = reinterpret_cast(*iter); - qs_buf->items[offset] = *detail; - safe_delete(detail); - } - - event_details.clear(); - - qs_pack->Deflate(); - - if(worldserver.Connected()) - worldserver.SendPacket(qs_pack); - - safe_delete(qs_pack); - } - else { - FinishTrade(with->CastToNPC()); - } - } -#ifdef BOTS - // TODO: Log Bot trades - else if(with->IsBot()) - with->CastToBot()->FinishTrade(this, Bot::BotTradeClientNormal); -#endif - trade->Reset(); - } - - - return; -} - -void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeBusy_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); - return; - } - // Trade request recipient is cancelling the trade due to being busy - // Trade requester gets message "I'm busy right now" - // Send busy message on to trade initiator if client - TradeBusy_Struct* msg = (TradeBusy_Struct*) app->pBuffer; - Mob* tradee = entity_list.GetMob(msg->to_mob_id); - - if (tradee && tradee->IsClient()) { - tradee->CastToClient()->QueuePacket(app); - } - return; -} - -void Client::Handle_OP_BoardBoat(const EQApplicationPacket *app) -{ - - if(app->size <= 5) - return; - - char *boatname; - boatname = new char[app->size-3]; - memset(boatname, 0, app->size-3); - memcpy(boatname, app->pBuffer, app->size-4); - - Mob* boat = entity_list.GetMob(boatname); - if (boat) - this->BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat - safe_delete_array(boatname); - return; -} - -void Client::Handle_OP_LeaveBoat(const EQApplicationPacket *app) -{ - Mob* boat = entity_list.GetMob(this->BoatID); // find the mob corresponding to the boat id - if (boat) { - if ((boat->GetTarget() == this) && boat->GetHateAmount(this) == 0) // if the client somehow left while still controlling the boat (and the boat isn't attacking them) - boat->SetTarget(0); // fix it to stop later problems - } - this->BoatID = 0; - return; -} - -void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RandomReq_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); - return; - } - const RandomReq_Struct* rndq = (const RandomReq_Struct*) app->pBuffer; - uint32 randLow=rndq->low > rndq->high?rndq->high:rndq->low; - uint32 randHigh=rndq->low > rndq->high?rndq->low:rndq->high; - uint32 randResult; - - if(randLow==0 && randHigh==0) - { // defaults - randLow=0; - randHigh=100; - } - randResult=MakeRandomInt(randLow, randHigh); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RandomReply, sizeof(RandomReply_Struct)); - RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer; - rr->low=randLow; - rr->high=randHigh; - rr->result=randResult; - strcpy(rr->name, GetName()); - entity_list.QueueCloseClients(this, outapp, false, 400); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Buff(const EQApplicationPacket *app) -{ - if (app->size != sizeof(SpellBuffFade_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size); - DumpPacket(app); - return; - } - - SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) app->pBuffer; - uint32 spid = sbf->spellid; - mlog(SPELLS__BUFFS, "Client requested that buff with spell id %d be canceled.", spid); - - //something about IsDetrimentalSpell() crashes this portion of code.. - //tbh we shouldn't use it anyway since this is a simple red vs blue buff check and - //isdetrimentalspell() is much more complex - if(spid == 0xFFFF || (IsValidSpell(spid) && (spells[spid].goodEffect == 0))) - QueuePacket(app); - else - BuffFadeBySpellID(spid); - - return; -} - -void Client::Handle_OP_GMHideMe(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/hideme"); - return; - } - if (app->size != sizeof(SpawnAppearance_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMHideMe, size=%i, expected %i", app->size, sizeof(SpawnAppearance_Struct)); - return; - } - SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; - Message(13, "#: %i, %i", sa->type, sa->parameter); - SetHideMe(!sa->parameter); - return; - -} - -void Client::Handle_OP_GMNameChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMName_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMNameChange, size=%i, expected %i", app->size, sizeof(GMName_Struct)); - return; - } - const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer; - if(this->Admin() < minStatusToUseGMCommands){ - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/name"); - return; - } - Client* client = entity_list.GetClientByName(gmn->oldname); - LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname); - bool usedname = database.CheckUsedName((const char*) gmn->newname); - if(client==0) { - Message(13, "%s not found for name change. Operation failed!", gmn->oldname); - return; - } - if((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) { - Message(13, "Invalid number of characters in new name (%s).", gmn->newname); - return; - } - if (!usedname) { - Message(13, "%s is already in use. Operation failed!", gmn->newname); - return; - - } - database.UpdateName(gmn->oldname, gmn->newname); - strcpy(client->name, gmn->newname); - client->Save(); - - if(gmn->badname==1) { - database.AddToNameFilter(gmn->oldname); - } - EQApplicationPacket* outapp = app->Copy(); - GMName_Struct* gmn2 = (GMName_Struct*) outapp->pBuffer; - gmn2->unknown[0] = 1; - gmn2->unknown[1] = 1; - gmn2->unknown[2] = 1; - entity_list.QueueClients(this, outapp, false); - safe_delete(outapp); - UpdateWho(); - return; -} - -void Client::Handle_OP_GMKill(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kill"); - return; - } - if (app->size != sizeof(GMKill_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMKill, size=%i, expected %i", app->size, sizeof(GMKill_Struct)); - return; - } - GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer; - Mob* obj = entity_list.GetMob(gmk->name); - Client* client = entity_list.GetClientByName(gmk->name); - if(obj!=0) { - if(client!=0) { - entity_list.QueueClients(this,app); - } - else { - obj->Kill(); - } - } - else { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KillPlayer, sizeof(ServerKillPlayer_Struct)); - ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*) pack->pBuffer; - strcpy(skp->gmname, gmk->gmname); - strcpy(skp->target, gmk->name); - skp->admin = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - return; -} - -void Client::Handle_OP_GMLastName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMLastName_Struct)) { - std::cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << std::endl; - return; - } - GMLastName_Struct* gmln = (GMLastName_Struct*) app->pBuffer; - if (strlen(gmln->lastname) >= 64) { - Message(13, "/LastName: New last name too long. (max=63)"); - } - else { - Client* client = entity_list.GetClientByName(gmln->name); - if (client == 0) { - Message(13, "/LastName: %s not found", gmln->name); - } - else { - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(client->account_name, client->name, "/lastname"); - return; - } - else - - client->ChangeLastName(gmln->lastname); - } - gmln->unknown[0] = 1; - gmln->unknown[1] = 1; - gmln->unknown[2] = 1; - gmln->unknown[3] = 1; - entity_list.QueueClients(this, app, false); - } - return; -} - -void Client::Handle_OP_GMToggle(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMToggle_Struct)) { - std::cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << sizeof(GMToggle_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/toggle"); - return; - } - GMToggle_Struct *ts = (GMToggle_Struct *) app->pBuffer; - if (ts->toggle == 0) { - this->Message_StringID(0,TOGGLE_OFF); - //Message(0, "Turning tells OFF"); - tellsoff = true; - } - else if (ts->toggle == 1) { - //Message(0, "Turning tells ON"); - this->Message_StringID(0,TOGGLE_ON); - tellsoff = false; - } - else { - Message(0, "Unkown value in /toggle packet"); - } - UpdateWho(); - return; -} - -void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(LFG_Struct)) { - std::cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << std::endl; - DumpPacket(app); - return; - } - - // Process incoming packet - LFG_Struct* lfg = (LFG_Struct*) app->pBuffer; - - switch(lfg->value & 0xFF) { - case 0: - if(LFG) { - database.SetLFG(CharacterID(), false); - LFG = false; - LFGComments[0] = '\0'; - } - break; - case 1: - if(!LFG) { - LFG = true; - database.SetLFG(CharacterID(), true); - } - LFGFromLevel = lfg->FromLevel; - LFGToLevel = lfg->ToLevel; - LFGMatchFilter = lfg->MatchFilter; - strcpy(LFGComments, lfg->Comments); - break; - default: - Message(0, "Error: unknown LFG value %i", lfg->value); - } - - UpdateWho(); - - // Issue outgoing packet to notify other clients - EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGAppearance, sizeof(LFG_Appearance_Struct)); - LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer; - lfga->spawn_id = this->GetID(); - lfga->lfg = (uint8)LFG; - - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - return; -} - -void Client::Handle_OP_GMGoto(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GMSummon_Struct)) { - std::cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << std::endl; - return; - } - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/goto"); - return; - } - GMSummon_Struct* gmg = (GMSummon_Struct*) app->pBuffer; - Mob* gt = entity_list.GetMob(gmg->charname); - if (gt != nullptr) { - this->MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading()); - } - else if (!worldserver.Connected()) - Message(0, "Error: World server disconnected."); - else { - ServerPacket* pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct)); - memset(pack->pBuffer, 0, pack->size); - ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*) pack->pBuffer; - strcpy(wsgmg->myname, this->GetName()); - strcpy(wsgmg->gotoname, gmg->charname); - wsgmg->admin = admin; - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // This is when a potential purchaser right clicks on this client who is in Trader mode to - // browse their goods. - // - _pkt(TRADING__PACKETS, app); - - TraderClick_Struct* tcs= (TraderClick_Struct*)app->pBuffer; - - if(app->size!=sizeof(TraderClick_Struct)) { - - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch"); - - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); - - TraderClick_Struct* outtcs=(TraderClick_Struct*)outapp->pBuffer; - - Client* Customer = entity_list.GetClientByID(tcs->TraderID); - - if (Customer) - outtcs->Approval = Customer->WithCustomer(GetID()); - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" - " returned a nullptr pointer"); - return; - } - - outtcs->TraderID = tcs->TraderID; - - outtcs->Unknown008 = 0x3f800000; - - QueuePacket(outapp); - - _pkt(TRADING__PACKETS, outapp); - - if(outtcs->Approval) { - this->BulkSendTraderInventory(Customer->CharacterID()); - Customer->Trader_CustomerBrowsing(this); - } - else - Message_StringID(clientMessageYellow, TRADER_BUSY); - - safe_delete(outapp); - - return; -} - -void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Click_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); - return; - } - - Merchant_Click_Struct* mc=(Merchant_Click_Struct*)app->pBuffer; - - // Send back opcode OP_ShopRequest - tells client to open merchant window. - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - //Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - int merchantid=0; - Mob* tmp = entity_list.GetMob(mc->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - - int action = 1; - if(merchantid == 0) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = 1; //open... - mco->rate = 1.0; - QueuePacket(outapp); - safe_delete(outapp); - return; - } - if(tmp->IsEngaged()){ - this->Message_StringID(0,MERCHANT_BUSY); - action = 0; - } - if (GetFeigned() || IsInvisible()) - { - Message(0,"You cannot use a merchant right now."); - action = 0; - } - int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction(); - int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp); - if (factionlvl >= 7) { - MerchantRejectMessage(tmp, primaryfaction); - action = 0; - } - - if (tmp->Charmed()) - action = 0; - - // 1199 I don't have time for that now. etc - if (!tmp->CastToNPC()->IsMerchantOpen()) { - tmp->Say_StringID(MakeRandomInt(1199, 1202)); - action = 0; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); - Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; - - mco->npcid = mc->npcid; - mco->playerid = 0; - mco->command = action; // Merchant command 0x01 = open - if (RuleB(Merchant, UsePriceMod)){ - mco->rate = 1/((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp,true)); // works - } - else - mco->rate = 1/(RuleR(Merchant, BuyCostMod)); - - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - if (action == 1) - BulkSendMerchantInventory(merchantid,tmp->GetNPCTypeID()); - - return; -} - -void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app) -{ - _pkt(TRADING__PACKETS, app); - - if (app->size==sizeof(BazaarSearch_Struct)) { - - BazaarSearch_Struct* bss= (BazaarSearch_Struct*)app->pBuffer; - - this->SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type, - bss->Name, bss->MinPrice*1000, bss->MaxPrice*1000); - } - else if (app->size==sizeof(BazaarWelcome_Struct)) { - - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer; - - if (bws->Beginning.Action==BazaarWelcome) - SendBazaarWelcome(); - } - else if (app->size==sizeof(NewBazaarInspect_Struct)) { - - NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(nbis->Name); - if(c) { - ItemInst* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber); - if(inst) - SendItemPacket(0, inst, ItemPacketViewLink); - } - return; - } - else { - _log(TRADING__CLIENT, "Malformed BazaarSearch_Struct packe, Action %it received, ignoring..."); - LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n"); - } - - return; -} - -void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Sell_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", - sizeof(Merchant_Sell_Struct), app->size); - return; - } - RDTSC_Timer t1; - t1.start(); - Merchant_Sell_Struct* mp=(Merchant_Sell_Struct*)app->pBuffer; -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName()); - DumpPacket(app); -#endif - - int merchantid; - bool tmpmer_used = false; - Mob* tmp = entity_list.GetMob(mp->npcid); - - if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) - return; - - if (mp->quantity < 1) return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*tmp) > USE_NPC_RANGE2) - return; - - merchantid=tmp->CastToNPC()->MerchantType; - - uint32 item_id = 0; - std::list merlist = zone->merchanttable[merchantid]; - std::list::const_iterator itr; - for(itr = merlist.begin();itr != merlist.end();++itr){ - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tmp->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - if(mp->itemslot == ml.slot){ - item_id = ml.item; - break; - } - } - const Item_Struct* item = nullptr; - uint32 prevcharges = 0; - if (item_id == 0) { //check to see if its on the temporary table - std::list tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()]; - std::list::const_iterator tmp_itr; - TempMerchantList ml; - for(tmp_itr = tmp_merlist.begin();tmp_itr != tmp_merlist.end();++tmp_itr){ - ml = *tmp_itr; - if(mp->itemslot == ml.slot){ - item_id = ml.item; - tmpmer_used = true; - prevcharges = ml.charges; - break; - } - } - } - item = database.GetItem(item_id); - if (!item){ - //error finding item, client didnt get the update packet for whatever reason, roleplay a tad - Message(15,"%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.",tmp->GetCleanName()); - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueCloseClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - return; - } - if (CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - if(tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) - { - if(prevcharges > item->MaxCharges && item->MaxCharges > 1) - mp->quantity = item->MaxCharges; - else - mp->quantity = prevcharges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); - Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer; - mpo->quantity = mp->quantity; - mpo->playerid = mp->playerid; - mpo->npcid = mp->npcid; - mpo->itemslot=mp->itemslot; - - int16 freeslotid=0; - int16 charges = 0; - if (item->Stackable || item->MaxCharges > 1) - charges = mp->quantity; - else - charges = item->MaxCharges; - - ItemInst* inst = database.CreateItem(item, charges); - - int SinglePrice = 0; - if (RuleB(Merchant, UsePriceMod)) - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); - else - SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); - - if(item->MaxCharges > 1) - mpo->price = SinglePrice; - else - mpo->price = SinglePrice * mp->quantity; - if(mpo->price < 0 ) - { - safe_delete(outapp); - safe_delete(inst); - return; - } - - if(!TakeMoneyFromPP(mpo->price)) - { - char *hacker_str = nullptr; - MakeAnyLenString(&hacker_str, "Vendor Cheat: attempted to buy %i of %i: %s that cost %d cp but only has %d pp %d gp %d sp %d cp\n", - mpo->quantity, item->ID, item->Name, - mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); - database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); - safe_delete_array(hacker_str); - safe_delete(outapp); - safe_delete(inst); - return; - } - - bool stacked = TryStacking(inst); - if(!stacked) - freeslotid = m_inv.FindFreeSlot(false, true, item->Size); - - //make sure we are not completely full... - if (freeslotid == MainCursor) { - if (m_inv.GetItem(MainCursor) != nullptr) { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - } - - if (freeslotid == INVALID_INDEX) - { - Message(13, "You do not have room for any more items."); - safe_delete(outapp); - safe_delete(inst); - return; - } - - std::string packet; - if (!stacked && inst) { - PutItemInInventory(freeslotid, *inst); - SendItemPacket(freeslotid, inst, ItemPacketTrade); - } - else if(!stacked){ - LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); - } - QueuePacket(outapp); - if(inst && tmpmer_used){ - int32 new_charges = prevcharges - mp->quantity; - zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(),item_id,new_charges); - if(new_charges<=0){ - EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); - Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; - delitem->itemslot = mp->itemslot; - delitem->npcid = mp->npcid; - delitem->playerid = mp->playerid; - delitempacket->priority = 6; - entity_list.QueueClients(tmp,delitempacket); //que for anyone that could be using the merchant so they see the update - safe_delete(delitempacket); - } - else { - // Update the charges/quantity in the merchant window - inst->SetCharges(new_charges); - inst->SetPrice(SinglePrice); - inst->SetMerchantSlot(mp->itemslot); - inst->SetMerchantCount(new_charges); - - SendItemPacket(mp->itemslot, inst, ItemPacketMerchant); - } - } - safe_delete(inst); - safe_delete(outapp); - - // start QS code - if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = tmp->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = 0; - qsaudit->merchant_money.gold = 0; - qsaudit->merchant_money.silver = 0; - qsaudit->merchant_money.copper = 0; - qsaudit->merchant_count = 1; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = (mpo->price / 1000); - qsaudit->char_money.gold = (mpo->price / 100) % 10; - qsaudit->char_money.silver = (mpo->price / 10) % 10; - qsaudit->char_money.copper = mpo->price % 10; - qsaudit->char_count = 0; - - qsaudit->items[0].char_slot = freeslotid; - qsaudit->items[0].item_id = m_inv[freeslotid]->GetID(); - qsaudit->items[0].charges = mpo->quantity; - qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(1); - qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(2); - qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(3); - qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(4); - qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(5); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - if (RuleB(EventLog, RecordBuyFromMerchant)) - LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true); - - if ((RuleB(Character, EnableDiscoveredItems))) - { - if(!GetGM() && !IsDiscovered(item_id)) - DiscoverItem(item_id); - } - - t1.stop(); - std::cout << "At 1: " << t1.getDuration() << std::endl; - return; -} - -void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Merchant_Purchase_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", - sizeof(Merchant_Purchase_Struct), app->size); - return; - } - RDTSC_Timer t1(true); - Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(mp->npcid); - - if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT) - return; - - //you have to be somewhat close to them to be properly using them - if(DistNoRoot(*vendor) > USE_NPC_RANGE2) - return; - - uint32 price=0; - uint32 itemid = GetItemIDAt(mp->itemslot); - if(itemid == 0) - return; - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(mp->itemslot); - if(!item || !inst){ - Message(13,"You seemed to have misplaced that item.."); - return; - } - if(mp->quantity > 1) - { - if((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges())) - return; - } - - if (!item->NoDrop) { - //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); - return; - } - - int cost_quantity = mp->quantity; - if(inst->IsCharged()) - int cost_quantity = 1; - - if (RuleB(Merchant, UsePriceMod)) - price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor,true)+0.5); // need to round up, because client does it automatically when displaying price - else - price=(int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))+0.5); - AddMoneyToPP(price,false); - - if (inst->IsStackable() || inst->IsCharged()) - { - unsigned int i_quan = inst->GetCharges(); - if (mp->quantity > i_quan || inst->IsCharged()) - mp->quantity = i_quan; - } - else - mp->quantity = 1; - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, mp->quantity, price, item, false); - - int charges = mp->quantity; - //Hack workaround so usable items with 0 charges aren't simply deleted - if(charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) - charges = 1; - - int freeslot = 0; - if(charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(),itemid,charges,true)) > 0){ - ItemInst* inst2 = inst->Clone(); - if (RuleB(Merchant, UsePriceMod)){ - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor,false)); - } - else - inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); - inst2->SetMerchantSlot(freeslot); - - uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); - - if(inst2->IsStackable()) { - inst2->SetCharges(MerchantQuantity); - } - inst2->SetMerchantCount(MerchantQuantity); - - SendItemPacket(freeslot-1, inst2, ItemPacketMerchant); - safe_delete(inst2); - } - - // start QS code - if(RuleB(QueryServ, PlayerLogMerchantTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct) + sizeof(QSTransactionItems_Struct)); - QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; - - qsaudit->zone_id = zone->GetZoneID(); - qsaudit->merchant_id = vendor->CastToNPC()->MerchantType; - qsaudit->merchant_money.platinum = (price / 1000); - qsaudit->merchant_money.gold = (price / 100) % 10; - qsaudit->merchant_money.silver = (price / 10) % 10; - qsaudit->merchant_money.copper = price % 10; - qsaudit->merchant_count = 0; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = 0; - qsaudit->char_money.gold = 0; - qsaudit->char_money.silver = 0; - qsaudit->char_money.copper = 0; - qsaudit->char_count = 1; - - qsaudit->items[0].char_slot = mp->itemslot; - qsaudit->items[0].item_id = itemid; - qsaudit->items[0].charges = charges; - qsaudit->items[0].aug_1 = m_inv[mp->itemslot]->GetAugmentItemID(1); - qsaudit->items[0].aug_2 = m_inv[mp->itemslot]->GetAugmentItemID(2); - qsaudit->items[0].aug_3 = m_inv[mp->itemslot]->GetAugmentItemID(3); - qsaudit->items[0].aug_4 = m_inv[mp->itemslot]->GetAugmentItemID(4); - qsaudit->items[0].aug_5 = m_inv[mp->itemslot]->GetAugmentItemID(5); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - // end QS code - - // Now remove the item from the player, this happens regardless of outcome - if (!inst->IsStackable()) - this->DeleteItemInInventory(mp->itemslot,0,false); - else - this->DeleteItemInInventory(mp->itemslot,mp->quantity,false); - - //This forces the price to show up correctly for charged items. - if(inst->IsCharged()) - mp->quantity = 1; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); - Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer; - mco->npcid = vendor->GetID(); - mco->itemslot=mp->itemslot; - mco->quantity=mp->quantity; - mco->price=price; - QueuePacket(outapp); - safe_delete(outapp); - SendMoneyUpdate(); - t1.start(); - Save(1); - t1.stop(); - std::cout << "Save took: " << t1.getDuration() << std::endl; - return; -} - -void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) -{ - EQApplicationPacket empty(OP_ShopEndConfirm); - QueuePacket(&empty); - //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2); - //outapp->pBuffer[0] = 0x0a; - //outapp->pBuffer[1] = 0x66; - //QueuePacket(outapp); - //safe_delete(outapp); - //Save(); - return; -} - -/* -void Client::Handle_OP_CloseContainer(const EQApplicationPacket *app) -{ - if (app->size != sizeof(CloseContainer_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i", - sizeof(CloseContainer_Struct), app->size); - return; - } - - SetTradeskillObject(nullptr); - - ClickObjectAck_Struct* oos = (ClickObjectAck_Struct*)app->pBuffer; - Entity* entity = entity_list.GetEntityObject(oos->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - object->Close(); - } - return; -} -*/ - -void Client::Handle_OP_ClickObjectAction(const EQApplicationPacket *app) -{ - if (app->size == 0) { - // RoF sends this packet 0 sized when switching from auto-combine to experiment windows. - // Not completely sure if 0 sized is for this or for closing objects as commented out below - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - - return; - - // RoF sends a 0 sized packet for closing objects - /* - Object* object = GetTradeskillObject(); - if (object) { - object->CastToObject()->Close(); - } - */ - } - else - { - if (app->size != sizeof(ClickObjectAction_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on OP_ClickObjectAction: Expected %i, Got %i", - sizeof(ClickObjectAction_Struct), app->size); - return; - } - - ClickObjectAction_Struct* oos = (ClickObjectAction_Struct*)app->pBuffer; - Entity* entity = entity_list.GetEntityObject(oos->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - if(oos->open == 0) { - object->Close(); - } else { - LogFile->write(EQEMuLog::Error, "Unsupported action %d in OP_ClickObjectAction", oos->open); - } - } else { - LogFile->write(EQEMuLog::Error, "Invalid object %d in OP_ClickObjectAction", oos->drop_id); - } - } - - SetTradeskillObject(nullptr); - - EQApplicationPacket end_trade1(OP_FinishWindow, 0); - QueuePacket(&end_trade1); - - EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_ClickObject(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickObject_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i", - sizeof(ClickObject_Struct), app->size); - return; - } - - ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer; - Entity* entity = entity_list.GetID(click_object->drop_id); - if (entity && entity->IsObject()) { - Object* object = entity->CastToObject(); - - object->HandleClick(this, click_object); - - std::vector args; - args.push_back(object); - - char buf[10]; - snprintf(buf, 9, "%u", click_object->drop_id); - buf[9] = '\0'; - parse->EventPlayer(EVENT_CLICK_OBJECT, this, buf, 0, &args); - } - - // Observed in RoF after OP_ClickObjectAction: - //EQApplicationPacket end_trade2(OP_FinishWindow2, 0); - //QueuePacket(&end_trade2); - return; -} - -void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) -{ - if (app->size != sizeof(TradeskillFavorites_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", - sizeof(TradeskillFavorites_Struct), app->size); - return; - } - - TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; - - LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); - - // results show that object_type is combiner type - // some_id = 0 if world combiner, item number otherwise - - // make where clause segment for container(s) - char containers[30]; - if (tsf->some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", tsf->object_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", tsf->object_type, tsf->some_id); - } - - char *query = 0; - char buf[5500]; //gotta be big enough for 500 IDs - - bool first = true; - uint16 r; - char *pos = buf; - - //Assumes item IDs are <10 characters long - for(r = 0; r < 500; r++) { - if(tsf->favorite_recipes[r] == 0) - continue; - - if(first) { - pos += snprintf(pos, 10, "%u", tsf->favorite_recipes[r]); - first = false; - } else { - pos += snprintf(pos, 10, ",%u", tsf->favorite_recipes[r]); - } - } - - if(first) //no favorites.... - return; - - //To be a good kid, I should move this SQL somewhere else... - //but im lazy right now, so it stays here - uint32 qlen = 0; - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE tr.enabled <> 0 AND tr.id IN (%s) " - " AND tr.must_learn & 0x20 <> 0x20 AND ((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 100 ", CharacterID(), buf, containers); - - TradeskillSearchResults(query, qlen, tsf->object_type, tsf->some_id); - - safe_delete_array(query); - return; -} - -void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipesSearch_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", - sizeof(RecipesSearch_Struct), app->size); - return; - } - - RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer; - rss->query[55] = '\0'; //just to be sure. - - - LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); - - // make where clause segment for container(s) - char containers[30]; - if (rss->some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", rss->object_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", rss->object_type, rss->some_id); - } - - char *query = 0; - char searchclause[140]; //2X rss->query + SQL crap - - //omit the rlike clause if query is empty - if(rss->query[0] != 0) { - char buf[120]; //larger than 2X rss->query - database.DoEscapeString(buf, rss->query, strlen(rss->query)); - - snprintf(searchclause, 139, "name rlike '%s' AND", buf); - } else { - searchclause[0] = '\0'; - } - uint32 qlen = 0; - - //arbitrary limit of 200 recipes, makes sense to me. - qlen = MakeAnyLenString(&query, "SELECT tr.id,tr.name,tr.trivial,SUM(tre.componentcount),crl.madecount,tr.tradeskill " - " FROM tradeskill_recipe AS tr " - " LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " - " WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " - " AND tr.must_learn & 0x20 <> 0x20 AND((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) OR (tr.must_learn & 0x3 = 0)) " - " GROUP BY tr.id " - " HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " - " LIMIT 200 " - , CharacterID(), searchclause, rss->mintrivial, rss->maxtrivial, containers); - - TradeskillSearchResults(query, qlen, rss->object_type, rss->some_id); - - safe_delete_array(query); - return; -} - -void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) -{ - if(app->size < sizeof(uint32)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", - sizeof(uint32), app->size); - return; - } - uint32 *recipe_id = (uint32*) app->pBuffer; - - SendTradeskillDetails(*recipe_id); - - return; -} - -void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RecipeAutoCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", - sizeof(RecipeAutoCombine_Struct), app->size); - return; - } - - RecipeAutoCombine_Struct* rac = (RecipeAutoCombine_Struct*)app->pBuffer; - - Object::HandleAutoCombine(this, rac); - return; -} - -void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) -{ - if (app->size != sizeof(NewCombine_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", - sizeof(NewCombine_Struct), app->size); - return; - } - /*if (m_tradeskill_object == nullptr) { - Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); - return; - }*/ - - //fixed this to work for non-world objects - - // Delegate to tradeskill object to perform combine - NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer; - Object::HandleCombine(this, in_combine, m_tradeskill_object); - return; -} - -void Client::Handle_OP_ItemName(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ItemNamePacket_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", - sizeof(ItemNamePacket_Struct), app->size); - return; - } - ItemNamePacket_Struct *p = (ItemNamePacket_Struct*)app->pBuffer; - const Item_Struct *item = 0; - if ((item = database.GetItem(p->item_id))!=nullptr) { - EQApplicationPacket* outapp=new EQApplicationPacket(OP_ItemName,sizeof(ItemNamePacket_Struct)); - p=(ItemNamePacket_Struct*)outapp->pBuffer; - memset(p, 0, sizeof(ItemNamePacket_Struct)); - strcpy(p->name,item->Name); - FastQueuePacket(&outapp); - } - return; -} - -void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app) -{ - if (app->size != sizeof(AugmentItem_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for AugmentItem_Struct: Expected: %i, Got: %i", - sizeof(AugmentItem_Struct), app->size); - return; - } - - // Delegate to tradeskill object to perform combine - AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; - bool deleteItems = false; - if(GetClientVersion() >= EQClientRoF) - { - ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - - //Message(15, "%i %i %i %i %i %i", in_augment->container_slot, in_augment->augment_slot, in_augment->container_index, in_augment->augment_index, in_augment->augment_action, in_augment->dest_inst_id); - - // Adding augment - if (in_augment->augment_action == 0) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot=-1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; - //Message(13, "%i AugSlot", aug_slot_id); - if(slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(MainCursor); - - if (tobe_auged && auged_with) - { - if (((tobe_auged->IsAugmentSlotAvailable(auged_with->GetAugmentType(), in_augment->augment_index)) != -1) && - (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) - { - tobe_auged->PutAugment(in_augment->augment_index, *auged_with); - - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if(aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_AUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - parse->EventItem(EVENT_AUGMENT_INSERT, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting.", in_augment->augment_index); - return; - } - - itemOneToPush = tobe_auged->Clone(); - // Must push items after the items in inventory are deleted - necessary due to lore items... - if (itemOneToPush) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(MainCursor, 0, true); - if(PutItemInInventory(slot_id, *itemOneToPush, true)) - { - //Message(13, "Sucessfully added an augment to your item!"); - return; - } - else - { - Message(13, "Error: No available slot for end result. Please free up some bag space."); - } - } - else - { - Message(13, "Error in cloning item for augment. Aborted."); - } - - } - else - { - Message(13, "Error: No available slot for augment in that item."); - } - } - } - else if(in_augment->augment_action == 1) - { - ItemInst *tobe_auged, *auged_with = nullptr; - int8 slot=-1; - Inventory& user_inv = GetInv(); - - uint16 slot_id = in_augment->container_slot; - uint16 aug_slot_id = in_augment->augment_slot; //it's actually solvent slot - if(slot_id == INVALID_INDEX || aug_slot_id == INVALID_INDEX) - { - Message(13, "Error: Invalid Aug Index."); - return; - } - - tobe_auged = user_inv.GetItem(slot_id); - auged_with = user_inv.GetItem(aug_slot_id); - - ItemInst *old_aug = nullptr; - if(!auged_with) - return; - const uint32 id = auged_with->GetID(); - ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_index); - if(aug) { - std::vector args; - args.push_back(aug); - parse->EventItem(EVENT_UNAUGMENT_ITEM, this, tobe_auged, nullptr, "", in_augment->augment_index, &args); - - args.assign(1, tobe_auged); - - args.push_back(false); - - parse->EventItem(EVENT_AUGMENT_REMOVE, this, aug, nullptr, "", in_augment->augment_index, &args); - } - else - { - Message(13, "Error: Could not find augmentation at index %i. Aborting."); - return; - } - old_aug = tobe_auged->RemoveAugment(in_augment->augment_index); - - itemOneToPush = tobe_auged->Clone(); - if (old_aug) - itemTwoToPush = old_aug->Clone(); - if(itemOneToPush && itemTwoToPush && auged_with) - { - DeleteItemInInventory(slot_id, 0, true); - DeleteItemInInventory(aug_slot_id, auged_with->IsStackable() ? 1 : 0, true); - if(!PutItemInInventory(slot_id, *itemOneToPush, true)) - { - Message(15, "Shouldn't happen, contact an admin!"); - } - - if(PutItemInInventory(MainCursor, *itemTwoToPush, true)) - { - //Message(15, "Successfully removed an augmentation!"); - } - } - } - } - else - { - Object::HandleAugmentation(this, in_augment, m_tradeskill_object); - } - return; -} - -void Client::Handle_OP_ClickDoor(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ClickDoor_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ClickDoor, size=%i, expected %i", app->size, sizeof(ClickDoor_Struct)); - return; - } - ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer; - Doors* currentdoor = entity_list.FindDoor(cd->doorid); - if(!currentdoor) - { - Message(0,"Unable to find door, please notify a GM (DoorID: %i).",cd->doorid); - return; - } - - char buf[20]; - snprintf(buf, 19, "%u", cd->doorid); - buf[19] = '\0'; - std::vector args; - args.push_back(currentdoor); - parse->EventPlayer(EVENT_CLICK_DOOR, this, buf, 0, &args); - - currentdoor->HandleClick(this,0); - return; -} - -void Client::Handle_OP_CreateObject(const EQApplicationPacket *app) -{ - DropItem(MainCursor); - return; -} - -void Client::Handle_OP_FaceChange(const EQApplicationPacket *app) -{ - if (app->size != sizeof(FaceChange_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_FaceChange: Expected: %i, Got: %i", - sizeof(FaceChange_Struct), app->size); - return; - } - - // Notify other clients in zone - entity_list.QueueClients(this, app, false); - - FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer; - m_pp.haircolor = fc->haircolor; - m_pp.beardcolor = fc->beardcolor; - m_pp.eyecolor1 = fc->eyecolor1; - m_pp.eyecolor2 = fc->eyecolor2; - m_pp.hairstyle = fc->hairstyle; - m_pp.face = fc->face; - m_pp.beard = fc->beard; - m_pp.drakkin_heritage = fc->drakkin_heritage; - m_pp.drakkin_tattoo = fc->drakkin_tattoo; - m_pp.drakkin_details = fc->drakkin_details; - Save(); - Message_StringID(13,FACE_ACCEPTED); - //Message(13, "Facial features updated."); - return; -} - -void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) -{ - //this seems to be the initial invite to form a group - Handle_OP_GroupInvite2(app); -} - -void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupInvite_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", - sizeof(GroupInvite_Struct), app->size); - return; - } - - GroupInvite_Struct* gis = (GroupInvite_Struct*) app->pBuffer; - - Mob *Invitee = entity_list.GetMob(gis->invitee_name); - - if(Invitee == this) - { - Message_StringID(clientMessageWhite, GROUP_INVITEE_SELF); - return; - } - - if(Invitee) { - if(Invitee->IsClient()) { - if((!Invitee->IsGrouped() && !Invitee->IsRaidGrouped()) || - (Invitee->GetGroup() && Invitee->CastToClient()->GetMerc() && Invitee->GetGroup()->GroupCount() == 2)) - { - if(app->GetOpcode() == OP_GroupInvite2) - { - //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we - //Don't have to deal with GroupFollow2 crap. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(outapp->pBuffer, app->pBuffer, outapp->size); - Invitee->CastToClient()->QueuePacket(outapp); - safe_delete(outapp); - return; - } - else - { - //The correct opcode, no reason to bother wasting time reconstructing the packet - Invitee->CastToClient()->QueuePacket(app); - } - } - } -#ifdef BOTS - else if(Invitee->IsBot()) { - Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); - } -#endif - } - else - { - ServerPacket* pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); - memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - void Client::Handle_OP_GroupAcknowledge(const EQApplicationPacket *app) { return; @@ -6557,12 +6400,12 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) return; } - GroupCancel_Struct* gf = (GroupCancel_Struct*) app->pBuffer; + GroupCancel_Struct* gf = (GroupCancel_Struct*)app->pBuffer; Mob* inviter = entity_list.GetClientByName(gf->name1); - if(inviter != nullptr) + if (inviter != nullptr) { - if(inviter->IsClient()) + if (inviter->IsClient()) inviter->CastToClient()->QueuePacket(app); } else @@ -6577,6 +6420,193 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) return; } +void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) +{ + //should check for leader, only they should be able to do this.. + Group* group = GetGroup(); + if (group) + group->DisbandGroup(); + + if (LFP) + UpdateLFP(); + + return; +} + +void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupGeneric_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", + sizeof(GroupGeneric_Struct), app->size); + return; + } + + LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); + + GroupGeneric_Struct* gd = (GroupGeneric_Struct*)app->pBuffer; + + Raid *raid = entity_list.GetRaidByClient(this); + if (raid){ + Mob* memberToDisband = nullptr; + + if (!raid->IsGroupLeader(GetName())) + memberToDisband = this; + else + memberToDisband = GetTarget(); + + if (!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + + if (!memberToDisband) + memberToDisband = this; + + if (!memberToDisband->IsClient()) + return; + + //we have a raid.. see if we're in a raid group + uint32 grp = raid->GetGroup(memberToDisband->GetName()); + bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; + if (grp < 12){ + if (wasGrpLdr){ + raid->SetGroupLeader(memberToDisband->GetName(), false); + for (int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if (raid->members[x].GroupNumber == grp) + { + if (strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) + { + raid->SetGroupLeader(raid->members[x].membername); + break; + } + } + } + } + raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); + raid->GroupUpdate(grp); //break + //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); + //raid->SendGroupUpdate(memberToDisband->CastToClient()); + raid->SendGroupDisband(memberToDisband->CastToClient()); + } + //we're done + return; + } + + Group* group = GetGroup(); + + if (!group) + return; + +#ifdef BOTS + // this block is necessary to allow more control over controlling how bots are zoned or camped. + if (Bot::GroupHasBot(group)) { + if (group->IsLeader(this)) { + if ((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { + Bot::ProcessBotGroupDisband(this, std::string()); + } + else { + Mob* tempMember = entity_list.GetMob(gd->name2); + if (tempMember) { + if (tempMember->IsBot()) + Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); + } + } + } + } +#endif + if ((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { + group->DisbandGroup(); + if (GetMerc() != nullptr) + GetMerc()->Suspend(); + } + else { + Mob* memberToDisband = nullptr; + memberToDisband = GetTarget(); + + if (!memberToDisband) + memberToDisband = entity_list.GetMob(gd->name2); + + if (memberToDisband) { + if (group->IsLeader(this)) { + // the group leader can kick other members out of the group... + //group->DelMember(memberToDisband,false); + if (memberToDisband->IsClient()) + { + group->DelMember(memberToDisband, false); + Client* memberClient = memberToDisband->CastToClient(); + Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); + if (memberClient && memberMerc && group) + { + if (!memberMerc->IsGrouped() && !memberClient->IsGrouped()) { + Group *g = new Group(memberClient); + + entity_list.AddGroup(g); + + if (g->GetID() == 0) { + safe_delete(g); + return; + } + if (Merc::AddMercToGroup(memberMerc, g)) { + database.SetGroupLeaderName(g->GetID(), memberClient->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID()); + database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true); + database.RefreshGroupFromDB(memberClient); + } + } + } + } + else if (memberToDisband->IsMerc()) { + memberToDisband->CastToMerc()->Suspend(); + } + } + else { + // ...but other members can only remove themselves + group->DelMember(this, false); + + if (!IsGrouped() && GetMerc() != nullptr) { + if (!IsGrouped()) { + Group *g = new Group(this); + + if (!g) { + delete g; + g = nullptr; + return; + } + + entity_list.AddGroup(g); + + if (g->GetID() == 0) { + safe_delete(g); + return; + } + + if (Merc::AddMercToGroup(GetMerc(), g)) { + database.SetGroupLeaderName(g->GetID(), this->GetName()); + g->SaveGroupLeaderAA(); + database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID()); + database.SetGroupID(GetMerc()->GetName(), g->GetID(), this->CharacterID(), true); + database.RefreshGroupFromDB(this); + } + else + { + if (GetMerc()) + GetMerc()->Depop(); + } + } + } + } + } + else + LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); + } + if (LFP) { + // If we are looking for players, update to show we are on our own now. + UpdateLFP(); + } + + return; +} + void Client::Handle_OP_GroupFollow(const EQApplicationPacket *app) { Handle_OP_GroupFollow2(app); @@ -6590,42 +6620,42 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) return; } - if(LFP) { + if (LFP) { // If we were looking for players to start our own group, but we accept an invitation to another // group, turn LFP off. database.SetLFP(CharacterID(), false); worldserver.StopLFP(CharacterID()); } - GroupGeneric_Struct* gf = (GroupGeneric_Struct*) app->pBuffer; + GroupGeneric_Struct* gf = (GroupGeneric_Struct*)app->pBuffer; Mob* inviter = entity_list.GetClientByName(gf->name1); - if(inviter != nullptr && inviter->IsClient()) { + if (inviter != nullptr && inviter->IsClient()) { isgrouped = true; - strn0cpy(gf->name1,inviter->GetName(), 64); - strn0cpy(gf->name2,this->GetName(), 64); + strn0cpy(gf->name1, inviter->GetName(), 64); + strn0cpy(gf->name2, this->GetName(), 64); Raid* raid = entity_list.GetRaidByClient(inviter->CastToClient()); Raid* iraid = entity_list.GetRaidByClient(this); //inviter has a raid don't do group stuff instead do raid stuff! - if(raid){ + if (raid){ // Suspend the merc while in a raid (maybe a rule could be added for this) if (GetMerc()) GetMerc()->Suspend(); uint32 groupToUse = 0xFFFFFFFF; - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(raid->members[x].member){ //this assumes the inviter is in the zone - if(raid->members[x].member == inviter->CastToClient()){ + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (raid->members[x].member){ //this assumes the inviter is in the zone + if (raid->members[x].member == inviter->CastToClient()){ groupToUse = raid->members[x].GroupNumber; break; } } } - if(iraid == raid){ //both in same raid + if (iraid == raid){ //both in same raid uint32 ngid = raid->GetGroup(inviter->GetName()); - if(raid->GroupCount(ngid) < 6){ + if (raid->GroupCount(ngid) < 6){ raid->MoveMember(GetName(), ngid); raid->SendGroupDisband(this); //raid->SendRaidGroupAdd(GetName(), ngid); @@ -6634,8 +6664,8 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } return; } - if(raid->RaidCount() < MAX_RAID_MEMBERS){ - if(raid->GroupCount(groupToUse) < 6){ + if (raid->RaidCount() < MAX_RAID_MEMBERS){ + if (raid->GroupCount(groupToUse) < 6){ raid->SendRaidCreate(this); raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->AddMember(this, groupToUse); @@ -6643,7 +6673,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) //raid->SendRaidGroupAdd(GetName(), groupToUse); //raid->SendGroupUpdate(this); raid->GroupUpdate(groupToUse); //break - if(raid->IsLocked()) { + if (raid->IsLocked()) { raid->SendRaidLockTo(this); } return; @@ -6653,7 +6683,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->AddMember(this); raid->SendBulkRaid(this); - if(raid->IsLocked()) { + if (raid->IsLocked()) { raid->SendRaidLockTo(this); } return; @@ -6663,14 +6693,14 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) Group* group = entity_list.GetGroupByClient(inviter->CastToClient()); - if(!group){ + if (!group){ //Make new group group = new Group(inviter); - if(!group) + if (!group) return; entity_list.AddGroup(group); - if(group->GetID() == 0) { + if (group->GetID() == 0) { Message(13, "Unable to get new group id. Cannot create group."); inviter->Message(13, "Unable to get new group id. Cannot create group."); return; @@ -6683,10 +6713,10 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) group->UpdateGroupAAs(); //Invite the inviter into the group first.....dont ask - if(inviter->CastToClient()->GetClientVersion() < EQClientSoD) + if (inviter->CastToClient()->GetClientVersion() < EQClientSoD) { - EQApplicationPacket* outapp=new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* outgj=(GroupJoin_Struct*)outapp->pBuffer; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* outgj = (GroupJoin_Struct*)outapp->pBuffer; strcpy(outgj->membername, inviter->GetName()); strcpy(outgj->yourname, inviter->GetName()); outgj->action = groupActInviteInitial; // 'You have formed the group'. @@ -6706,28 +6736,28 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } } - if(!group) + if (!group) return; inviter->CastToClient()->QueuePacket(app);//notify inviter the client accepted - if(!group->AddMember(this)) + if (!group->AddMember(this)) return; - if(inviter->CastToClient()->IsLFP()) { + if (inviter->CastToClient()->IsLFP()) { // If the player who invited us to a group is LFP, have them update world now that we have joined // their group. inviter->CastToClient()->UpdateLFP(); } - if(GetClientVersion() >= EQClientSoD) + if (GetClientVersion() >= EQClientSoD) SendGroupJoinAcknowledge(); database.RefreshGroupFromDB(this); group->SendHPPacketsTo(this); // Temporary hack for SoD, as things seem to work quite differently - if(inviter->CastToClient()->GetClientVersion() >= EQClientSoD) + if (inviter->CastToClient()->GetClientVersion() >= EQClientSoD) database.RefreshGroupFromDB(inviter->CastToClient()); // Add the merc back into the new group @@ -6748,7 +6778,7 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) worldserver.SendPacket(pack); safe_delete(pack); } - else if(inviter == nullptr) + else if (inviter == nullptr) { ServerPacket* pack = new ServerPacket(ServerOP_GroupFollow, sizeof(ServerGroupFollow_Struct)); ServerGroupFollow_Struct *sgfs = (ServerGroupFollow_Struct *)pack->pBuffer; @@ -6760,4847 +6790,173 @@ void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) } } -void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) +void Client::Handle_OP_GroupInvite(const EQApplicationPacket *app) { - if (app->size != sizeof(GroupGeneric_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for GroupGeneric_Struct: Expected: %i, Got: %i", - sizeof(GroupGeneric_Struct), app->size); + //this seems to be the initial invite to form a group + Handle_OP_GroupInvite2(app); +} + +void Client::Handle_OP_GroupInvite2(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupInvite_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupInvite: Expected: %i, Got: %i", + sizeof(GroupInvite_Struct), app->size); return; } - LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); + GroupInvite_Struct* gis = (GroupInvite_Struct*)app->pBuffer; - GroupGeneric_Struct* gd = (GroupGeneric_Struct*) app->pBuffer; + Mob *Invitee = entity_list.GetMob(gis->invitee_name); - Raid *raid = entity_list.GetRaidByClient(this); - if(raid){ - Mob* memberToDisband = nullptr; + if (Invitee == this) + { + Message_StringID(clientMessageWhite, GROUP_INVITEE_SELF); + return; + } - if(!raid->IsGroupLeader(GetName())) - memberToDisband = this; - else - memberToDisband = GetTarget(); - - if(!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if(!memberToDisband) - memberToDisband = this; - - if(!memberToDisband->IsClient()) - return; - - //we have a raid.. see if we're in a raid group - uint32 grp = raid->GetGroup(memberToDisband->GetName()); - bool wasGrpLdr = raid->members[raid->GetPlayerIndex(memberToDisband->GetName())].IsGroupLeader; - if(grp < 12){ - if(wasGrpLdr){ - raid->SetGroupLeader(memberToDisband->GetName(), false); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) + if (Invitee) { + if (Invitee->IsClient()) { + if ((!Invitee->IsGrouped() && !Invitee->IsRaidGrouped()) || + (Invitee->GetGroup() && Invitee->CastToClient()->GetMerc() && Invitee->GetGroup()->GroupCount() == 2)) + { + if (app->GetOpcode() == OP_GroupInvite2) { - if(raid->members[x].GroupNumber == grp) - { - if(strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) - { - raid->SetGroupLeader(raid->members[x].membername); - break; - } - } + //Make a new packet using all the same information but make sure it's a fixed GroupInvite opcode so we + //Don't have to deal with GroupFollow2 crap. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(outapp->pBuffer, app->pBuffer, outapp->size); + Invitee->CastToClient()->QueuePacket(outapp); + safe_delete(outapp); + return; } - } - raid->MoveMember(memberToDisband->GetName(), 0xFFFFFFFF); - raid->GroupUpdate(grp); //break - //raid->SendRaidGroupRemove(memberToDisband->GetName(), grp); - //raid->SendGroupUpdate(memberToDisband->CastToClient()); - raid->SendGroupDisband(memberToDisband->CastToClient()); - } - //we're done - return; - } - - Group* group = GetGroup(); - - if(!group) - return; - -#ifdef BOTS - // this block is necessary to allow more control over controlling how bots are zoned or camped. - if(Bot::GroupHasBot(group)) { - if(group->IsLeader(this)) { - if((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { - Bot::ProcessBotGroupDisband(this, std::string()); - } else { - Mob* tempMember = entity_list.GetMob(gd->name2); - if(tempMember) { - if(tempMember->IsBot()) - Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); - } - } - } - } -#endif - if((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { - group->DisbandGroup(); - if(GetMerc() != nullptr) - GetMerc()->Suspend(); - } else { - Mob* memberToDisband = nullptr; - memberToDisband = GetTarget(); - - if(!memberToDisband) - memberToDisband = entity_list.GetMob(gd->name2); - - if(memberToDisband ) { - if(group->IsLeader(this)) { - // the group leader can kick other members out of the group... - //group->DelMember(memberToDisband,false); - if(memberToDisband->IsClient()) - { - group->DelMember(memberToDisband,false); - Client* memberClient = memberToDisband->CastToClient(); - Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); - if(memberClient && memberMerc && group) - { - if(!memberMerc->IsGrouped() && !memberClient->IsGrouped()) { - Group *g = new Group(memberClient); - - entity_list.AddGroup(g); - - if(g->GetID() == 0) { - safe_delete(g); - return; - } - if(Merc::AddMercToGroup(memberMerc, g)) { - database.SetGroupLeaderName(g->GetID(), memberClient->GetName()); - g->SaveGroupLeaderAA(); - database.SetGroupID(memberClient->GetName(), g->GetID(), memberClient->CharacterID()); - database.SetGroupID(memberMerc->GetName(), g->GetID(), memberClient->CharacterID(), true); - database.RefreshGroupFromDB(memberClient); - } - } - } - } - else if(memberToDisband->IsMerc()) { - memberToDisband->CastToMerc()->Suspend(); - } - } - else { - // ...but other members can only remove themselves - group->DelMember(this,false); - - if(!IsGrouped() && GetMerc() != nullptr) { - if(!IsGrouped()) { - Group *g = new Group(this); - - if(!g) { - delete g; - g = nullptr; - return; - } - - entity_list.AddGroup(g); - - if(g->GetID() == 0) { - safe_delete(g); - return; - } - - if(Merc::AddMercToGroup(GetMerc(), g)) { - database.SetGroupLeaderName(g->GetID(), this->GetName()); - g->SaveGroupLeaderAA(); - database.SetGroupID(this->GetName(), g->GetID(), this->CharacterID()); - database.SetGroupID(GetMerc()->GetName(), g->GetID(), this->CharacterID(), true); - database.RefreshGroupFromDB(this); - } - else - { - if(GetMerc()) - GetMerc()->Depop(); - } - } - } - } - } - else - LogFile->write(EQEMuLog::Error, "Failed to remove player from group. Unable to find player named %s in player group", gd->name2); - } - if(LFP) { - // If we are looking for players, update to show we are on our own now. - UpdateLFP(); - } - - return; -} - -void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) -{ -//should check for leader, only they should be able to do this.. - Group* group = GetGroup(); - if (group) - group->DisbandGroup(); - - if(LFP) - UpdateLFP(); - - return; -} - -void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/emote"); - return; - } - if (app->size != sizeof(GMEmoteZone_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMEmoteZone, size=%i, expected %i", app->size, sizeof(GMEmoteZone_Struct)); - return; - } - GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer; - char* newmessage=0; - if(strstr(gmez->text,"^")==0) - entity_list.Message(0, 15, gmez->text); - else{ - for(newmessage = strtok((char*)gmez->text,"^");newmessage!=nullptr;newmessage=strtok(nullptr, "^")) - entity_list.Message(0, 15, newmessage); - } - return; -} - -void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) { - - if(app->size != sizeof(Inspect_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); - return; - } - - Inspect_Struct* ins = (Inspect_Struct*) app->pBuffer; - Mob* tmp = entity_list.GetMob(ins->TargetID); - - if(tmp != 0 && tmp->IsClient()) { - if(tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target - // Inspecting an SoF or later client will make the server handle the request - else { ProcessInspectRequest(tmp->CastToClient(), this); } - } - -#ifdef BOTS - if(tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } -#endif - - return; -} - -void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { - - if (app->size != sizeof(InspectResponse_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); - return; - } - - //Fills the app sent from client. - EQApplicationPacket* outapp = app->Copy(); - InspectResponse_Struct* insr = (InspectResponse_Struct*) outapp->pBuffer; - Mob* tmp = entity_list.GetMob(insr->TargetID); - const Item_Struct* item = nullptr; - - for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { - const ItemInst* inst = GetInv().GetItem(L); - item = inst ? inst->GetItem() : nullptr; - - if(item) { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = item->Icon; - } - else { insr->itemicons[L] = 0xFFFFFFFF; } - } - - const ItemInst* inst = GetInv().GetItem(MainAmmo); - item = inst ? inst->GetItem() : nullptr; - - if(item) { - // another one..I did these, didn't I!!? - strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); - insr->itemicons[SoF::slots::MainAmmo] = item->Icon; - } - else { insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*) insr->text; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SetPlayerInspectMessage(name, &playermessage); - - if(tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester - - return; -} - -void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) { - - if (app->size != sizeof(InspectMessage_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); - return; - } - - InspectMessage_Struct* newmessage = (InspectMessage_Struct*) app->pBuffer; - InspectMessage_Struct& playermessage = this->GetInspectMessage(); - memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); - database.SetPlayerInspectMessage(name, &playermessage); -} - -#if 0 // I dont think there's an op for this now, and we check this - // when the client is sitting -void Client::Handle_OP_Medding(const EQApplicationPacket *app) -{ - if (app->pBuffer[0]) - medding = true; - else - medding = false; - return; -} -#endif - -void Client::Handle_OP_DeleteSpell(const EQApplicationPacket *app) -{ - if(app->size != sizeof(DeleteSpell_Struct)) - return; - - EQApplicationPacket* outapp = app->Copy(); - DeleteSpell_Struct* dss = (DeleteSpell_Struct*) outapp->pBuffer; - - if(dss->spell_slot < 0 || dss->spell_slot > int(MAX_PP_SPELLBOOK)) - return; - - if(m_pp.spell_book[dss->spell_slot] != SPELLBOOK_UNKNOWN) { - m_pp.spell_book[dss->spell_slot] = SPELLBOOK_UNKNOWN; - dss->success = 1; - } - else - dss->success = 0; - - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(LoadSpellSet_Struct)) { - printf("Wrong size of LoadSpellSet_Struct! Expected: %zu, Got: %i\n",sizeof(LoadSpellSet_Struct),app->size); - return; - } - int i; - LoadSpellSet_Struct* ss=(LoadSpellSet_Struct*)app->pBuffer; - for(i=0;ispell[i] != 0xFFFFFFFF) - UnmemSpell(i,true); - } -} - - -void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(PetitionBug_Struct)) - printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n",sizeof(PetitionBug_Struct),app->size); - else{ - Message(0, "Petition Bugs are not supported, please use /bug."); - } - return; -} - -void Client::Handle_OP_Bug(const EQApplicationPacket *app) -{ - if(app->size!=sizeof(BugStruct)) - printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct)); - else{ - BugStruct* bug=(BugStruct*)app->pBuffer; - database.UpdateBug(bug); - } - return; -} - -void Client::Handle_OP_Petition(const EQApplicationPacket *app) -{ - if (app->size <= 1) - return; - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) - { - Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); - return; - }*/ - else - { - if(petition_list.FindPetitionByAccountName(AccountName())) - { - Message(0,"You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); - return; - } - Petition* pet = new Petition(CharacterID()); - pet->SetAName(this->AccountName()); - pet->SetClass(this->GetClass()); - pet->SetLevel(this->GetLevel()); - pet->SetCName(this->GetName()); - pet->SetRace(this->GetRace()); - pet->SetLastGM(""); - pet->SetCName(this->GetName()); - pet->SetPetitionText((char*) app->pBuffer); - pet->SetZone(zone->GetZoneID()); - pet->SetUrgency(0); - petition_list.AddPetition(pet); - database.InsertPetitionToDB(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); - } - return; -} - -void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Petition_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); - return; - } - Petition_Struct* inpet = (Petition_Struct*) app->pBuffer; - - Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); - //if (inpet->urgency != pet->GetUrgency()) - pet->SetUrgency(inpet->urgency); - pet->SetLastGM(this->GetName()); - pet->SetGMText(inpet->gmtext); - - pet->SetCheckedOut(false); - petition_list.UpdatePetition(pet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) -{ - Handle_OP_PetitionDelete(app); -} - -void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetitionUpdate_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); - return; - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct)); - PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*) outapp->pBuffer; - pet->petnumber = *((int*) app->pBuffer); - pet->color = 0x00; - pet->status = 0xFFFFFFFF; - pet->senttime = 0; - strcpy(pet->accountid, ""); - strcpy(pet->gmsenttoo, ""); - pet->quetotal = petition_list.GetTotalPetitions(); - strcpy(pet->charname, ""); - FastQueuePacket(&outapp); - - if (petition_list.DeletePetition(pet->petnumber) == -1) - std::cout << "Something is borked with: " << pet->petnumber << std::endl; - petition_list.ClearPetitions(); - petition_list.UpdateGMQueue(); - petition_list.ReadDatabase(); - petition_list.UpdateZoneListQueue(); - return; -} - -void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PetCommand_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); - return; - } - char val1[20]={0}; - PetCommand_Struct* pet = (PetCommand_Struct*) app->pBuffer; - Mob* mypet = this->GetPet(); - - if(!mypet || pet->command == PET_LEADER) - { - if(pet->command == PET_LEADER) - { - if(mypet && (!GetTarget() || GetTarget() == mypet)) - { - mypet->Say_StringID(PET_LEADERIS, GetName()); - } - else if((mypet = GetTarget())) - { - Mob *Owner = mypet->GetOwner(); - if(Owner) - mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); else - mypet->Say_StringID(I_FOLLOW_NOONE); - } - } - - return; - } - - if(mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) - return; - - // just let the command "/pet get lost" work for familiars - if(mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) - return; - - uint32 PetCommand = pet->command; - - // Handle Sit/Stand toggle in UF and later. - if(GetClientVersion() >= EQClientUnderfoot) - { - if(PetCommand == PET_SITDOWN) - if(mypet->GetPetOrder() == SPO_Sit) - PetCommand = PET_STANDUP; - } - - switch(PetCommand) - { - case PET_ATTACK: { - if (!GetTarget()) - break; - if (GetTarget()->IsMezzed()) { - Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); - break; - } - if (mypet->IsFeared()) - break; //prevent pet from attacking stuff while feared - - if (!mypet->IsAttackAllowed(GetTarget())) { - mypet->Say_StringID(NOT_LEGAL_TARGET); - break; - } - - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { - if (mypet->IsHeld()) { - if (!mypet->IsFocused()) { - mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. - if(mypet->GetPetOrder() != SPO_Guard) - mypet->SetPetOrder(SPO_Follow); - } else { - mypet->SetTarget(GetTarget()); - } + { + //The correct opcode, no reason to bother wasting time reconstructing the packet + Invitee->CastToClient()->QueuePacket(app); } - zone->AddAggroMob(); - mypet->AddToHateList(GetTarget(), 1); - Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); } } - break; +#ifdef BOTS + else if (Invitee->IsBot()) { + Bot::ProcessBotGroupInvite(this, std::string(Invitee->GetName())); + } +#endif } - case PET_BACKOFF: { - if (mypet->IsFeared()) break; //keeps pet running while feared + else + { + ServerPacket* pack = new ServerPacket(ServerOP_GroupInvite, sizeof(GroupInvite_Struct)); + memcpy(pack->pBuffer, gis, sizeof(GroupInvite_Struct)); + worldserver.SendPacket(pack); + safe_delete(pack); + } + return; +} - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_CALMING); - mypet->WipeHateList(); - mypet->SetTarget(nullptr); - } - break; - } - case PET_HEALTHREPORT: { - Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); - mypet->ShowBuffList(this); - //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); - break; - } - case PET_GETLOST: { - if (mypet->Charmed()) - break; - if (mypet->GetPetType() == petCharmed || !mypet->IsNPC()) { - // eqlive ignores this command - // we could just remove the charm - // and continue - mypet->BuffFadeByEffect(SE_Charm); - break; - } else { - SetPet(nullptr); - } +void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_GroupMakeLeader, app, GroupMakeLeader_Struct); - mypet->Say_StringID(MT_PetResponse, PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); + GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; - //Oddly, the client (Titanium) will still allow "/pet get lost" command despite me adding the code below. If someone can figure that out, you can uncomment this code and use it. - /* - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(PET_GETLOST_STRING); - mypet->CastToNPC()->Depop(); - } - */ + Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); - break; - } - case PET_GUARDHERE: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + Group* g = GetGroup(); - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - if(mypet->IsNPC()) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); - mypet->SetPetOrder(SPO_Guard); - mypet->CastToNPC()->SaveGuardSpot(); - } + if (NewLeader && g) + { + if (g->IsLeader(this)) + g->ChangeLeader(NewLeader); + else { + LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); + DumpPacket(app); } - break; } - case PET_FOLLOWME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF +} - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; +void Client::Handle_OP_GroupMentor(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupMentor_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupMentor, size=%i, expected %i", app->size, sizeof(GroupMentor_Struct)); + DumpPacket(app); + return; } - case PET_TAUNT: { - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_DO_TAUNT); - mypet->CastToNPC()->SetTaunting(true); - } - break; - } - case PET_NOTAUNT: { - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - Message_StringID(MT_PetResponse, PET_NO_TAUNT); - mypet->CastToNPC()->SetTaunting(false); - } - break; - } - case PET_GUARDME: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + GroupMentor_Struct *gms = (GroupMentor_Struct *)app->pBuffer; + gms->name[63] = '\0'; - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { - mypet->SetHeld(false); - mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; + if (IsRaidGrouped()) { + Raid *raid = GetRaid(); + if (!raid) + return; + uint32 group_id = raid->GetGroup(this); + if (group_id > 11) + return; + if (strlen(gms->name)) + raid->SetGroupMentor(group_id, gms->percent, gms->name); + else + raid->ClearGroupMentor(group_id); + return; } - case PET_SITDOWN: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if(!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); - } - break; - } - case PET_STANDUP: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + Group *group = GetGroup(); + if (!group) + return; - if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Follow); - mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); - } - break; - } - case PET_SLUMBER: { - if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + if (strlen(gms->name)) + group->SetGroupMentor(gms->percent, gms->name); + else + group->ClearGroupMentor(); - if(mypet->GetPetType() != petAnimation) { - mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); - mypet->SetPetOrder(SPO_Sit); - mypet->SetRunAnimSpeed(0); - if(!mypet->UseBardSpellLogic()) //maybe we can have a bard pet - mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting - mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); - } - break; - } - case PET_HOLD: { - if(GetAA(aaPetDiscipline) && mypet->IsNPC()){ - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF + return; +} - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } - break; +void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GroupRole_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); + DumpPacket(app); + return; } - case PET_HOLD_ON: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { - if (mypet->IsFeared()) - break; //could be exploited like PET_BACKOFF + GroupRole_Struct *grs = (GroupRole_Struct*)app->pBuffer; - mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); - mypet->WipeHateList(); - mypet->SetHeld(true); - } + Group *g = GetGroup(); + + if (!g) + return; + + switch (grs->RoleNumber) + { + case 1: //Main Tank + { + if (grs->Toggle) + g->DelegateMainTank(grs->Name1, grs->Toggle); + else + g->UnDelegateMainTank(grs->Name1, grs->Toggle); break; } - case PET_HOLD_OFF: { - if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsHeld()) - mypet->SetHeld(false); + case 2: //Main Assist + { + if (grs->Toggle) + g->DelegateMainAssist(grs->Name1, grs->Toggle); + else + g->UnDelegateMainAssist(grs->Name1, grs->Toggle); break; } - case PET_NOCAST: { - if(GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsNoCast()) { - Message_StringID(MT_PetResponse, PET_CASTING); - mypet->CastToNPC()->SetNoCast(false); - } else { - Message_StringID(MT_PetResponse, PET_NOT_CASTING); - mypet->CastToNPC()->SetNoCast(true); - } - } - break; - } - case PET_FOCUS: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } else { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_ON: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (!mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); - mypet->CastToNPC()->SetFocused(true); - } - } - break; - } - case PET_FOCUS_OFF: { - if(GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { - if (mypet->IsFeared()) - break; - if (mypet->IsFocused()) { - Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); - mypet->CastToNPC()->SetFocused(false); - } - } + case 3: //Puller + { + if (grs->Toggle) + g->DelegatePuller(grs->Name1, grs->Toggle); + else + g->UnDelegatePuller(grs->Name1, grs->Toggle); break; } default: - printf("Client attempted to use a unknown pet command:\n"); break; } } -void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*) app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->SetCheckedOut(false); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PetitionQue(const EQApplicationPacket *app) -{ -#ifdef _EQDEBUG - printf("%s looking at petitions..\n",this->GetName()); -#endif - return; -} - -void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) -{ - if (app->size < 2) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); - return; - } - if(petition_list.DeletePetitionByCharName((char*)app->pBuffer)) - Message_StringID(0,PETITION_DELETED); - else - Message_StringID(0,PETITION_NO_DELETE); - return; -} - -void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) -{ - if (app->size != sizeof(uint32)) { - std::cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; - return; - } - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - uint32 getpetnum = *((uint32*) app->pBuffer); - Petition* getpet = petition_list.GetPetitionByID(getpetnum); - if (getpet != 0) { - getpet->AddCheckout(); - getpet->SetCheckedOut(true); - getpet->SendPetitionToPlayer(this->CastToClient()); - petition_list.UpdatePetition(getpet); - petition_list.UpdateGMQueue(); - petition_list.UpdateZoneListQueue(); - } - } - return; -} - -void Client::Handle_OP_PetitionRefresh(const EQApplicationPacket *app) -{ - // This is When Client Asks for Petition Again and Again... - // break is here because it floods the zones and causes lag if it - // Were to actually do something:P We update on our own schedule now. - return; -} - -void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BookRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); - return; - } - BookRequest_Struct* book = (BookRequest_Struct*) app->pBuffer; - ReadBook(book); - if(GetClientVersion() >= EQClientSoF) - { - EQApplicationPacket EndOfBook(OP_FinishWindow, 0); - QueuePacket(&EndOfBook); - } - return; -} - -void Client::Handle_OP_Emote(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Emote_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Emote: got %d, expected %d", app->size, - sizeof(Emote_Struct)); - DumpPacket(app); - return; - } - - // Calculate new packet dimensions - Emote_Struct* in = (Emote_Struct*)app->pBuffer; - in->message[1023] = '\0'; - - const char* name = GetName(); - uint32 len_name = strlen(name); - uint32 len_msg = strlen(in->message); - // crash protection -- cheater - if (len_msg > 512) { - in->message[512] = '\0'; - len_msg = 512; - } - uint32 len_packet = sizeof(in->unknown01) + len_name - + len_msg + 1; - - // Construct outgoing packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Emote, len_packet); - Emote_Struct* out = (Emote_Struct*)outapp->pBuffer; - out->unknown01 = in->unknown01; - memcpy(out->message, name, len_name); - memcpy(&out->message[len_name], in->message, len_msg); - - /* - if (target && target->IsClient()) { - entity_list.QueueCloseClients(this, outapp, false, 100, target); - - cptr = outapp->pBuffer + 2; - - // not sure if live does this or not. thought it was a nice feature, but would take a lot to - // clean up grammatical and other errors. Maybe with a regex parser... - replacestr((char *)cptr, target->GetName(), "you"); - replacestr((char *)cptr, " he", " you"); - replacestr((char *)cptr, " she", " you"); - replacestr((char *)cptr, " him", " you"); - replacestr((char *)cptr, " her", " you"); - target->CastToClient()->QueuePacket(outapp); - - } - else - */ - entity_list.QueueCloseClients(this, outapp, true, 100, 0, true, FilterSocials); - - safe_delete(outapp); - return; -} - -void Client::Handle_OP_Animation(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Animation_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_Animation: got %d, expected %d", app->size, - sizeof(Animation_Struct)); - DumpPacket(app); - return; - } - - Animation_Struct *s = (Animation_Struct *) app->pBuffer; - - //might verify spawn ID, but it wouldent affect anything - - DoAnim(s->action, s->value); - - return; -} - -void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) -{ - if(app->size != sizeof(SetServerFilter_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized " - "OP_SetServerFilter: got %d, expected %d", app->size, - sizeof(SetServerFilter_Struct)); - DumpPacket(app); - return; - } - SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer; - ServerFilter(filter); - return; -} - -void Client::Handle_OP_GMDelCorpse(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GMDelCorpse_Struct)) - return; - if(this->Admin() < commandEditPlayerCorpses) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/delcorpse"); - return; - } - GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer; - Mob* corpse = entity_list.GetMob(dc->corpsename); - if(corpse==0) { - return; - } - if(corpse->IsCorpse() != true) { - return; - } - corpse->CastToCorpse()->Delete(); - std::cout << name << " deleted corpse " << dc->corpsename << std::endl; - Message(13, "Corpse %s deleted.", dc->corpsename); - return; -} - -void Client::Handle_OP_GMKick(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GMKick_Struct)) - return; - if(this->Admin() < minStatusToKick) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/kick"); - return; - } - GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer; - - Client* client = entity_list.GetClientByName(gmk->name); - if(client==0) { - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, gmk->gmname); - strcpy(skp->name, gmk->name); - skp->adminrank = this->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } - else { - entity_list.QueueClients(this,app); - //client->Kick(); - } - return; -} - -void Client::Handle_OP_GMServers(const EQApplicationPacket *app) -{ - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_ZoneStatus, strlen(this->GetName())+2); - memset(pack->pBuffer, (uint8) admin, 1); - strcpy((char *) &pack->pBuffer[1], this->GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - return; -} - -void Client::Handle_OP_Illusion(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Illusion_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, - sizeof(Illusion_Struct)); - DumpPacket(app); - return; - } - - if(!GetGM()) - { - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName()); - return; - } - - Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer; - //these need to be implemented - /* - texture = bnpc->texture; - helmtexture = bnpc->helmtexture; - luclinface = bnpc->luclinface; - */ - race = bnpc->race; - size = 0; - - entity_list.QueueClients(this,app); - return; -} - -void Client::Handle_OP_GMBecomeNPC(const EQApplicationPacket *app) -{ - if(this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/becomenpc"); - return; - } - if (app->size != sizeof(BecomeNPC_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMBecomeNPC, size=%i, expected %i", app->size, sizeof(BecomeNPC_Struct)); - return; - } - //entity_list.QueueClients(this, app, false); - BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer; - - Mob* cli = (Mob*) entity_list.GetMob(bnpc->id); - if(cli==0) - return; - - if(cli->IsClient()) - cli->CastToClient()->QueuePacket(app); - cli->SendAppearancePacket(AT_NPCName, 1, true); - cli->CastToClient()->SetBecomeNPC(true); - cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel); - cli->Message_StringID(0,TOGGLE_OFF); - cli->CastToClient()->tellsoff = true; - //TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this. - return; -} - -void Client::Handle_OP_Fishing(const EQApplicationPacket *app) -{ - if(!p_timers.Expired(&database, pTimerFishing, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - - if (CanFish()) { - parse->EventPlayer(EVENT_FISH_START, this, "", 0); - - //these will trigger GoFish() after a delay if we're able to actually fish, and if not, we won't stop the client from trying again immediately (although we may need to tell it to repop the button) - p_timers.Start(pTimerFishing, FishingReuseTime-1); - fishing_timer.Start(); - } - return; -// Changes made based on Bobs work on foraging. Now can set items in the forage database table to -// forage for. -} - -void Client::Handle_OP_Forage(const EQApplicationPacket *app) -{ - - if(!p_timers.Expired(&database, pTimerForaging, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerForaging, ForagingReuseTime-1); - - ForageItem(); - - return; -} - -void Client::Handle_OP_Mend(const EQApplicationPacket *app) -{ - if(!HasSkill(SkillMend)) - return; - - if(!p_timers.Expired(&database, pTimerMend, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - p_timers.Start(pTimerMend, MendReuseTime-1); - - int mendhp = GetMaxHP() / 4; - int currenthp = GetHP(); - if (MakeRandomInt(0, 199) < (int)GetSkill(SkillMend)) { - - int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend; - - if(MakeRandomInt(0,99) < criticalchance){ - mendhp *= 2; - Message_StringID(4,MEND_CRITICAL); - } - SetHP(GetHP() + mendhp); - SendHPUpdate(); - Message_StringID(4,MEND_SUCCESS); - } else { - /* the purpose of the following is to make the chance to worsen wounds much less common, - which is more consistent with the way eq live works. - according to my math, this should result in the following probability: - 0 skill - 25% chance to worsen - 20 skill - 23% chance to worsen - 50 skill - 16% chance to worsen */ - if ((GetSkill(SkillMend) <= 75) && (MakeRandomInt(GetSkill(SkillMend),100) < 75) && (MakeRandomInt(1, 3) == 1)) - { - SetHP(currenthp > mendhp ? (GetHP() - mendhp) : 1); - SendHPUpdate(); - Message_StringID(4,MEND_WORSEN); - } - else - Message_StringID(4,MEND_FAIL); - } - - CheckIncreaseSkill(SkillMend, nullptr, 10); - return; -} - -void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app) -{ - if(!ClientFinishedLoading()) - { - SetHP(GetHP()-1); - return; - } - - if(app->size != sizeof(EnvDamage2_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_EnvDamage: got %d, expected %d", app->size, - sizeof(EnvDamage2_Struct)); - DumpPacket(app); - return; - } - EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer; - if(admin >= minStatusToAvoidFalling && GetGM()){ - Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP()-1);//needed or else the client wont acknowledge - return; - } else if(GetInvul()) { - Message(13, "Your invuln status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype); - SetHP(GetHP()-1);//needed or else the client wont acknowledge - return; - } - - int damage = ed->damage; - - if (ed->dmgtype == 252) { - - switch(GetAA(aaAcrobatics)) { //Don't know what acrobatics effect is yet but it should be done client side via aa effect.. till then - case 1: - damage = damage * 95 / 100; - break; - case 2: - damage = damage * 90 / 100; - break; - case 3: - damage = damage * 80 / 100; - break; - } - } - - if(damage < 0) - damage = 31337; - - else if(zone->GetZoneID() == 183 || zone->GetZoneID() == 184) - return; - else - SetHP(GetHP() - damage); - - if(GetHP() <= 0) - { - mod_client_death_env(); - - Death(0, 32000, SPELL_UNKNOWN, SkillHandtoHand); - } - SendHPUpdate(); - return; -} - -void Client::Handle_OP_Damage(const EQApplicationPacket *app) -{ - if(app->size != sizeof(CombatDamage_Struct)) { - LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Damage: got %d, expected %d", app->size, - sizeof(CombatDamage_Struct)); - DumpPacket(app); - return; - } - - // Broadcast to other clients - CombatDamage_Struct* damage = (CombatDamage_Struct*)app->pBuffer; - //dont send to originator of falling damage packets - entity_list.QueueClients(this, app, (damage->type==DamageTypeFalling)); - return; -} - -void Client::Handle_OP_AAAction(const EQApplicationPacket *app) -{ - mlog(AA__IN, "Received OP_AAAction"); - mpkt(AA__IN, app); - - if(app->size!=sizeof(AA_Action)){ - printf("Error! OP_AAAction size didnt match!\n"); - return; - } - AA_Action* action=(AA_Action*)app->pBuffer; - - if(action->action == aaActionActivate) {//AA Hotkey - mlog(AA__MESSAGE, "Activating AA %d", action->ability); - ActivateAA((aaID) action->ability); - } else if(action->action == aaActionBuy) { - BuyAA(action); - } - else if(action->action == aaActionDisableEXP){ //Turn Off AA Exp - if(m_epp.perAA > 0) - Message_StringID(0, AA_OFF); - m_epp.perAA = 0; - SendAAStats(); - } else if(action->action == aaActionSetEXP) { - if(m_epp.perAA == 0) - Message_StringID(0, AA_ON); - m_epp.perAA = action->exp_value; - if (m_epp.perAA<0 || m_epp.perAA>100) m_epp.perAA=0; // stop exploit with sanity check - // send an update - SendAAStats(); - SendAATable(); - } else { - printf("Unknown AA action: %u %u 0x%x %d\n", action->action, action->ability, action->unknown08, action->exp_value); - } - - return; -} - -void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // Client has elected to buy an item from a Trader - // - _pkt(TRADING__PACKETS, app); - - if(app->size==sizeof(TraderBuy_Struct)){ - - TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; - - if(Client* Trader=entity_list.GetClientByID(tbs->TraderID)){ - - BuyTraderItem(tbs,Trader,app); - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - } - } - else { - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Struct size mismatch"); - - } - return; -} - -void Client::Handle_OP_Trader(const EQApplicationPacket *app) -{ - // Bazaar Trader: - // - // SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed. - // I don't know what they are for (yet), but it doesn't seem to matter that we ignore them. - - _pkt(TRADING__PACKETS, app); - - uint32 max_items = 80; - - /* - if (GetClientVersion() >= EQClientRoF) - max_items = 200; - */ - - //Show Items - if(app->size==sizeof(Trader_ShowItems_Struct)) - { - Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer; - - switch(sis->Code) - { - case BazaarTrader_EndTraderMode: { - Trader_EndTrader(); - break; - } - case BazaarTrader_EndTransaction: { - - Client* c=entity_list.GetClientByID(sis->TraderID); - if(c) - c->WithCustomer(0); - else - _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); - - break; - } - case BazaarTrader_ShowItems: { - Trader_ShowItems(); - break; - } - default: { - _log(TRADING__CLIENT, "Unhandled action code in OP_Trader ShowItems_Struct"); - break; - } - } - } - else if(app->size==sizeof(ClickTrader_Struct)) - { - if(Buyer) { - Trader_EndTrader(); - Message(13, "You cannot be a Trader and Buyer at the same time."); - return; - } - - ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer; - - if(ints->Code==BazaarTrader_StartTraderMode) - { - GetItems_Struct* gis=GetTraderItems(); - - // Verify there are no NODROP or items with a zero price - bool TradeItemsValid = true; - - for(uint32 i = 0; i < max_items; i++) { - - if(gis->Items[i] == 0) break; - - if(ints->ItemCost[i] == 0) { - Message(13, "Item in Trader Satchel with no price. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - const Item_Struct *Item = database.GetItem(gis->Items[i]); - - if(!Item) { - Message(13, "Unexpected error. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - - if(Item->NoDrop == 0) { - Message(13, "NODROP Item in Trader Satchel. Unable to start trader mode"); - TradeItemsValid = false; - break; - } - } - - if(!TradeItemsValid) { - Trader_EndTrader(); - return; - } - - for (uint32 i = 0; i < max_items; i++) { - if(database.GetItem(gis->Items[i])) { - database.SaveTraderItem(this->CharacterID(),gis->Items[i],gis->SerialNumber[i], - gis->Charges[i],ints->ItemCost[i],i); - } else { - //return; //sony doesnt memset so assume done on first bad item - break; - } - - } - safe_delete(gis); - - this->Trader_StartTrader(); - - if (GetClientVersion() >= EQClientRoF) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); - TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer; - tss->Code = BazaarTrader_StartTraderMode2; - QueuePacket(outapp); - _pkt(TRADING__PACKETS, outapp); - safe_delete(outapp); - } - } - else if (app->size==sizeof(TraderStatus_Struct)) - { - TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; - - if(tss->Code==BazaarTrader_ShowItems) - { - Trader_ShowItems(); - } - } - else { - _log(TRADING__CLIENT,"Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", - ints->Code); - - LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->Code); - } - } - - else if(app->size==sizeof(TraderPriceUpdate_Struct)) - { - HandleTraderPriceUpdate(app); - } - else { - _log(TRADING__CLIENT,"Unknown size for OP_Trader: %i\n", app->size); - LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size); - DumpPacket(app); - return; - } - - return; -} - -void Client::Handle_OP_GMFind(const EQApplicationPacket *app) -{ - if (this->Admin() < minStatusToUseGMCommands) { - Message(13, "Your account has been reported for hacking."); - database.SetHackerFlag(this->account_name, this->name, "/find"); - return; - } - if (app->size != sizeof(GMSummon_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GMFind, size=%i, expected %i", app->size, sizeof(GMSummon_Struct)); - return; - } - //Break down incoming - GMSummon_Struct* request=(GMSummon_Struct*)app->pBuffer; - //Create a new outgoing - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GMFind, sizeof(GMSummon_Struct)); - GMSummon_Struct* foundplayer=(GMSummon_Struct*)outapp->pBuffer; - //Copy the constants - strcpy(foundplayer->charname,request->charname); - strcpy(foundplayer->gmname, request->gmname); - //Check if the NPC exits intrazone... - Mob* gt = entity_list.GetMob(request->charname); - if (gt != 0) { - foundplayer->success=1; - foundplayer->x=(int32)gt->GetX(); - foundplayer->y=(int32)gt->GetY(); - - foundplayer->z=(int32)gt->GetZ(); - foundplayer->zoneID=zone->GetZoneID(); - } - //Send the packet... - FastQueuePacket(&outapp); - return; -} - -void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) -{ - if (app->size != sizeof(PickPocket_Struct)) - { - LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); - DumpPacket(app); - } - - if(!HasSkill(SkillPickPockets)) - { - return; - } - - if(!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) - { - Message(13,"Ability recovery time not yet met."); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName()); - return; - } - PickPocket_Struct* pick_in = (PickPocket_Struct*) app->pBuffer; - - Mob* victim = entity_list.GetMob(pick_in->to); - if (!victim) - return; - - p_timers.Start(pTimerBeggingPickPocket, 8); - if (victim == this){ - Message(0,"You catch yourself red-handed."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->GetOwnerID()){ - Message(0,"You cannot steal from pets!"); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } - else if (victim->IsNPC()){ - victim->CastToNPC()->PickPocket(this); - } - else{ - Message(0,"Stealing from clients not yet supported."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); - sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer; - pick_out->coin = 0; - pick_out->from = victim->GetID(); - pick_out->to = GetID(); - pick_out->myskill = GetSkill(SkillPickPockets); - pick_out->type = 0; - //if we do not send this packet the client will lock up and require the player to relog. - QueuePacket(outapp); - safe_delete(outapp); - } -} - -void Client::Handle_OP_Bind_Wound(const EQApplicationPacket *app) -{ - if (app->size != sizeof(BindWound_Struct)){ - LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet"); - DumpPacket(app); - } - BindWound_Struct* bind_in = (BindWound_Struct*) app->pBuffer; - Mob* bindmob = entity_list.GetMob(bind_in->to); - if (!bindmob){ - LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName()); - } - else { - LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName()); - BindWound(bindmob, true); - } - return; -} - -void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) -{ - int PlayerClass = GetClass(); - - if((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) - return; - - if (app->size != sizeof(TrackTarget_Struct)) - { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", - sizeof(TrackTarget_Struct), app->size); - return; - } - - TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; - - TrackingID = tts->EntityID; -} - -void Client::Handle_OP_Track(const EQApplicationPacket *app) -{ - if(GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) - return; - - if( GetSkill(SkillTracking)==0 ) - SetSkill(SkillTracking,1); - else - CheckIncreaseSkill(SkillTracking, nullptr, 15); - - if(!entity_list.MakeTrackPacket(this)) - LogFile->write(EQEMuLog::Error, "Unable to generate OP_Track packet requested by client."); - - return; -} - -void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) -{ - // size 0 send right after OP_Track - return; -} - -void Client::Handle_0x0193(const EQApplicationPacket *app) -{ - // Not sure what this opcode does. It started being sent when OP_ClientUpdate was - // changed to pump OP_ClientUpdate back out instead of OP_MobUpdate - // 2 bytes: 00 00 -} - -void Client::Handle_OP_ClientError(const EQApplicationPacket *app) -{ - ClientError_Struct* error = (ClientError_Struct*)app->pBuffer; - LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name); - LogFile->write(EQEMuLog::Error, "Error message:%s", error->message); - return; -} - -void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) -{ - if(IsInAGuild()) - { - SendGuildRanks(); - SendGuildMembers(); - } - return; -} - -void Client::Handle_OP_TGB(const EQApplicationPacket *app) -{ - OPTGB(app); - return; -} - -void Client::Handle_OP_Split(const EQApplicationPacket *app) -{ - if (app->size != sizeof(Split_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); - return; - } - // The client removes the money on its own, but we have to - // update our state anyway, and make sure they had enough to begin - // with. - Split_Struct *split = (Split_Struct *)app->pBuffer; - //Per the note above, Im not exactly sure what to do on error - //to notify the client of the error... - if(!isgrouped) { - Message(13, "You can not split money if your not in a group."); - return; - } - Group *cgroup = GetGroup(); - if(cgroup == nullptr) { - //invalid group, not sure if we should say more... - Message(13, "You can not split money if your not in a group."); - return; - } - - if(!TakeMoneyFromPP(static_cast(split->copper) + - 10 * static_cast(split->silver) + - 100 * static_cast(split->gold) + - 1000 * static_cast(split->platinum))) { - Message(13, "You do not have enough money to do that split."); - return; - } - cgroup->SplitMoney(split->copper, split->silver, split->gold, split->platinum); - - return; - -} - -void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillSenseTraps)) - return; - - if(!p_timers.Expired(&database, pTimerSenseTraps, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = SenseTrapsReuseTime; - switch(GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerSenseTraps, reuse-1); - - Trap* trap = entity_list.FindNearbyTrap(this,800); - - CheckIncreaseSkill(SkillSenseTraps, nullptr); - - if (trap && trap->skill > 0) { - int uskill = GetSkill(SkillSenseTraps); - if ((MakeRandomInt(0,99) + uskill) >= (MakeRandomInt(0,99) + trap->skill*0.75)) - { - float xdif = trap->x - GetX(); - float ydif = trap->y - GetY(); - if (xdif == 0 && ydif == 0) - Message(MT_Skills,"You sense a trap right under your feet!"); - else if (xdif > 10 && ydif > 10) - Message(MT_Skills,"You sense a trap to the NorthWest."); - else if (xdif < -10 && ydif > 10) - Message(MT_Skills,"You sense a trap to the NorthEast."); - else if (ydif > 10) - Message(MT_Skills,"You sense a trap to the North."); - else if (xdif > 10 && ydif < -10) - Message(MT_Skills,"You sense a trap to the SouthWest."); - else if (xdif < -10 && ydif < -10) - Message(MT_Skills,"You sense a trap to the SouthEast."); - else if (ydif < -10) - Message(MT_Skills,"You sense a trap to the South."); - else if (xdif > 10) - Message(MT_Skills,"You sense a trap to the West."); - else - Message(MT_Skills,"You sense a trap to the East."); - trap->detected = true; - - float angle = CalculateHeadingToTarget(trap->x, trap->y); - - if(angle < 0) - angle = (256+angle); - - angle *= 2; - MovePC(zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), angle); - return; - } - } - Message(MT_Skills,"You did not find any traps nearby."); - return; -} - -void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app) -{ - if (!HasSkill(SkillDisarmTraps)) - return; - - if(!p_timers.Expired(&database, pTimerDisarmTraps, false)) { - Message(13,"Ability recovery time not yet met."); - return; - } - int reuse = DisarmTrapsReuseTime; - switch(GetAA(aaAdvTrapNegotiation)) { - case 1: - reuse -= 1; - break; - case 2: - reuse -= 3; - break; - case 3: - reuse -= 5; - break; - } - p_timers.Start(pTimerDisarmTraps, reuse-1); - - Trap* trap = entity_list.FindNearbyTrap(this,60); - if (trap && trap->detected) - { - int uskill = GetSkill(SkillDisarmTraps); - if ((MakeRandomInt(0, 49) + uskill) >= (MakeRandomInt(0, 49) + trap->skill)) - { - Message(MT_Skills,"You disarm a trap."); - trap->disarmed = true; - trap->chkarea_timer.Disable(); - trap->respawn_timer.Start((trap->respawn_time + MakeRandomInt(0, trap->respawn_var))*1000); - } - else - { - if(MakeRandomInt(0, 99) < 25){ - Message(MT_Skills,"You set off the trap while trying to disarm it!"); - trap->Trigger(this); - } - else{ - Message(MT_Skills,"You failed to disarm a trap."); - } - } - CheckIncreaseSkill(SkillDisarmTraps, nullptr); - return; - } - Message(MT_Skills,"You did not find any traps close enough to disarm."); - return; -} - -void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - //Opens the tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if(tribmast && tribmast->IsNPC() && tribmast->GetClass()==TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } else { - st->response=0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_OpenGuildTributeMaster of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(StartTribute_Struct)) - printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - //Opens the guild tribute master window - StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; - Mob* tribmast = entity_list.GetMob(st->tribute_master_id); - if(tribmast && tribmast->IsNPC() && tribmast->GetClass()==GUILD_TRIBUTE_MASTER - && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { - st->response = 1; - QueuePacket(app); - tribute_master_id = st->tribute_master_id; - DoTributeUpdate(); - } else { - st->response=0; - QueuePacket(app); - } - } - return; -} - -void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeItem of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates an item... - if(app->size != sizeof(TributeItem_Struct)) - printf("Error in OP_TributeItem. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - TributeItem_Struct* t = (TributeItem_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if(!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if(DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeItem(t->slot, t->quantity); - - _log(TRIBUTE__OUT, "Sending tribute item reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeMoney of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //player donates money - if(app->size != sizeof(TributeMoney_Struct)) - printf("Error in OP_TributeMoney. Expected size of: %zu, but got: %i\n",sizeof(StartTribute_Struct),app->size); - else { - TributeMoney_Struct* t = (TributeMoney_Struct*)app->pBuffer; - - tribute_master_id = t->tribute_master_id; - //make sure they are dealing with a valid tribute master - Mob* tribmast = entity_list.GetMob(t->tribute_master_id); - if(!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) - return; - if(DistNoRoot(*tribmast) > USE_NPC_RANGE2) - return; - - t->tribute_points = TributeMoney(t->platinum); - - _log(TRIBUTE__OUT, "Sending tribute money reply with %d points", t->tribute_points); - _pkt(TRIBUTE__OUT, app); - - QueuePacket(app); - } - return; -} - -void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_SelectTribute of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //we should enforce being near a real tribute master to change this - //but im not sure how I wanna do that right now. - if(app->size != sizeof(SelectTributeReq_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_SelectTribute packet"); - else { - SelectTributeReq_Struct *t = (SelectTributeReq_Struct *) app->pBuffer; - SendTributeDetails(t->client_id, t->tribute_id); - } - return; -} - -void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeUpdate of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - //sent when the client changes their tribute settings... - if(app->size != sizeof(TributeInfo_Struct)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeUpdate packet"); - else { - TributeInfo_Struct *t = (TributeInfo_Struct *) app->pBuffer; - ChangeTributeSettings(t); - } - return; -} - -void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeToggle of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - if(app->size != sizeof(uint32)) - LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeToggle packet"); - else { - uint32 *val = (uint32 *) app->pBuffer; - ToggleTribute(*val? true : false); - } - return; -} - -void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) -{ - _log(TRIBUTE__IN, "Received OP_TributeNPC of length %d", app->size); - _pkt(TRIBUTE__IN, app); - - return; -} - -void Client::Handle_OP_CrashDump(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_ControlBoat(const EQApplicationPacket *app) -{ - if (app->size != sizeof(ControlBoat_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ControlBoat, size=%i, expected %i", app->size, sizeof(ControlBoat_Struct)); - return; - } - ControlBoat_Struct* cbs = (ControlBoat_Struct*)app->pBuffer; - Mob* boat = entity_list.GetMob(cbs->boatId); - if (boat == 0) - return; // do nothing if the boat isn't valid - - if(!boat->IsNPC() || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "OP_Control Boat was sent against %s which is of race %u", boat->GetName(), boat->GetRace()); - database.SetMQDetectionFlag(this->AccountName(), this->GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - if (cbs->TakeControl) { - // this uses the boat's target to indicate who has control of it. It has to check hate to make sure the boat isn't actually attacking anyone. - if ((boat->GetTarget() == 0) || (boat->GetTarget() == this && boat->GetHateAmount(this) == 0)) { - boat->SetTarget(this); - } - else { - this->Message_StringID(13,IN_USE); - return; - } - } - else - boat->SetTarget(0); - - EQApplicationPacket* outapp=new EQApplicationPacket(OP_ControlBoat,0); - FastQueuePacket(&outapp); - safe_delete(outapp); - // have the boat signal itself, so quests can be triggered by boat use - boat->CastToNPC()->SignalNPC(0); -} - -void Client::Handle_OP_DumpName(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) -{ - if(HasSkill(SkillSafeFall)) //this should only get called if the client has safe fall, but just in case... - CheckIncreaseSkill(SkillSafeFall, nullptr); //check for skill up -} - -void Client::Handle_OP_Heartbeat(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_SafePoint(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_Ignore(const EQApplicationPacket *app) -{ -} - -void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app) -{ - if(app->size != sizeof(FindPersonRequest_Struct)) - printf("Error in FindPersonRequest_Struct. Expected size of: %zu, but got: %i\n",sizeof(FindPersonRequest_Struct),app->size); - else { - FindPersonRequest_Struct* t = (FindPersonRequest_Struct*)app->pBuffer; - - std::vector points; - Mob* target = entity_list.GetMob(t->npc_id); - - if(target == nullptr) { - //empty length packet == not found. - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - if(!RuleB(Pathing, Find) && RuleB(Bazaar, EnableWarpToTrader) && target->IsClient() && (target->CastToClient()->Trader || - target->CastToClient()->Buyer)) { - Message(15, "Moving you to Trader %s", target->GetName()); - MovePC(zone->GetZoneID(), zone->GetInstanceID(), target->GetX(), target->GetY(), target->GetZ() , 0.0f); - } - - if(!RuleB(Pathing, Find) || !zone->pathing) - { - //fill in the path array... - // - points.resize(2); - points[0].x = GetX(); - points[0].y = GetY(); - points[0].z = GetZ(); - points[1].x = target->GetX(); - points[1].y = target->GetY(); - points[1].z = target->GetZ(); - } - else - { - Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION); - Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION); - - if(!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End)) - { - points.resize(2); - points[0].x = Start.x; - points[0].y = Start.y; - points[0].z = Start.z; - - points[1].x = End.x; - points[1].y = End.y; - points[1].z = End.z; - - } - else - { - std::list pathlist = zone->pathing->FindRoute(Start, End); - - if(pathlist.size() == 0) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - //the client seems to have issues with packets larger than this - if(pathlist.size() > 36) - { - EQApplicationPacket outapp(OP_FindPersonReply, 0); - QueuePacket(&outapp); - return; - } - - // Live appears to send the points in this order: - // Final destination. - // Current Position. - // rest of the points. - FindPerson_Point p; - - int PointNumber = 0; - - bool LeadsToTeleporter = false; - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back()); - - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - - p.x = GetX(); - p.y = GetY(); - p.z = GetZ(); - points.push_back(p); - - for(std::list::iterator Iterator = pathlist.begin(); Iterator != pathlist.end(); ++Iterator) - { - if((*Iterator) == -1) // Teleporter - { - LeadsToTeleporter = true; - break; - } - - Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false); - p.x = v.x; - p.y = v.y; - p.z = v.z; - points.push_back(p); - ++PointNumber; - } - - if(!LeadsToTeleporter) - { - p.x = target->GetX(); - p.y = target->GetY(); - p.z = target->GetZ(); - - points.push_back(p); - } - - } - } - - SendPathPacket(points); - } - return; -} - -void Client::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { - Entity::DBAWComplete(workpt_b1, dbaw); - switch (workpt_b1) { - case DBA_b1_Entity_Client_InfoForLogin: { - if (!FinishConnState2(dbaw)) - client_state = CLIENT_ERROR; - break; - } - case DBA_b1_Entity_Client_Save: { - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 affected_rows = 0; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if (dbaq->GetAnswer(errbuf, 0, &affected_rows) && affected_rows == 1) { - if (dbaq->QPT()) { - SaveBackup(); - } - } - else { - std::cout << "Async client save failed. '" << errbuf << "'" << std::endl; - Message(13, "Error: Asyncronous save of your character failed."); - if (Admin() >= 200) - Message(13, "errbuf: %s", errbuf); - } - pQueuedSaveWorkID = 0; - break; - } - default: { - std::cout << "Error: Client::DBAWComplete(): Unknown workpt_b1" << std::endl; - break; - } - } -} - -bool Client::FinishConnState2(DBAsyncWork* dbaw) { - uint32 pplen = 0; - DBAsyncQuery* dbaq = 0; - EQApplicationPacket* outapp = 0; - MYSQL_RES* result = 0; - bool loaditems = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 i; - - for (i=1; i<=3; i++) { - dbaq = dbaw->PopAnswer(); - if (!dbaq) { - std::cout << "Error in FinishConnState2(): dbaq==0" << std::endl; - return false; - } - if (!dbaq->GetAnswer(errbuf, &result)) { - std::cout << "Error in FinishConnState2(): !dbaq[" << dbaq->QPT() << "]->GetAnswer(): " << errbuf << std::endl; - return false; - } - if (dbaq->QPT() == 1) { - database.GetAccountInfoForLogin_result(result, 0, account_name, &lsaccountid, &gmspeed, &revoked, &gmhideme, &account_creation); - if(gmhideme) - { - trackable = false; - } - } - else if (dbaq->QPT() == 2) { - loaditems = database.GetCharacterInfoForLogin_result(result, 0, 0, &m_pp, &m_inv, &m_epp, &pplen, &guild_id, &guildrank, &class_, &level, &LFP, &LFG, &MaxXTargets, &firstlogon); - } - else if (dbaq->QPT() == 3) { - database.RemoveTempFactions(this); - database.LoadFactionValues_result(result, factionvalues); - } - else { - std::cout << "Error in FinishConnState2(): dbaq->PQT() unknown" << std::endl; - return false; - } - } - - // This should be a part of the PlayerProfile BLOB, but we don't want to modify that - // The player inspect message is retrieved from the db on load, then saved as new updates come in..no mods to Client::Save() - database.GetPlayerInspectMessage(m_pp.name, &m_inspect_message); - - conn_state = PlayerProfileLoaded; - - m_pp.zone_id = zone->GetZoneID(); - m_pp.zoneInstance = zone->GetInstanceID(); - - TotalSecondsPlayed = m_pp.timePlayedMin * 60; - - max_AAXP = RuleI(AA, ExpPerPoint); - - if(!RuleB(Character, MaintainIntoxicationAcrossZones)) - m_pp.intoxication = 0; - - //uint32 aalen = database.GetPlayerAlternateAdv(account_id, name, &aa); - //if (aalen == 0) { - // std::cout << "Client dropped: !GetPlayerAlternateAdv, name=" << name << std::endl; - // return false; - //} - - - - //////////////////////////////////////////////////////////// // Player Profile Packet - // Try to find the EQ ID for the guild, if doesnt exist, guild has been deleted. - - // Clear memory, but leave it in the DB (no reason not to, guild might be restored?) - strcpy(name, m_pp.name); - strcpy(lastname, m_pp.last_name); - if((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1)||(m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) { - m_pp.x = zone->safe_x(); - m_pp.y = zone->safe_y(); - m_pp.z = zone->safe_z(); - } - - //these now come from the database, and it is the authority. - if(class_ > 0) - m_pp.class_ = class_; - else - class_ = m_pp.class_; - if(level > 0) { - if(m_pp.level != level) { - //they changed their level in the database... not ideal, but oh well.. - m_pp.exp = GetEXPForLevel(level); - m_pp.level = level; - } - } else { - level = m_pp.level; - } - - x_pos = m_pp.x; - y_pos = m_pp.y; - z_pos = m_pp.z; - heading = m_pp.heading; - race = m_pp.race; - base_race = m_pp.race; - gender = m_pp.gender; - base_gender = m_pp.gender; - deity = m_pp.deity;//FYI: DEITY_AGNOSTIC = 396; still valid? - haircolor = m_pp.haircolor; - beardcolor = m_pp.beardcolor; - eyecolor1 = m_pp.eyecolor1; - eyecolor2 = m_pp.eyecolor2; - hairstyle = m_pp.hairstyle; - luclinface = m_pp.face; - beard = m_pp.beard; - drakkin_heritage = m_pp.drakkin_heritage; - drakkin_tattoo = m_pp.drakkin_tattoo; - drakkin_details = m_pp.drakkin_details; - - //if we zone in with invalid Z, fix it. - if (zone->zonemap != nullptr) { - - Map::Vertex me; - me.x = GetX(); - me.y = GetY(); - me.z = GetZ() + (GetSize() == 0.0 ? 6 : GetSize()); - - Map::Vertex hit; - - if (zone->zonemap->FindBestZ(me, &hit) == BEST_Z_INVALID) - { -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "Player %s started below the zone trying to fix! (%.3f, %.3f, %.3f)", GetName(), me.x, me.y, me.z); -#endif - me.z += 200; //arbitrary # - if (zone->zonemap->FindBestZ(me, &hit) != BEST_Z_INVALID) - { - //+10 so they dont stick in the ground - SendTo(me.x, me.y, hit.z + 10); - m_pp.z = hit.z + 10; - } - else - { - //one more, desperate try - me.z += 2000; - if (zone->zonemap->FindBestZ(me, &hit) != BEST_Z_INVALID) - { - //+10 so they dont stick in the ground - SendTo(me.x, me.y, hit.z + 10); - m_pp.z = hit.z + 10; - } - } - } - } - - if (m_pp.gm && admin < minStatusToBeGM) - m_pp.gm = 0; - - if (m_pp.platinum < 0 || m_pp.gold < 0 || m_pp.silver < 0 || m_pp.copper < 0 ) - { - m_pp.platinum = 0; - m_pp.gold = 0; - m_pp.silver = 0; - m_pp.copper = 0; - } - - if (!IsInAGuild()) { - m_pp.guild_id = GUILD_NONE; - } - else - { - m_pp.guild_id = GuildID(); - - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID)) - GuildBanker = (guild_mgr.IsGuildLeader(GuildID(), CharacterID()) || guild_mgr.GetBankerFlag(CharacterID())); - } - - m_pp.guildbanker = GuildBanker; - - switch (race) - { - case OGRE: - size = 9; - break; - case TROLL: - size = 8; - break; - case VAHSHIR: - case BARBARIAN: - size = 7; - break; - case HUMAN: - case HIGH_ELF: - case ERUDITE: - case IKSAR: - case DRAKKIN: - size = 6; - break; - case HALF_ELF: - size = 5.5; - break; - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - size = 5; - break; - case DWARF: - size = 4; - break; - case HALFLING: - size = 3.5; - break; - case GNOME: - size = 3; - break; - default: - size = 0; - } - - //validate skills - //im not sure I follow this logic... commenting for now... - /* - if(Admin() < minStatusToHaveInvalidSkills) { - SkillType sk; - for (sk = _1H_BLUNT; sk <= HIGHEST_SKILL; sk = (SkillType)(sk+1)) { - //int cap = GetSkillCap(sk-1); - int cap = MaxSkill(sk-1, GetClass(), GetLevel()); - if (cap >= 254) - m_pp.skills[sk] = cap; - } - } - */ - - //validate adventure points, this cap is arbitrary at 2,000,000,000 - if(m_pp.ldon_points_guk < 0) - m_pp.ldon_points_guk = 0; - if(m_pp.ldon_points_guk > 0x77359400) - m_pp.ldon_points_guk = 0x77359400; - if(m_pp.ldon_points_mir < 0) - m_pp.ldon_points_mir = 0; - if(m_pp.ldon_points_mir > 0x77359400) - m_pp.ldon_points_mir = 0x77359400; - if(m_pp.ldon_points_mmc < 0) - m_pp.ldon_points_mmc = 0; - if(m_pp.ldon_points_mmc > 0x77359400) - m_pp.ldon_points_mmc = 0x77359400; - if(m_pp.ldon_points_ruj < 0) - m_pp.ldon_points_ruj = 0; - if(m_pp.ldon_points_ruj > 0x77359400) - m_pp.ldon_points_ruj = 0x77359400; - if(m_pp.ldon_points_tak < 0) - m_pp.ldon_points_tak = 0; - if(m_pp.ldon_points_tak > 0x77359400) - m_pp.ldon_points_tak = 0x77359400; - if(m_pp.ldon_points_available < 0) - m_pp.ldon_points_available = 0; - if(m_pp.ldon_points_available > 0x77359400) - m_pp.ldon_points_available = 0x77359400; - - if(GetSkill(SkillSwimming) < 100) - SetSkill(SkillSwimming,100); - - //pull AAs from the PP - for(uint32 a=0; a < MAX_PP_AA_ARRAY; a++){ - //set up our AA pointer - aa[a] = &m_pp.aa_array[a]; - - - uint32 id = aa[a]->AA; - //watch for invalid AA IDs - if(id == aaNone) - continue; - if(id >= aaHighestID) { - aa[a]->AA = aaNone; - aa[a]->value = 0; - continue; - } - - //watch for invalid AA values - if(aa[a]->value == 0) { - aa[a]->AA = aaNone; - continue; - } - if(aa[a]->value > HIGHEST_AA_VALUE) { - aa[a]->AA = aaNone; - aa[a]->value = 0; - continue; - } - - if(aa[a]->value > 1) //hack in some stuff for sony's new AA method (where each level of each aa.has a seperate ID) - aa_points[(id - aa[a]->value +1)] = aa[a]->value; - else - aa_points[id] = aa[a]->value; - } - - - if(SPDAT_RECORDS > 0) - { - for(uint32 z=0;z= (uint32)SPDAT_RECORDS) - UnmemSpell(z, false); - } - - database.LoadBuffs(this); - uint32 max_slots = GetMaxBuffSlots(); - for(int i = 0; i < max_slots; i++) { - if(buffs[i].spellid != SPELL_UNKNOWN) { - m_pp.buffs[i].spellid = buffs[i].spellid; - m_pp.buffs[i].bard_modifier = 10; - m_pp.buffs[i].slotid = 2; - m_pp.buffs[i].player_id = 0x2211; - m_pp.buffs[i].level = buffs[i].casterlevel; - m_pp.buffs[i].effect = 0; - m_pp.buffs[i].duration = buffs[i].ticsremaining; - m_pp.buffs[i].counters = buffs[i].counters; - } else { - m_pp.buffs[i].spellid = SPELLBOOK_UNKNOWN; - m_pp.buffs[i].bard_modifier = 10; - m_pp.buffs[i].slotid = 0; - m_pp.buffs[i].player_id = 0; - m_pp.buffs[i].level = 0; - m_pp.buffs[i].effect = 0; - m_pp.buffs[i].duration = 0; - m_pp.buffs[i].counters = 0; - } - } - } - - KeyRingLoad(); - - uint32 groupid = database.GetGroupID(GetName()); - Group* group = nullptr; - if(groupid > 0){ - group = entity_list.GetGroupByID(groupid); - if(!group) { //nobody from our is here... start a new group - group = new Group(groupid); - if(group->GetID() != 0) - entity_list.AddGroup(group, groupid); - else //error loading group members... - { - delete group; - group = nullptr; - } - } //else, somebody from our group is already here... - - if(group) - group->UpdatePlayer(this); - else - database.SetGroupID(GetName(), 0, CharacterID()); //cannot re-establish group, kill it - - } else { //no group id - //clear out the group junk in our PP - uint32 xy=0; - for(xy=0;xy < MAX_GROUP_MEMBERS;xy++) - memset(m_pp.groupMembers[xy], 0, 64); - } - - if(group){ - // If the group leader is not set, pull the group leader infomrmation from the database. - if(!group->GetLeader()){ - char ln[64]; - char MainTankName[64]; - char AssistName[64]; - char PullerName[64]; - char NPCMarkerName[64]; - GroupLeadershipAA_Struct GLAA; - memset(ln, 0, 64); - strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA)); - Client *c = entity_list.GetClientByName(ln); - if(c) - group->SetLeader(c); - - group->SetMainTank(MainTankName); - group->SetMainAssist(AssistName); - group->SetPuller(PullerName); - group->SetNPCMarker(NPCMarkerName); - group->SetGroupAAs(&GLAA); - - //group->NotifyMainTank(this, 1); - //group->NotifyMainAssist(this, 1); - //group->NotifyPuller(this, 1); - - // If we are the leader, force an update of our group AAs to other members in the zone, in case - // we purchased a new one while out-of-zone. - if(group->IsLeader(this)) - group->SendLeadershipAAUpdate(); - - } - LFG = false; - } - -#ifdef BOTS - Bot::LoadAndSpawnAllZonedBots(this); -#endif - - CalcBonuses(); - if (m_pp.cur_hp <= 0) - m_pp.cur_hp = GetMaxHP(); - - SetHP(m_pp.cur_hp); - Mob::SetMana(m_pp.mana); - SetEndurance(m_pp.endurance); - - if(IsLFP()) { - // Update LFP in case any (or all) of our group disbanded while we were zoning. - UpdateLFP(); - } - - if(m_pp.z <= zone->newzone_data.underworld) { - m_pp.x = zone->newzone_data.safe_x; - m_pp.y = zone->newzone_data.safe_y; - m_pp.z = zone->newzone_data.safe_z; - } - - char val[20] = {0}; - if (database.GetVariable("Expansions", val, 20)) - m_pp.expansions = atoi(val); - else - m_pp.expansions = 0x3FF; - - p_timers.SetCharID(CharacterID()); - if(!p_timers.Load(&database)) { - LogFile->write(EQEMuLog::Error, "Unable to load ability timers from the database for %s (%i)!", GetCleanName(), CharacterID()); - } - - for(unsigned int i =0 ; i < MAX_PP_MEMSPELL; ++i) - if(IsValidSpell(m_pp.mem_spells[i])) - m_pp.spellSlotRefresh[i] = p_timers.GetRemainingTime(pTimerSpellStart + m_pp.mem_spells[i]) * 1000; - - if(m_pp.class_==SHADOWKNIGHT || m_pp.class_==PALADIN) - { - uint32 abilitynum=0; - if(m_pp.class_==SHADOWKNIGHT) - abilitynum = pTimerHarmTouch; - else - abilitynum = pTimerLayHands; - - - uint32 remaining = p_timers.GetRemainingTime(abilitynum); - if(remaining > 0 && remaining < 15300) - m_pp.abilitySlotRefresh = remaining * 1000; - else - m_pp.abilitySlotRefresh = 0; - } - -#ifdef _EQDEBUG - printf("Dumping inventory on load:\n"); - m_inv.dumpEntireInventory(); -#endif - -//lost in current PP -// strcpy(m_pp.servername,"eqemulator"); - - m_pp.air_remaining = 60; //Reset to max so they dont drown on zone in if its underwater - - if(zone->IsPVPZone()) - m_pp.pvp=1; - - m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440; - - // Reset rest timer if the durations have been lowered in the database - if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate))) - m_pp.RestTimer = 0; - - //This checksum should disappear once dynamic structs are in... each struct strategy will do it - CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4); - - outapp = new EQApplicationPacket(OP_PlayerProfile,sizeof(PlayerProfile_Struct)); - - // The entityid field in the Player Profile is used by the Client in relation to Group Leadership AA - m_pp.entityid = GetID(); - memcpy(outapp->pBuffer,&m_pp,outapp->size); - outapp->priority = 6; - FastQueuePacket(&outapp); - - if(m_pp.RestTimer) - rest_timer.Start(m_pp.RestTimer * 1000); - - database.LoadPetInfo(this); - //this was moved before the spawn packets are sent - //in hopes that it adds more consistency... - //Remake pet - if (m_petinfo.SpellID > 1 && !GetPet() && m_petinfo.SpellID <= SPDAT_RECORDS) - { - MakePoweredPet(m_petinfo.SpellID, spells[m_petinfo.SpellID].teleport_zone, m_petinfo.petpower, m_petinfo.Name, m_petinfo.size); - if (GetPet() && GetPet()->IsNPC()) { - NPC *pet = GetPet()->CastToNPC(); - pet->SetPetState(m_petinfo.Buffs, m_petinfo.Items); - pet->CalcBonuses(); - pet->SetHP(m_petinfo.HP); - pet->SetMana(m_petinfo.Mana); - } - m_petinfo.SpellID = 0; - } - // Moved here so it's after where we load the pet data. - if(!GetAA(aaPersistentMinion)) - memset(&m_suspendedminion, 0, sizeof(PetInfo)); - - /* Server Zone Entry Packet */ - outapp = new EQApplicationPacket(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct)); - ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer; - - FillSpawnStruct(&sze->player,CastToMob()); - sze->player.spawn.curHp=1; - sze->player.spawn.NPC=0; - sze->player.spawn.z += 6; //arbitrary lift, seems to help spawning under zone. - outapp->priority = 6; - FastQueuePacket(&outapp); - - /* Zone Spawns Packet */ - entity_list.SendZoneSpawnsBulk(this); - entity_list.SendZoneCorpsesBulk(this); - entity_list.SendZonePVPUpdates(this); //hack until spawn struct is fixed. - - /* Time of Day packet */ - outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); - TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.getEQTimeOfDay(time(0), tod); - outapp->priority = 6; - FastQueuePacket(&outapp); - - /* Tribute Packets */ - DoTributeUpdate(); - if(m_pp.tribute_active) { - //restart the tribute timer where we left off - tribute_timer.Start(m_pp.tribute_time_remaining); - } - - /* - Character Inventory Packet - this is not quite where live sends inventory, they do it after tribute - */ - if (loaditems) { //dont load if a length error occurs - BulkSendInventoryItems(); - - // Send stuff on the cursor which isnt sent in bulk - iter_queue it; - for (it=m_inv.cursor_begin();it!=m_inv.cursor_end();++it) { - // First item cursor is sent in bulk inventory packet - if (it==m_inv.cursor_begin()) - continue; - const ItemInst *inst=*it; - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - } - } - - /* Task Packets */ - LoadClientTaskState(); - - if (GetClientVersion() >= EQClientRoF) - { - outapp = new EQApplicationPacket(OP_ReqNewZone, 0); - Handle_Connect_OP_ReqNewZone(outapp); - safe_delete(outapp); - } - - if(ClientVersionBit & BIT_UnderfootAndLater) - { - outapp = new EQApplicationPacket(OP_XTargetResponse, 8); - outapp->WriteUInt32(GetMaxXTargets()); - outapp->WriteUInt32(0); - FastQueuePacket(&outapp); - } - - /* - Weather Packet - This shouldent be moved, this seems to be what the client - uses to advance to the next state (sending ReqNewZone) - */ - outapp = new EQApplicationPacket(OP_Weather, 12); - Weather_Struct *ws = (Weather_Struct *) outapp->pBuffer; - ws->val1 = 0x000000FF; - if (zone->zone_weather == 1) - ws->type = 0x31; // Rain - if (zone->zone_weather == 2) - { - outapp->pBuffer[8] = 0x01; - ws->type = 0x02; - } - outapp->priority = 6; - QueuePacket(outapp); - safe_delete(outapp); - - SetAttackTimer(); - - conn_state = ZoneInfoSent; - - return true; -} - -/* Finish client connecting state */ -void Client::CompleteConnect() { - UpdateWho(); - client_state = CLIENT_CONNECTED; - - hpupdate_timer.Start(); - position_timer.Start(); - autosave_timer.Start(); - SetDuelTarget(0); - SetDueling(false); - - EnteringMessages(this); - LoadZoneFlags(); - - /* Sets GM Flag if needed & Sends Petition Queue */ - UpdateAdmin(false); - - if (IsInAGuild()){ - uint8 rank = GuildRank(); - if(GetClientVersion() >= EQClientRoF) - { - 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 - default: { break; } // GUILD_NONE - } - } - SendAppearancePacket(AT_GuildID, GuildID(), false); - SendAppearancePacket(AT_GuildRank, rank, false); - } - for (uint32 spellInt = 0; spellInt < MAX_PP_SPELLBOOK; spellInt++) - { - if (m_pp.spell_book[spellInt] < 3 || m_pp.spell_book[spellInt] > 50000) - m_pp.spell_book[spellInt] = 0xFFFFFFFF; - } - //SendAATable(); - - if (GetHideMe()) Message(13, "[GM] You are currently hidden to all clients"); - - uint32 raidid = database.GetRaidID(GetName()); - Raid *raid = nullptr; - if (raidid > 0){ - raid = entity_list.GetRaidByID(raidid); - if (!raid){ - raid = new Raid(raidid); - if (raid->GetID() != 0){ - entity_list.AddRaid(raid, raidid); - } - else - raid = nullptr; - } - if (raid){ - SetRaidGrouped(true); - raid->LearnMembers(); - raid->VerifyRaid(); - raid->GetRaidDetails(); - /* - Only leader should get this; send to all for now till - I figure out correct creation; can probably also send a no longer leader packet for non leaders - but not important for now. - */ - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->SendRaidAdd(GetName(), this); - raid->SendBulkRaid(this); - raid->SendGroupUpdate(this); - uint32 grpID = raid->GetGroup(GetName()); - if (grpID < 12){ - raid->SendRaidGroupRemove(GetName(), grpID); - raid->SendRaidGroupAdd(GetName(), grpID); - } - if (raid->IsLocked()) - raid->SendRaidLockTo(this); - } - } - - //bulk raid send in here eventually - - //reapply some buffs - uint32 buff_count = GetMaxTotalSlots(); - for (uint32 j1 = 0; j1 < buff_count; j1++) { - 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: - case SE_Illusion: { - if (spell.base[x1] == -1) { - if (gender == 1) - gender = 0; - else if (gender == 0) - gender = 1; - SendIllusionPacket(GetRace(), gender, 0xFF, 0xFF); - } - else if (spell.base[x1] == -2) - { - if (GetRace() == 128 || GetRace() == 130 || GetRace() <= 12) - SendIllusionPacket(GetRace(), GetGender(), spell.max[x1], spell.max[x1]); - } - else if (spell.max[x1] > 0) - { - SendIllusionPacket(spell.base[x1], 0xFF, spell.max[x1], spell.max[x1]); - } - else - { - SendIllusionPacket(spell.base[x1], 0xFF, 0xFF, 0xFF); - } - switch (spell.base[x1]){ - case OGRE: - SendAppearancePacket(AT_Size, 9); - break; - case TROLL: - SendAppearancePacket(AT_Size, 8); - break; - case VAHSHIR: - case BARBARIAN: - SendAppearancePacket(AT_Size, 7); - break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - SendAppearancePacket(AT_Size, 5); - break; - case DWARF: - SendAppearancePacket(AT_Size, 4); - break; - case HALFLING: - case GNOME: - SendAppearancePacket(AT_Size, 3); - break; - default: - SendAppearancePacket(AT_Size, 6); - break; - } - break; - } - case SE_SummonHorse: { - SummonHorse(buffs[j1].spellid); - //hasmount = true; //this was false, is that the correct thing? - break; - } - case SE_Silence: - { - Silence(true); - break; - } - case SE_Amnesia: - { - Amnesia(true); - break; - } - case SE_DivineAura: - { - invulnerable = true; - break; - } - case SE_Invisibility2: - case SE_Invisibility: - { - invisible = true; - SendAppearancePacket(AT_Invis, 1); - break; - } - case SE_Levitate: - { - if (!zone->CanLevitate()) - { - if (!GetGM()) - { - SendAppearancePacket(AT_Levitate, 0); - BuffFadeByEffect(SE_Levitate); - Message(13, "You can't levitate in this zone."); - } - } - else{ - SendAppearancePacket(AT_Levitate, 2); - } - break; - } - case SE_InvisVsUndead2: - case SE_InvisVsUndead: - { - invisible_undead = true; - break; - } - case SE_InvisVsAnimals: - { - invisible_animals = true; - break; - } - case SE_AddMeleeProc: - case SE_WeaponProc: - { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_DefensiveProc: - { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - case SE_RangedProc: - { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].base2[x1], buffs[j1].spellid); - break; - } - } - } - } - - /* Sends appearances for all mobs not doing anim_stand aka sitting, looting, playing dead */ - entity_list.SendZoneAppearance(this); - - /* Sends the Nimbus particle effects (up to 3) for any mob using them */ - entity_list.SendNimbusEffects(this); - - entity_list.SendUntargetable(this); - - client_data_loaded = true; - int x; - for (x = 0; x < 8; x++) - SendWearChange(x); - Mob *pet = GetPet(); - if (pet != nullptr) { - for (x = 0; x < 8; x++) - pet->SendWearChange(x); - } - - entity_list.SendTraders(this); - - zoneinpacket_timer.Start(); - - if (GetPet()){ - GetPet()->SendPetBuffsToClient(); - } - - if (GetGroup()) - database.RefreshGroupFromDB(this); - - if (RuleB(TaskSystem, EnableTaskSystem)) - TaskPeriodic_Timer.Start(); - else - TaskPeriodic_Timer.Disable(); - - conn_state = ClientConnectFinished; - - //enforce some rules.. - if (!CanBeInZone()) { - _log(CLIENT__ERROR, "Kicking char from zone, not allowed here"); - GoToSafeCoords(database.GetZoneID("arena"), 0); - return; - } - - if (zone) - zone->weatherSend(); - - TotalKarma = database.GetKarma(AccountID()); - SendDisciplineTimers(); - - parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0); - - /* This sub event is for if a player logs in for the first time since entering world. */ - if (firstlogon == 1){ - parse->EventPlayer(EVENT_CONNECT, this, "", 0); - /* QS: PlayerLogConnectDisconnect */ - if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ - std::string event_desc = StringFormat("Connect :: Logged into zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); - } - } - - if(zone) { - if(zone->GetInstanceTimer()) { - uint32 ttime = zone->GetInstanceTimer()->GetRemainingTime(); - uint32 day = (ttime/86400000); - uint32 hour = (ttime/3600000)%24; - uint32 minute = (ttime/60000)%60; - uint32 second = (ttime/1000)%60; - if(day) { - Message(15, "%s(%u) will expire in %u days, %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), day, hour, minute, second); - } - else if(hour) { - Message(15, "%s(%u) will expire in %u hours, %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), hour, minute, second); - } - else if(minute) { - Message(15, "%s(%u) will expire in %u minutes, and %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), minute, second); - } - else { - Message(15, "%s(%u) will expire in in %u seconds.", - zone->GetLongName(), zone->GetInstanceID(), second); - } - } - } - - SendRewards(); - SendAltCurrencies(); - database.LoadAltCurrencyValues(CharacterID(), alternate_currency); - SendAlternateCurrencyValues(); - alternate_currency_loaded = true; - ProcessAlternateCurrencyQueue(); - - CalcItemScale(); - DoItemEnterZone(); - - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) - GuildBanks->SendGuildBank(this); - - if(GetClientVersion() >= EQClientSoD) - entity_list.SendFindableNPCList(this); - - if(IsInAGuild()) { - SendGuildRanks(); - guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), zone->GetZoneID(), time(nullptr)); - guild_mgr.RequestOnlineGuildMembers(this->CharacterID(), this->GuildID()); - } - - /** Request adventure info **/ - ServerPacket *pack = new ServerPacket(ServerOP_AdventureDataRequest, 64); - strcpy((char*)pack->pBuffer, GetName()); - worldserver.SendPacket(pack); - delete pack; - - if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { - EQApplicationPacket *outapp = MakeBuffsPacket(false); - CastToClient()->FastQueuePacket(&outapp); - } - - entity_list.RefreshClientXTargets(this); -} - -void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) -{ - KeyRingList(); -} - -void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) { - if(app->size != 1) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint8 *mode = (uint8 *) app->pBuffer; - if(*mode) { - m_pp.leadAAActive = 1; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_ON); - } else { - m_pp.leadAAActive = 0; - Save(); - Message_StringID(clientMessageYellow, LEADERSHIP_EXP_OFF); - } -} - - -void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) { - if(app->size != sizeof(uint32)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); - DumpPacket(app); - return; - } - uint32 aaid = *((uint32 *) app->pBuffer); - - if(aaid >= _maxLeaderAA) - return; - - uint32 current_rank = m_pp.leader_abilities.ranks[aaid]; - if(current_rank >= MAX_LEADERSHIP_TIERS) { - Message(13, "This ability can be trained no further."); - return; - } - - uint8 cost = LeadershipAACosts[aaid][current_rank]; - if(cost == 0) { - Message(13, "This ability can be trained no further."); - return; - } - - //TODO: we need to enforce prerequisits - - if(aaid >= raidAAMarkNPC) { - //it is a raid ability. - if(cost > m_pp.raid_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.raid_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - } else { - //it is a group ability. - if(cost > m_pp.group_leadership_points) { - Message(13, "You do not have enough points to purchase this ability."); - return; - } - - //sell them the ability. - m_pp.group_leadership_points -= cost; - m_pp.leader_abilities.ranks[aaid]++; - } - - //success, send them an update - EQApplicationPacket *outapp = new EQApplicationPacket(OP_UpdateLeadershipAA, sizeof(UpdateLeadershipAA_Struct)); - UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *) outapp->pBuffer; - u->ability_id = aaid; - u->new_rank = m_pp.leader_abilities.ranks[aaid]; - u->pointsleft = m_pp.group_leadership_points; // FIXME: Take into account raid abilities - FastQueuePacket(&outapp); - - Group *g = GetGroup(); - - // Update all group members with the new AA the leader has purchased. - if(g) { - g->UpdateGroupAAs(); - g->SendLeadershipAAUpdate(); - } - -} - -void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) -{ - if(app->size != sizeof(SetTitle_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); - DumpPacket(app); - return; - } - - SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; - - std::string Title; - - if(!sts->is_suffix) - { - Title = title_manager.GetPrefix(sts->title_id); - SetAATitle(Title.c_str()); - } - else - { - Title = title_manager.GetSuffix(sts->title_id); - SetTitleSuffix(Title.c_str()); - } -} - -void Client::Handle_OP_RequestTitles(const EQApplicationPacket *app) -{ - - EQApplicationPacket *outapp = title_manager.MakeTitlesPacket(this); - - if(outapp != nullptr) - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_BankerChange(const EQApplicationPacket *app) -{ - if(app->size != sizeof(BankerChange_Struct) && app->size!=4) //Titanium only sends 4 Bytes for this - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BankerChange expected %i got %i", sizeof(BankerChange_Struct), app->size); - DumpPacket(app); - return; - } - - uint32 distance = 0; - NPC *banker = entity_list.GetClosestBanker(this, distance); - - if(!banker || distance > USE_NPC_RANGE2) - { - char *hacked_string = nullptr; - MakeAnyLenString(&hacked_string, "Player tried to make use of a banker(money) but %s is non-existant or too far away (%u units).", - banker ? banker->GetName() : "UNKNOWN NPC", distance); - database.SetMQDetectionFlag(AccountName(), GetName(), hacked_string, zone->GetShortName()); - safe_delete_array(hacked_string); - return; - } - - EQApplicationPacket *outapp=new EQApplicationPacket(OP_BankerChange,nullptr,sizeof(BankerChange_Struct)); - BankerChange_Struct *bc=(BankerChange_Struct *)outapp->pBuffer; - - if(m_pp.platinum < 0) - m_pp.platinum = 0; - if(m_pp.gold < 0) - m_pp.gold = 0; - if(m_pp.silver < 0) - m_pp.silver = 0; - if(m_pp.copper < 0) - m_pp.copper = 0; - - if(m_pp.platinum_bank < 0) - m_pp.platinum_bank = 0; - if(m_pp.gold_bank < 0) - m_pp.gold_bank = 0; - if(m_pp.silver_bank < 0) - m_pp.silver_bank = 0; - if(m_pp.copper_bank < 0) - m_pp.copper_bank = 0; - - uint64 cp = static_cast(m_pp.copper) + - (static_cast(m_pp.silver) * 10) + - (static_cast(m_pp.gold) * 100) + - (static_cast(m_pp.platinum) * 1000); - - m_pp.copper=cp%10; - cp/=10; - m_pp.silver=cp%10; - cp/=10; - m_pp.gold=cp%10; - cp/=10; - m_pp.platinum=cp; - - cp = static_cast(m_pp.copper_bank) + - (static_cast(m_pp.silver_bank) * 10) + - (static_cast(m_pp.gold_bank) * 100) + - (static_cast(m_pp.platinum_bank) * 1000); - - m_pp.copper_bank=cp%10; - cp/=10; - m_pp.silver_bank=cp%10; - cp/=10; - m_pp.gold_bank=cp%10; - cp/=10; - m_pp.platinum_bank=cp; - - bc->copper=m_pp.copper; - bc->silver=m_pp.silver; - bc->gold=m_pp.gold; - bc->platinum=m_pp.platinum; - - bc->copper_bank=m_pp.copper_bank; - bc->silver_bank=m_pp.silver_bank; - bc->gold_bank=m_pp.gold_bank; - bc->platinum_bank=m_pp.platinum_bank; - - FastQueuePacket(&outapp); - - return; -} - -void Client::Handle_OP_AutoFire(const EQApplicationPacket *app) -{ - if(app->size != sizeof(bool)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AutoFire expected %i got %i", sizeof(bool), app->size); - DumpPacket(app); - return; - } - bool *af = (bool*)app->pBuffer; - auto_fire = *af; - auto_attack = false; - SetAttackTimer(); -} -void Client::Handle_OP_Rewind(const EQApplicationPacket *app) -{ - if ((rewind_timer.GetRemainingTime() > 1 && rewind_timer.Enabled())) { - Message_StringID(MT_System, REWIND_WAIT); - } else { - CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), rewind_x, rewind_y, rewind_z, 0, 2, Rewind); - rewind_timer.Start(30000, true); - } -} - -void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) -{ - if (app->size != sizeof(RaidGeneral_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected %i", app->size, sizeof(RaidGeneral_Struct)); - DumpPacket(app); - return; - } - - RaidGeneral_Struct *ri = (RaidGeneral_Struct*)app->pBuffer; - switch(ri->action) - { - case RaidCommandInviteIntoExisting: - case RaidCommandInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if(i){ - Group *g = i->GetGroup(); - if(g){ - if(g->IsLeader(i) == false) - Message(13, "You can only invite an ungrouped player or group leader to join your raid."); - else{ - //This sends an "invite" to the client in question. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(rg->leader_name, ri->leader_name, 64); - strn0cpy(rg->player_name, ri->player_name, 64); - - rg->parameter = 0; - rg->action = 20; - i->QueuePacket(outapp); - safe_delete(outapp); - } - } - else{ - //This sends an "invite" to the client in question. - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; - strn0cpy(rg->leader_name, ri->leader_name, 64); - strn0cpy(rg->player_name, ri->player_name, 64); - - rg->parameter = 0; - rg->action = 20; - i->QueuePacket(outapp); - safe_delete(outapp); - } - } - break; - } - case RaidCommandAcceptInvite: { - Client *i = entity_list.GetClientByName(ri->player_name); - if(i){ - if(IsRaidGrouped()){ - i->Message_StringID(0, 5060); //group failed, must invite members not in raid... - return; - } - Raid *r = entity_list.GetRaidByClient(i); - if(r){ - r->VerifyRaid(); - Group *g = GetGroup(); - if(g){ - if(g->GroupCount()+r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, group invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - else{ - if(1+r->RaidCount() > MAX_RAID_MEMBERS) - { - i->Message(13, "Invite failed, member invite would create a raid larger than the maximum number of members allowed."); - return; - } - } - if(g){//add us all - uint32 freeGroup = r->GetFreeGroup(); - Client *addClient = nullptr; - for(int x = 0; x < 6; x++) - { - if(g->members[x]){ - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - - if(!addClient) - { - addClient = c; - r->SetGroupLeader(addClient->GetName()); - } - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - if(g->IsLeader(g->members[x])) - r->AddMember(c, freeGroup, false, true); - else - r->AddMember(c, freeGroup); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - g->DisbandGroup(); - r->GroupUpdate(freeGroup); - } - else{ - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(this); - r->SendBulkRaid(this); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - else - { - Group *ig = i->GetGroup(); - Group *g = GetGroup(); - if(g) //if our target has a group - { - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - - uint32 groupFree = r->GetFreeGroup(); //get a free group - if(ig){ //if we already have a group then cycle through adding us... - Client *addClientig = nullptr; - for(int x = 0; x < 6; x++) - { - if(ig->members[x]){ - if(!addClientig){ - if(ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if(ig->IsLeader(ig->members[x])){ - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, true, true, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else{ - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - ig->DisbandGroup(); - r->GroupUpdate(groupFree); - groupFree = r->GetFreeGroup(); - } - else{ //else just add the inviter - r->SendRaidCreate(i); - r->AddMember(i,0xFFFFFFFF, true, false, true); - } - - Client *addClient = nullptr; - //now add the existing group - for(int x = 0; x < 6; x++) - { - if(g->members[x]){ - if(!addClient) - { - if(g->members[x]->IsClient()){ - addClient = g->members[x]->CastToClient(); - r->SetGroupLeader(addClient->GetName()); - } - } - if(g->IsLeader(g->members[x])) - { - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree, false, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if(g->members[x]->IsClient()) - c = g->members[x]->CastToClient(); - else - continue; - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, groupFree); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - g->DisbandGroup(); - r->GroupUpdate(groupFree); - } - else - { - if(ig){ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - Client *addClientig = nullptr; - for(int x = 0; x < 6; x++) - { - if(ig->members[x]) - { - if(!addClientig){ - if(ig->members[x]->IsClient()){ - addClientig = ig->members[x]->CastToClient(); - r->SetGroupLeader(addClientig->GetName()); - } - } - if(ig->IsLeader(ig->members[x])) - { - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0, true, true, true); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - else - { - Client *c = nullptr; - if(ig->members[x]->IsClient()) - c = ig->members[x]->CastToClient(); - else - continue; - - r->SendRaidCreate(c); - r->SendMakeLeaderPacketTo(r->leadername, c); - r->AddMember(c, 0); - r->SendBulkRaid(c); - if(r->IsLocked()) { - r->SendRaidLockTo(c); - } - } - } - } - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->SendBulkRaid(this); - r->AddMember(this); - ig->DisbandGroup(); - r->GroupUpdate(0); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - else{ - r = new Raid(i); - entity_list.AddRaid(r); - r->SetRaidDetails(); - r->SendRaidCreate(i); - r->SendRaidCreate(this); - r->SendMakeLeaderPacketTo(r->leadername, this); - r->AddMember(i,0xFFFFFFFF, true, false, true); - r->SendBulkRaid(this); - r->AddMember(this); - if(r->IsLocked()) { - r->SendRaidLockTo(this); - } - } - } - } - } - break; - } - case RaidCommandDisband: { - Raid *r = entity_list.GetRaidByClient(this); - if(r){ - //if(this == r->GetLeader()){ - uint32 grp = r->GetGroup(ri->leader_name); - - if(grp < 12){ - uint32 i = r->GetPlayerIndex(ri->leader_name); - if(r->members[i].IsGroupLeader){ //assign group leader to someone else - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(strlen(r->members[x].membername) > 0 && i != x){ - if(r->members[x].GroupNumber == grp){ - r->SetGroupLeader(ri->leader_name, false); - r->SetGroupLeader(r->members[x].membername); - break; - } - } - } - - } - if(r->members[i].IsRaidLeader){ - for(int x = 0; x < MAX_RAID_MEMBERS; x++){ - if(strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) - { - r->SetRaidLeader(r->members[i].membername, r->members[x].membername); - break; - } - } - } - } - - r->RemoveMember(ri->leader_name); - Client *c = entity_list.GetClientByName(ri->leader_name); - if(c) - r->SendGroupDisband(c); - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, grp); - r->GroupUpdate(grp);// break - //} - } - break; - } - case RaidCommandMoveGroup: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(ri->parameter < 12) //moving to a group - { - uint8 grpcount = r->GroupCount(ri->parameter); - - if(grpcount < 6) - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if(ri->parameter == oldgrp) //don't rejoin grp if we order to join same group. - break; - - if(r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader) - { - r->SetGroupLeader(ri->leader_name, false); - if(oldgrp < 12){ //we were the leader of our old grp - for(int x = 0; x < MAX_RAID_MEMBERS; x++) //assign a new grp leader if we can - { - if(r->members[x].GroupNumber == oldgrp) - { - if(strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) - { - r->SetGroupLeader(r->members[x].membername); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if(cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if(r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername, r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - } - } - if(grpcount == 0) - r->SetGroupLeader(ri->leader_name); - - r->MoveMember(ri->leader_name, ri->parameter); - if(c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupAdd(ri->leader_name, ri->parameter); - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - //r->SendGroupUpdate(c); - //break - r->GroupUpdate(ri->parameter); //send group update to our new group - if(oldgrp < 12) //if our old was a group send update there too - r->GroupUpdate(oldgrp); - - //r->SendMakeGroupLeaderPacketAll(); - } - } - else //moving to ungrouped - { - Client *c = entity_list.GetClientByName(ri->leader_name); - uint32 oldgrp = r->GetGroup(ri->leader_name); - if(r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader){ - r->SetGroupLeader(ri->leader_name, false); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if(strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) - { - r->SetGroupLeader(r->members[x].membername); - Client *cgl = entity_list.GetClientByName(r->members[x].membername); - if(cgl){ - r->SendRaidRemove(r->members[x].membername, cgl); - r->SendRaidCreate(cgl); - r->SendMakeLeaderPacketTo(r->leadername, cgl); - r->SendRaidAdd(r->members[x].membername, cgl); - r->SendBulkRaid(cgl); - if(r->IsLocked()) { - r->SendRaidLockTo(cgl); - } - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - strn0cpy(rga->playername,r->members[x].membername, 64); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - break; - } - } - } - r->MoveMember(ri->leader_name, 0xFFFFFFFF); - if(c){ - r->SendGroupDisband(c); - } - else{ - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); - ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; - rga->rid = r->GetID(); - rga->zoneid = zone->GetZoneID(); - rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, ri->leader_name, 64); - worldserver.SendPacket(pack); - safe_delete(pack); - } - //r->SendRaidGroupRemove(ri->leader_name, oldgrp); - r->GroupUpdate(oldgrp); - //r->SendMakeGroupLeaderPacketAll(); - } - } - break; - } - case RaidCommandRaidLock: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(!r->IsLocked()) - r->LockRaid(true); - else - r->SendRaidLockTo(this); - } - break; - } - case RaidCommandRaidUnlock: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(r->IsLocked()) - r->LockRaid(false); - else - r->SendRaidUnlockTo(this); - } - break; - } - case RaidCommandLootType2: - case RaidCommandLootType: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Loot type changed to: %d.", ri->parameter); - r->ChangeLootType(ri->parameter); - } - break; - } - - case RaidCommandAddLooter2: - case RaidCommandAddLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Adding %s as a raid looter.", ri->leader_name); - r->AddRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandRemoveLooter2: - case RaidCommandRemoveLooter: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - Message(15, "Removing %s as a raid looter.", ri->leader_name); - r->RemoveRaidLooter(ri->leader_name); - } - break; - } - - case RaidCommandMakeLeader: - { - Raid *r = entity_list.GetRaidByClient(this); - if(r) - { - if(strcmp(r->leadername, GetName()) == 0){ - r->SetRaidLeader(GetName(), ri->leader_name); - } - } - break; - } - - default: { - Message(13, "Raid command (%d) NYI", ri->action); - break; - } - } -} - -void Client::Handle_OP_Translocate(const EQApplicationPacket *app) { - - if(app->size != sizeof(Translocate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); - DumpPacket(app); - return; - } - Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; - - if(!PendingTranslocate) return; - - if((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { - Message(13, "You did not accept the Translocate within the required time limit."); - PendingTranslocate = false; - return; - } - - if(its->Complete == 1) { - - int SpellID = PendingTranslocateData.SpellID; - int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); - - if(i == 0) - { - // If the spell has a translocate to bind effect, AND we are already in the zone the client - // is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself - // to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are - // reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before. - if (((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) && - zone->GetZoneID() == PendingTranslocateData.ZoneID) - { - PendingTranslocate = false; - GoToBind(); - return; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); - Translocate_Struct *ots = (Translocate_Struct*)outapp->pBuffer; - memcpy(ots, &PendingTranslocateData, sizeof(Translocate_Struct)); - - //Was sending the packet back to initiate client zone... - //but that could be abusable, so lets go through proper channels - MovePC(ots->ZoneID, 0, ots->x, ots->y, ots->z, GetHeading(), 0, ZoneSolicited); - } - } - - PendingTranslocate = false; -} - -void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) { - - if(app->size != sizeof(Sacrifice_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); - DumpPacket(app); - return; - } - Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; - - if(!PendingSacrifice) { - LogFile->write(EQEMuLog::Error, "Unexpected OP_Sacrifice reply"); - DumpPacket(app); - return; - } - - if(ss->Confirm) { - Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); - if(Caster) Sacrifice(Caster); - } - PendingSacrifice = false; - SacrificeCaster.clear(); -} - -void Client::Handle_OP_AcceptNewTask(const EQApplicationPacket *app) { - - if(app->size != sizeof(AcceptNewTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AcceptNewTask expected %i got %i", - sizeof(AcceptNewTask_Struct), app->size); - DumpPacket(app); - return; - } - AcceptNewTask_Struct *ant = (AcceptNewTask_Struct*)app->pBuffer; - - if(ant->task_id > 0 && RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->AcceptNewTask(this, ant->task_id, ant->task_master_id); -} - -void Client::Handle_OP_CancelTask(const EQApplicationPacket *app) { - - if(app->size != sizeof(CancelTask_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_CancelTask expected %i got %i", - sizeof(CancelTask_Struct), app->size); - DumpPacket(app); - return; - } - CancelTask_Struct *cts = (CancelTask_Struct*)app->pBuffer; - - if(RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->CancelTask(this, cts->SequenceNumber); -} - -void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) { - - if(app->size != sizeof(TaskHistoryRequest_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", - sizeof(TaskHistoryRequest_Struct), app->size); - DumpPacket(app); - return; - } - TaskHistoryRequest_Struct *ths = (TaskHistoryRequest_Struct*)app->pBuffer; - - if(RuleB(TaskSystem, EnableTaskSystem) && taskstate) - taskstate->SendTaskHistory(this, ths->TaskIndex); -} - -void Client::Handle_OP_Bandolier(const EQApplicationPacket *app) { - - // Although there are three different structs for OP_Bandolier, they are all the same size. - // - if(app->size != sizeof(BandolierCreate_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Bandolier expected %i got %i", - sizeof(BandolierCreate_Struct), app->size); - DumpPacket(app); - return; - } - - BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer; - - switch(bs->action) { - case BandolierCreate: - CreateBandolier(app); - break; - case BandolierRemove: - RemoveBandolier(app); - break; - case BandolierSet: - SetBandolier(app); - break; - default: - LogFile->write(EQEMuLog::Debug, "Uknown Bandolier action %i", bs->action); - - } -} - -void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) { - - if(app->size != sizeof(PopupResponse_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PopupResponse expected %i got %i", - sizeof(PopupResponse_Struct), app->size); - DumpPacket(app); - return; - } - PopupResponse_Struct *prs = (PopupResponse_Struct*)app->pBuffer; - - // Handle any EQEmu defined popup Ids first - switch(prs->popupid) - { - case POPUPID_UPDATE_SHOWSTATSWINDOW: - if(GetTarget() && GetTarget()->IsClient()) - GetTarget()->CastToClient()->SendStatsWindow(this, true); - else - SendStatsWindow(this, true); - return; - - default: - break; - } - - char buf[16]; - sprintf(buf, "%d\0", prs->popupid); - - parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); - - Mob* Target = GetTarget(); - if(Target && Target->IsNPC()) { - parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); - } -} - -void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) { - - if(app->size != sizeof(MovePotionToBelt_Struct)) { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", - sizeof(MovePotionToBelt_Struct), app->size); - DumpPacket(app); - return; - } - MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; - if(mptbs->Action == 0) { - const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); - if(BaseItem) { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; - strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); - } - } - else { - m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; - m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; - strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); - } - - Save(); - -} - -void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFGGetMatchesRequest_Struct* gmrs = (LFGGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFGMatches, sizeof(ServerLFGMatchesRequest_Struct)); - ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*) pack->pBuffer; - smrs->FromID = GetID(); - smrs->QuerierLevel = GetLevel(); - strcpy(smrs->FromName, GetName()); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->Classes = gmrs->Classes; - worldserver.SendPacket(pack); - safe_delete(pack); - } -} - - -void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFP_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); - DumpPacket(app); - return; - } - LFP_Struct *lfp = (LFP_Struct*)app->pBuffer; - - LFP = lfp->Action != LFPOff; - database.SetLFP(CharacterID(), LFP); - - if(!LFP) { - worldserver.StopLFP(CharacterID()); - return; - } - - GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; - - for(unsigned int i=0; iGetZoneID(); - LFPMembers[0].GuildID = GuildID(); - - if(g) { - // This should not happen. The client checks if you are in a group and will not let you put LFP on if - // you are not the leader. - if(!g->IsLeader(this)) { - LogFile->write(EQEMuLog::Error,"Client sent LFP on for character %s who is grouped but not leader.", GetName()); - return; - } - // Fill the LFPMembers array with the rest of the group members, excluding ourself - // We don't fill in the class, level or zone, because we may not be able to determine - // them if the other group members are not in this zone. World will fill in this information - // for us, if it can. - int NextFreeSlot = 1; - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(strcasecmp(g->membername[i], LFPMembers[0].Name)) - strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); - } - } - - - worldserver.UpdateLFP(CharacterID(), lfp->Action, lfp->MatchFilter, lfp->FromLevel, lfp->ToLevel, lfp->Classes, - lfp->Comments, LFPMembers); - - -} - -void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) { - - if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); - DumpPacket(app); - return; - } - LFPGetMatchesRequest_Struct* gmrs = (LFPGetMatchesRequest_Struct*)app->pBuffer; - - if (!worldserver.Connected()) - Message(0, "Error: World server disconnected"); - else { - ServerPacket* pack = new ServerPacket(ServerOP_LFPMatches, sizeof(ServerLFPMatchesRequest_Struct)); - ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*) pack->pBuffer; - smrs->FromID = GetID(); - smrs->FromLevel = gmrs->FromLevel; - smrs->ToLevel = gmrs->ToLevel; - smrs->QuerierLevel = GetLevel(); - smrs->QuerierClass = GetClass(); - strcpy(smrs->FromName, GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - - return; -} - -void Client::Handle_OP_Barter(const EQApplicationPacket *app) -{ - - if(app->size < 4) - { - LogFile->write(EQEMuLog::Debug, "OP_Barter packet below minimum expected size. The packet was %i bytes.", app->size); - DumpPacket(app); - return; - } - - char* Buf = (char *)app->pBuffer; - - // The first 4 bytes of the packet determine the action. A lot of Barter packets require the - // packet the client sent, sent back to it as an acknowledgement. - // - uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf); - - _pkt(TRADING__BARTER, app); - - switch(Action) - { - - case Barter_BuyerSearch: - { - BuyerItemSearch(app); - break; - } - - case Barter_SellerSearch: - { - BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer; - SendBuyerResults(bsr->SearchString, bsr->SearchID); - break; - } - - case Barter_BuyerModeOn: - { - if(!Trader) { - ToggleBuyerMode(true); - } - else { - Buf = (char *)app->pBuffer; - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff); - Message(13, "You cannot be a Trader and Buyer at the same time."); - } - QueuePacket(app); - break; - } - - case Barter_BuyerModeOff: - { - QueuePacket(app); - ToggleBuyerMode(false); - break; - } - - case Barter_BuyerItemUpdate: - { - UpdateBuyLine(app); - break; - } - - case Barter_BuyerItemRemove: - { - BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer; - database.RemoveBuyLine(CharacterID(), bris->BuySlot); - QueuePacket(app); - break; - } - - case Barter_SellItem: - { - SellToBuyer(app); - break; - } - - case Barter_BuyerInspectBegin: - { - ShowBuyLines(app); - break; - } - - case Barter_BuyerInspectEnd: - { - BuyerInspectRequest_Struct* bir = ( BuyerInspectRequest_Struct*)app->pBuffer; - Client *Buyer = entity_list.GetClientByID(bir->BuyerID); - if(Buyer) - Buyer->WithCustomer(0); - - break; - } - - case Barter_BarterItemInspect: - { - BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Welcome: - { - SendBazaarWelcome(); - break; - } - - case Barter_WelcomeMessageUpdate: - { - BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer; - SetBuyerWelcomeMessage(bwmu->WelcomeMessage); - break; - } - - case Barter_BuyerItemInspect: - { - BuyerItemSearchLinkRequest_Struct* bislr = (BuyerItemSearchLinkRequest_Struct*)app->pBuffer; - - const Item_Struct* item = database.GetItem(bislr->ItemID); - - if (!item) - Message(13, "Error: This item does not exist!"); - else - { - ItemInst* inst = database.CreateItem(item); - if (inst) - { - SendItemPacket(0, inst, ItemPacketViewLink); - safe_delete(inst); - } - } - break; - } - - case Barter_Unknown23: - { - // Sent by SoD client for no discernible reason. - break; - } - - default: - Message(13, "Unrecognised Barter action."); - _log(TRADING__BARTER, "Unrecognised Barter Action %i", Action); - - } -} - -void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) { - - if(app->size != sizeof(VoiceMacroIn_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_VoiceMacroIn expected %i got %i", - sizeof(VoiceMacroIn_Struct), app->size); - - DumpPacket(app); - - return; - } - - if(!RuleB(Chat, EnableVoiceMacros)) return; - - VoiceMacroIn_Struct* vmi = (VoiceMacroIn_Struct*)app->pBuffer; - - VoiceMacroReceived(vmi->Type, vmi->Target, vmi->MacroNumber); - -} - -void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) { - - if(app->size != sizeof(DoGroupLeadershipAbility_Struct)) { - - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DoGroupLeadershipAbility expected %i got %i", - sizeof(DoGroupLeadershipAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DoGroupLeadershipAbility_Struct* dglas = (DoGroupLeadershipAbility_Struct*)app->pBuffer; - - switch(dglas->Ability) - { - case GroupLeadershipAbility_MarkNPC: - { - if(GetTarget()) - { - Group* g = GetGroup(); - if(g) - g->MarkNPC(GetTarget(), dglas->Parameter); - } - break; - } - - case groupAAInspectBuffs: - { - Mob *Target = GetTarget(); - - if(!Target || !Target->IsClient()) - return; - - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return; - - Target->CastToClient()->InspectBuffs(this, g->GetLeadershipAA(groupAAInspectBuffs)); - - break; - } - - default: - break; - } -} - -void Client::Handle_OP_ClearNPCMarks(const EQApplicationPacket *app) { - - if(app->size != 0) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearNPCMarks expected 0 got %i", - app->size); - - DumpPacket(app); - - return; - } - - Group *g = GetGroup(); - - if(g) - g->ClearAllNPCMarks(); -} - -void Client::Handle_OP_DelegateAbility(const EQApplicationPacket *app) { - - if(app->size != sizeof(DelegateAbility_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_DelegateAbility expected %i got %i", - sizeof(DelegateAbility_Struct), app->size); - - DumpPacket(app); - - return; - } - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if(!g) return; - - switch(das->DelegateAbility) - { - case 0: - { - g->DelegateMainAssist(das->Name); - break; - } - case 1: - { - g->DelegateMarkNPC(das->Name); - break; - } - case 2: - { - g->DelegateMainTank(das->Name); - break; - } - case 3: - { - g->DelegatePuller(das->Name); - break; - } - default: - break; - } -} - -void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app) { - if (app->size != sizeof(ApplyPoison_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_ApplyPoison, size=%i, expected %i", app->size, sizeof(ApplyPoison_Struct)); - DumpPacket(app); - return; - } - uint32 ApplyPoisonSuccessResult = 0; - ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer; - const ItemInst* PrimaryWeapon = GetInv().GetItem(MainPrimary); - const ItemInst* SecondaryWeapon = GetInv().GetItem(MainSecondary); - const ItemInst* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot]; - - bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == ItemTypePoison); - - if(!IsPoison) - { - mlog(SPELLS__CASTING_ERR, "Item used to cast spell effect from a poison item was missing from inventory slot %d " - "after casting, or is not a poison!", ApplyPoisonData->inventorySlot); - - Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot); - } - else if(GetClass() == ROGUE) - { - if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == ItemType1HPiercing) || - (SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == ItemType1HPiercing)) { - float SuccessChance = (GetSkill(SkillApplyPoison) + GetLevel()) / 400.0f; - double ChanceRoll = MakeRandomFloat(0, 1); - - CheckIncreaseSkill(SkillApplyPoison, nullptr, 10); - - if(ChanceRoll < SuccessChance) { - ApplyPoisonSuccessResult = 1; - // NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here. - // My thinking was that DEX should be apart of the calculation. - AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX()/100) + 103); - } - - DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true); - - LogFile->write(EQEMuLog::Debug, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct)); - ApplyPoison_Struct* ApplyPoisonResult = (ApplyPoison_Struct*)outapp->pBuffer; - ApplyPoisonResult->success = ApplyPoisonSuccessResult; - ApplyPoisonResult->inventorySlot = ApplyPoisonData->inventorySlot; - - FastQueuePacket(&outapp); -} - - -void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) { - - // This packet is sent by the client when an Augment item information window is opened. - // We respond with an OP_ReadBook containing the type of distiller required to remove the augment. - // The OP_Augment packet includes a window parameter to determine which Item window in the UI the - // text is to be displayed in. out->type = 2 indicates the BookText_Struct contains item information. - // - - if(app->size != sizeof(AugmentInfo_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_AugmentInfo expected %i got %i", - sizeof(AugmentInfo_Struct), app->size); - - DumpPacket(app); - - return; - } - AugmentInfo_Struct* AugInfo = (AugmentInfo_Struct*) app->pBuffer; - - char *outstring = nullptr; - - const Item_Struct * item = database.GetItem(AugInfo->itemid); - - if (item) - { - MakeAnyLenString(&outstring, "You must use the solvent %s to remove this augment safely.", item->Name); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_ReadBook, strlen(outstring) + sizeof(BookText_Struct)); - - BookText_Struct *out = (BookText_Struct *) outapp->pBuffer; - - out->window = AugInfo->window; - - out->type = 2; - - out->invslot = 0; - - strcpy(out->booktext, outstring); - - safe_delete_array(outstring); - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) -{ - // This Opcode is sent by the client when the Leaderboard button on the PVP Stats window is pressed. - // - // It has a single uint32 payload which is the sort method: - // - // PVPSortByKills = 0, PVPSortByPoints = 1, PVPSortByInfamy = 2 - // - if(app->size != sizeof(PVPLeaderBoardRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", - sizeof(PVPLeaderBoardRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - /*PVPLeaderBoardRequest_Struct *pvplbrs = (PVPLeaderBoardRequest_Struct *)app->pBuffer;*/ //unused - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardReply, sizeof(PVPLeaderBoard_Struct)); - /*PVPLeaderBoard_Struct *pvplb = (PVPLeaderBoard_Struct *)outapp->pBuffer;*/ //unused - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app) -{ - // This opcode is sent by the client when the player right clicks a name on the PVP leaderboard and sends - // further details about the selected player, e.g. Race/Class/AAs/Guild etc. - // - if(app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", - sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardDetailsReply, sizeof(PVPLeaderBoardDetailsReply_Struct)); - PVPLeaderBoardDetailsReply_Struct *pvplbdrs = (PVPLeaderBoardDetailsReply_Struct *)outapp->pBuffer; - - // TODO: Record and send this data. - - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app) -{ - if(app->size != sizeof(Adventure_Sell_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_AdventureMerchantSell: got %u expected %u", - app->size, sizeof(Adventure_Sell_Struct)); - DumpPacket(app); - return; - } - - Adventure_Sell_Struct *ams_in = (Adventure_Sell_Struct*)app->pBuffer; - - Mob* vendor = entity_list.GetMob(ams_in->npcid); - if (vendor == 0 || !vendor->IsNPC() || ((vendor->GetClass() != ADVENTUREMERCHANT) && - (vendor->GetClass() != NORRATHS_KEEPERS_MERCHANT) && (vendor->GetClass() != DARK_REIGN_MERCHANT))) - { - Message(13, "Vendor was not found."); - return; - } - - if(DistNoRoot(*vendor) > USE_NPC_RANGE2) - { - Message(13, "Vendor is out of range."); - return; - } - - uint32 itemid = GetItemIDAt(ams_in->slot); - - if(itemid == 0) - { - Message(13, "Found no item at that slot."); - return; - } - - const Item_Struct* item = database.GetItem(itemid); - ItemInst* inst = GetInv().GetItem(ams_in->slot); - if(!item || !inst){ - Message(13, "You seemed to have misplaced that item..."); - return; - } - - // Note that Lucy has ldonsold values of 4 and 5 for items sold by Norrath's Keepers and Dark Reign, whereas 13th Floor - // has ldonsold = 0 for these items, so some manual editing of the items DB will be required to support sell back of the - // items. - // - // The Merchant seems to have some other way of knowing whether he will accept the item, other than the ldonsold field, - // e.g. if you summon items 76036 and 76053 (good and evil versions of Spell: Ward Of Vengeance), if you are interacting - // with a Norrath's Keeper merchant and click on 76036 in your inventory, he says he will give you radiant crystals for - // it, but he will refuse for item 76053. - // - // Similarly, just giving a cloth cap an ldonsold value of 4 will not make the Merchant buy it. - // - // Note that the the Client will not allow you to sell anything back to a Discord merchant, so there is no need to handle - // that case here. - if(item->LDoNSold == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if(item->LDoNPrice == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - int32 price = item->LDoNPrice * 70 / 100; - - if(price == 0) - { - Message(13, "The merchant does not want that item."); - return; - } - - if (RuleB(EventLog, RecordSellToMerchant)) - LogMerchant(this, vendor, ams_in->charges, price, item, false); - - if(!inst->IsStackable()) - { - DeleteItemInInventory(ams_in->slot, 0, false); - } - else - { - if(inst->GetCharges() < ams_in->charges) - { - ams_in->charges = inst->GetCharges(); - } - - if(ams_in->charges == 0) - { - Message(13, "Charge mismatch error."); - return; - } - - DeleteItemInInventory(ams_in->slot, ams_in->charges, false); - price *= ams_in->charges; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureMerchantSell, sizeof(Adventure_Sell_Struct)); - Adventure_Sell_Struct *ams = (Adventure_Sell_Struct*)outapp->pBuffer; - ams->slot = ams_in->slot; - ams->unknown000 = 1; - ams->npcid = ams->npcid; - ams->charges = ams_in->charges; - ams->sell_price = price; - FastQueuePacket(&outapp); - - switch(vendor->GetClass()) - { - case ADVENTUREMERCHANT: - { - UpdateLDoNPoints(price, 6); - break; - } - case NORRATHS_KEEPERS_MERCHANT: - { - SetRadiantCrystals(GetRadiantCrystals() + price); - break; - } - case DARK_REIGN_MERCHANT: - { - SetEbonCrystals(GetEbonCrystals() + price); - break; - } - - default: - break; - } - - Save(1); -} - -void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app) -{ - if(adventure_stats_timer) - { - return; - } - - adventure_stats_timer = new Timer(8000); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureStatsReply, sizeof(AdventureStats_Struct)); - AdventureStats_Struct *as = (AdventureStats_Struct*)outapp->pBuffer; - - if(database.GetAdventureStats(CharacterID(), as->success.guk, as->success.mir, as->success.mmc, as->success.ruj, - as->success.tak, as->failure.guk, as->failure.mir, as->failure.mmc, as->failure.ruj, as->failure.tak)) - { - as->failure.total = as->failure.guk + as->failure.mir + as->failure.mmc + as->failure.ruj + as->failure.tak; - as->success.total = as->success.guk + as->success.mir + as->success.mmc + as->success.ruj + as->success.tak; - m_pp.ldon_wins_guk = as->success.guk; - m_pp.ldon_wins_mir = as->success.mir; - m_pp.ldon_wins_mmc = as->success.mmc; - m_pp.ldon_wins_ruj = as->success.ruj; - m_pp.ldon_wins_tak = as->success.tak; - m_pp.ldon_losses_guk = as->failure.guk; - m_pp.ldon_losses_mir = as->failure.mir; - m_pp.ldon_losses_mmc = as->failure.mmc; - m_pp.ldon_losses_ruj = as->failure.ruj; - m_pp.ldon_losses_tak = as->failure.tak; - } - - FastQueuePacket(&outapp); -} - -void Client::Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(AdventureLeaderboardRequest_Struct)) - { - return; - } - - if(adventure_leaderboard_timer) - { - return; - } - - adventure_leaderboard_timer = new Timer(4000); - ServerPacket *pack = new ServerPacket(ServerOP_AdventureLeaderboard, sizeof(ServerLeaderboardRequest_Struct)); - ServerLeaderboardRequest_Struct *lr = (ServerLeaderboardRequest_Struct*)pack->pBuffer; - strcpy(lr->player, GetName()); - - AdventureLeaderboardRequest_Struct *lrs = (AdventureLeaderboardRequest_Struct*)app->pBuffer; - lr->type = 1 + (lrs->theme * 2) + lrs->type; - worldserver.SendPacket(pack); - delete pack; -} - -void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) -{ -// This opcode is sent by the client when the player choses which bind to return to. -// The client sends just a 4 byte packet with the selection number in it -// - if(app->size != 4) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RespawnWindow expected %i got %i", - 4, app->size); - DumpPacket(app); - return; - } - char *Buffer = (char *)app->pBuffer; - - uint32 Option = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - HandleRespawnFromHover(Option); -} - void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) { - if(app->size != sizeof(GroupUpdate_Struct)) + if (app->size != sizeof(GroupUpdate_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch on OP_GroupUpdate: got %u expected %u", app->size, sizeof(GroupUpdate_Struct)); @@ -11610,285 +6966,39 @@ void Client::Handle_OP_GroupUpdate(const EQApplicationPacket *app) GroupUpdate_Struct* gu = (GroupUpdate_Struct*)app->pBuffer; - switch(gu->action) { - case groupActMakeLeader: - { - Mob* newleader = entity_list.GetClientByName(gu->membername[0]); - Group* group = this->GetGroup(); + switch (gu->action) { + case groupActMakeLeader: + { + Mob* newleader = entity_list.GetClientByName(gu->membername[0]); + Group* group = this->GetGroup(); - if (newleader && group) { - // the client only sends this if it's the group leader, but check anyway - if(group->IsLeader(this)) - group->ChangeLeader(newleader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s",GetName()); - DumpPacket(app); - } + if (newleader && group) { + // the client only sends this if it's the group leader, but check anyway + if (group->IsLeader(this)) + group->ChangeLeader(newleader); + else { + LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); + DumpPacket(app); } - break; } - - default: - { - LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); - DumpPacket(app); - return; - } - } -} - -void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) -{ - // if the character has a start city, don't let them use the command - if(m_pp.binds[4].zoneId != 0) { - Message(15,"Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); - return; + break; } - if (app->size < 1) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + default: + { + LogFile->write(EQEMuLog::Debug, "Received unhandled OP_GroupUpdate requesting action %u", gu->action); DumpPacket(app); return; } - - float x(0),y(0),z(0); - uint32 zoneid = 0; - uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); - - std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - auto results = database.QueryDatabase(query); - if(!results.Success()) { - LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); - return; } - - bool validCity = false; - for (auto row = results.begin(); row != results.end(); ++row) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - if(zoneid != startCity) - continue; - - validCity = true; - x = atof(row[2]); - y = atof(row[3]); - z = atof(row[4]); - } - - if(validCity) { - Message(15,"Your home city has been set"); - SetStartZone(startCity, x, y, z); - return; - } - - query = StringFormat("SELECT zone_id, bind_id FROM start_zones " - "WHERE player_class=%i AND player_deity=%i AND player_race=%i", - m_pp.class_, m_pp.deity, m_pp.race); - results = database.QueryDatabase(query); - if (!results.Success()) - return; - - Message(15,"Use \"/startcity #\" to choose a home city from the following list:"); - - for (auto row = results.begin(); row != results.end(); ++row) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - char* name; - database.GetZoneLongName(database.GetZoneName(zoneid), &name); - Message(15,"%d - %s", zoneid, name); - } - -} - -void Client::Handle_OP_Report(const EQApplicationPacket *app) -{ - if(!CanUseReport) - { - Message_StringID(MT_System, REPORT_ONCE); - return; - } - - uint32 size = app->size; - uint32 current_point = 0; - std::string reported, reporter; - std::string current_string; - int mode = 0; - - while(current_point < size) - { - if(mode < 2) - { - if(app->pBuffer[current_point] == '|') - { - mode++; - } - else - { - if(mode == 0) - { - reported += app->pBuffer[current_point]; - } - else - { - reporter += app->pBuffer[current_point]; - } - } - current_point++; - } - else - { - if(app->pBuffer[current_point] == 0x0a) - { - current_string += '\n'; - } - else if(app->pBuffer[current_point] == 0x00) - { - CanUseReport = false; - database.AddReport(reporter, reported, current_string); - return; - } - else - { - current_string += app->pBuffer[current_point]; - } - current_point++; - } - } - - CanUseReport = false; - database.AddReport(reporter, reported, current_string); -} - -void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) -{ - if(app->size < sizeof(VeteranClaimRequest)) - { - LogFile->write(EQEMuLog::Debug, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", - app->size, sizeof(VeteranClaimRequest)); - DumpPacket(app); - return; - } - - VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; - - if(vcr->claim_id == 0xFFFFFFFF) //request update packet - { - SendRewards(); - } - else //try to claim something! - { - if(!TryReward(vcr->claim_id)) - { - Message(13, "Your claim has been rejected."); - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = -1; - FastQueuePacket(&vetapp); - } - else - { - EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); - VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; - strcpy(cr->name, GetName()); - cr->claim_id = vcr->claim_id; - cr->reject_field = 0; - FastQueuePacket(&vetapp); - } - } -} - -void Client::Handle_OP_GMSearchCorpse(const EQApplicationPacket *app) -{ - // Could make this into a rule, although there is a hard limit since we are using a popup, of 4096 bytes that can - // be displayed in the window, including all the HTML formatting tags. - // - const int maxResults = 10; - - if(app->size < sizeof(GMSearchCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "OP_GMSearchCorpse size lower than expected: got %u expected at least %u", - app->size, sizeof(GMSearchCorpse_Struct)); - DumpPacket(app); - return; - } - - GMSearchCorpse_Struct *gmscs = (GMSearchCorpse_Struct *)app->pBuffer; - gmscs->Name[63] = '\0'; - - char *escSearchString = new char[129]; - database.DoEscapeString(escSearchString, gmscs->Name, strlen(gmscs->Name)); - - std::string query = StringFormat("SELECT charname, zoneid, x, y, z, timeofdeath, rezzed, IsBurried " - "FROM player_corpses WheRE charname LIKE '%%%s%%' ORDER BY charname LIMIT %i", - escSearchString, maxResults); - safe_delete_array(escSearchString); - auto results = database.QueryDatabase(query); - if (!results.Success()) { - Message(0, "Query failed: %s.", results.ErrorMessage().c_str()); - return; - } - - if (results.RowCount() == 0) - return; - - if(results.RowCount() == maxResults) - Message(clientMessageError, "Your search found too many results; some are not displayed."); - else - Message(clientMessageYellow, "There are %i corpse(s) that match the search string '%s'.", results.RowCount(), gmscs->Name); - - char charName[64], timeOfDeath[20]; - - std::string popupText = ""; - - for (auto row = results.begin(); row != results.end(); ++row) { - - strn0cpy(charName, row[0], sizeof(charName)); - - uint32 ZoneID = atoi(row[1]); - float CorpseX = atof(row[2]); - float CorpseY = atof(row[3]); - float CorpseZ = atof(row[4]); - - strn0cpy(timeOfDeath, row[5], sizeof(timeOfDeath)); - - bool corpseRezzed = atoi(row[6]); - bool corpseBuried = atoi(row[7]); - - popupText += StringFormat("", - charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, timeOfDeath, - corpseRezzed ? "Yes" : "No", corpseBuried ? "Yes" : "No"); - - if(popupText.size() > 4000) { - Message(clientMessageError, "Unable to display all the results."); - break; - } - - } - - popupText += "
NameZoneXYZDate" - "RezzedBuried
 " - "
%s%s%8.0f%8.0f%8.0f%s%s%s
"; - - SendPopupToClient("Corpses", popupText.c_str()); - } void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) { - if(!GuildBanks) + if (!GuildBanks) return; - if((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) + if ((int)zone->GetZoneID() != RuleI(World, GuildBankZoneID)) { Message(13, "The Guild Bank is not available in this zone."); @@ -11905,11 +7015,11 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buffer); - if(!IsInAGuild()) + if (!IsInAGuild()) { Message(13, "You must be in a Guild to use the Guild Bank."); - if(Action == GuildBankDeposit) + if (Action == GuildBankDeposit) GuildBankDepositAck(true); else GuildBankAck(); @@ -11917,9 +7027,9 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) return; } - if(!IsGuildBanker()) + if (!IsGuildBanker()) { - if((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) + if ((Action != GuildBankDeposit) && (Action != GuildBankViewItem) && (Action != GuildBankWithdraw)) { _log(GUILDS__BANK_ERROR, "Suspected hacking attempt on guild bank from %s", GetName()); @@ -11929,700 +7039,253 @@ void Client::Handle_OP_GuildBank(const EQApplicationPacket *app) } } - switch(Action) + switch (Action) { - case GuildBankPromote: + case GuildBankPromote: + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - { - Message_StringID(13, GUILD_BANK_FULL); + Message_StringID(13, GUILD_BANK_FULL); - GuildBankDepositAck(true); + GuildBankDepositAck(true); - return; - } - - GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer; - - int Slot = GuildBanks->Promote(GuildID(), gbps->Slot); - - if(Slot >= 0) - { - ItemInst* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1); - - if(inst) - { - Message_StringID(clientMessageWhite, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name); - safe_delete(inst); - } - } - else - Message(13, "Unexpected error while moving item into Guild Bank."); - - GuildBankAck(); - - break; - } - - case GuildBankViewItem: - { - GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1); - - if(!inst) - break; - - SendItemPacket(0, inst, ItemPacketViewLink); - - safe_delete(inst); - - break; - } - - case GuildBankDeposit: // Deposit Item - { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) - { - Message_StringID(13, GUILD_BANK_FULL); - - GuildBankDepositAck(true); - - return; - } - - ItemInst *CursorItemInst = GetInv().GetItem(MainCursor); - - bool Allowed = true; - - if(!CursorItemInst) - { - Message(13, "No Item on the cursor."); - - GuildBankDepositAck(true); - - return; - } - - const Item_Struct* CursorItem = CursorItemInst->GetItem(); - - if(!CursorItem->NoDrop || CursorItemInst->IsInstNoDrop()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItemInst->IsNoneEmptyContainer()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItemInst->IsAugmented()) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItem->NoRent == 0) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - else if(CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID)) - { - Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); - - Allowed = false; - } - - if(!Allowed) - { - GuildBankDepositAck(true); - - return; - } - - if(GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, "")) - { - GuildBankDepositAck(false); - - DeleteItemInInventory(MainCursor, 0, false); - } - - break; - } - - case GuildBankPermissions: - { - GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer; - - if(gbps->Permissions == 1) - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName); - else - GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, ""); - - GuildBankAck(); - break; - } - - case GuildBankWithdraw: - { - if (GetInv()[MainCursor]) - { - Message_StringID(13, GUILD_BANK_EMPTY_HANDS); - - GuildBankAck(); - - break; - } - - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - ItemInst* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - - if(!inst) - { - GuildBankAck(); - - break; - } - - if(!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) - { - _log(GUILDS__BANK_ERROR, "Suspected attempted hack on the guild bank from %s", GetName()); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if(CheckLoreConflict(inst->GetItem())) - { - Message_StringID(13, DUP_LORE); - - GuildBankAck(); - - safe_delete(inst); - - break; - } - - if (gbwis->Quantity > 0) - { - PushItemOnCursor(*inst); - - SendItemPacket(MainCursor, inst, ItemPacketSummonItem); - - GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); - } - else - { - Message(0, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name); - } - - safe_delete(inst); - - GuildBankAck(); - - break; - } - - case GuildBankSplitStacks: - { - if(GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) - Message_StringID(13, GUILD_BANK_FULL); - else - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity); - } - - GuildBankAck(); - - break; - } - - case GuildBankMergeStacks: - { - GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; - - GuildBanks->MergeStacks(GuildID(), gbwis->SlotID); - - GuildBankAck(); - - break; - } - - default: - { - Message(13, "Unexpected GuildBank action."); - - _log(GUILDS__BANK_ERROR, "Received unexpected guild bank action code %i from %s", Action, GetName()); - } - } -} - -void Client::Handle_OP_GroupRoles(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupRole_Struct)) { - LogFile->write(EQEMuLog::Error, "Wrong size: OP_GroupRoles, size=%i, expected %i", app->size, sizeof(GroupRole_Struct)); - DumpPacket(app); - return; - } - GroupRole_Struct *grs = (GroupRole_Struct*)app->pBuffer; - - Group *g = GetGroup(); - - if(!g) - return; - - switch(grs->RoleNumber) - { - case 1: //Main Tank - { - if(grs->Toggle) - g->DelegateMainTank(grs->Name1, grs->Toggle); - else - g->UnDelegateMainTank(grs->Name1, grs->Toggle); - break; - } - case 2: //Main Assist - { - if(grs->Toggle) - g->DelegateMainAssist(grs->Name1, grs->Toggle); - else - g->UnDelegateMainAssist(grs->Name1, grs->Toggle); - break; - } - case 3: //Puller - { - if(grs->Toggle) - g->DelegatePuller(grs->Name1, grs->Toggle); - else - g->UnDelegatePuller(grs->Name1, grs->Toggle); - break; - } - default: - break; - } -} - -void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) -{ - // New OPCode for SOD+ as /hidecorpse is handled serverside now. - // - if(app->size != sizeof(HideCorpse_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_HideCorpse expected %i got %i", - sizeof(HideCorpse_Struct), app->size); - - DumpPacket(app); - - return; - } - - HideCorpse_Struct *hcs = (HideCorpse_Struct*)app->pBuffer; - - if(hcs->Action == HideCorpseLooted) - return; - - if((HideCorpseMode == HideCorpseNone) && (hcs->Action == HideCorpseNone)) - return; - - entity_list.HideCorpses(this, HideCorpseMode, hcs->Action); - - HideCorpseMode = hcs->Action; -} - -void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GuildUpdateURLAndChannel_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", - sizeof(GuildUpdateURLAndChannel_Struct), app->size); - - DumpPacket(app); - - return; - } - - GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; - - if(!IsInAGuild()) - return; - - if(!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - { - Message(13, "Only the guild leader can change the Channel or URL.!"); - return; - } - - if(guuacs->Action == 0) - guild_mgr.SetGuildURL(GuildID(), guuacs->Text); - else - guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); - -} - -void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) -{ - if(app->size != sizeof(GuildStatus_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildStatus expected %i got %i", - sizeof(GuildStatus_Struct), app->size); - - DumpPacket(app); - - return; - } - GuildStatus_Struct *gss = (GuildStatus_Struct*)app->pBuffer; - - Client *c = entity_list.GetClientByName(gss->Name); - - if(!c) - { - Message_StringID(clientMessageWhite, TARGET_PLAYER_FOR_GUILD_STATUS); - return; - } - - uint32 TargetGuildID = c->GuildID(); - - if(TargetGuildID == GUILD_NONE) - { - Message_StringID(clientMessageWhite, NOT_IN_A_GUILD, c->GetName()); - return; - } - - const char *GuildName = guild_mgr.GetGuildName(TargetGuildID); - - if(!GuildName) - return; - - bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); - bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); - - if((TargetGuildID == GuildID()) && (c != this)) - { - if(IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_YOUR_GUILD, c->GetName()); - else if(IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_YOUR_GUILD, c->GetName()); - else - Message_StringID(clientMessageWhite, MEMBER_OF_YOUR_GUILD, c->GetName()); - - return; - } - - if(IsLeader) - Message_StringID(clientMessageWhite, LEADER_OF_X_GUILD, c->GetName(), GuildName); - else if(IsOfficer) - Message_StringID(clientMessageWhite, OFFICER_OF_X_GUILD, c->GetName(), GuildName); - else - Message_StringID(clientMessageWhite, MEMBER_OF_X_GUILD, c->GetName(), GuildName); -} - -void Client::Handle_OP_BlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_BlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - - std::set::iterator Iterator; - - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - if(bbs->Initialise == 1) - { - BlockedBuffs->clear(); - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if((bbs->SpellID[i] > 0) && IsBeneficialSpell(bbs->SpellID[i])) - { - if(BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end()) - BlockedBuffs->insert(bbs->SpellID[i]); - } - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 1; - obbs->Flags = 0x54; - obbs->Count = BlockedBuffs->size(); - - unsigned int Element = 0; - - Iterator = BlockedBuffs->begin(); - - while(Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - return; - } - - if((bbs->Initialise == 0) && (bbs->Count > 0)) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_BlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = -1; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x54; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - { - if(!IsBeneficialSpell(bbs->SpellID[i])) - continue; - - if((BlockedBuffs->size() < BLOCKED_BUFF_COUNT) && (BlockedBuffs->find(bbs->SpellID[i]) == BlockedBuffs->end())) - BlockedBuffs->insert(bbs->SpellID[i]); - } - obbs->Count = BlockedBuffs->size(); - - Iterator = BlockedBuffs->begin(); - - unsigned int Element = 0; - - while(Iterator != BlockedBuffs->end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != sizeof(BlockedBuffs_Struct)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", - sizeof(BlockedBuffs_Struct), app->size); - - DumpPacket(app); - - return; - } - BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; - - std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; - - std::set RemovedBuffs; - - if(bbs->Count > 0) - { - std::set::iterator Iterator; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_RemoveBlockedBuffs, sizeof(BlockedBuffs_Struct)); - - BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; - - for(unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) - obbs->SpellID[i] = 0; - - obbs->Pet = bbs->Pet; - obbs->Initialise = 0; - obbs->Flags = 0x5a; - - for(unsigned int i = 0; i < bbs->Count; ++i) - { - Iterator = BlockedBuffs->find(bbs->SpellID[i]); - - if(Iterator != BlockedBuffs->end()) - { - RemovedBuffs.insert(bbs->SpellID[i]); - - BlockedBuffs->erase(Iterator); - } - } - obbs->Count = RemovedBuffs.size(); - - Iterator = RemovedBuffs.begin(); - - unsigned int Element = 0; - - while(Iterator != RemovedBuffs.end()) - { - obbs->SpellID[Element++] = (*Iterator); - ++Iterator; - } - - FastQueuePacket(&outapp); - } -} -void Client::Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app) -{ - if(!RuleB(Spells, EnableBlockedBuffs)) - return; - - if(app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_ClearBlockedBuffs expected 1 got %i", app->size); - - DumpPacket(app); - - return; - } - - bool Pet = app->pBuffer[0]; - - if(Pet) - PetBlockedBuffs.clear(); - else - PlayerBlockedBuffs.clear(); - - QueuePacket(app); -} - -void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app) -{ - // In SoD, this is used for clicking off Pet Buffs only. In Underfoot, it is used both for Client and Pets - // The payload contains buffslot and EntityID only, so we must check if the EntityID is ours or our pets. - // - VERIFY_PACKET_LENGTH(OP_BuffRemoveRequest, app, BuffRemoveRequest_Struct); - - BuffRemoveRequest_Struct *brrs = (BuffRemoveRequest_Struct*)app->pBuffer; - - Mob *m = nullptr; - - if(brrs->EntityID == GetID()) - m = this; - else if(brrs->EntityID == GetPetID()) - m = GetPet(); - - if(!m) - return; - - if(brrs->SlotID > (uint32)m->GetMaxTotalSlots()) - return; - - uint16 SpellID = m->GetSpellIDFromSlot(brrs->SlotID); - - if(SpellID && IsBeneficialSpell(SpellID)) - m->BuffFadeBySlot(brrs->SlotID, true); -} - -void Client::Handle_OP_CorpseDrag(const EQApplicationPacket *app) -{ - if(DraggedCorpses.size() >= (unsigned int)RuleI(Character, MaxDraggedCorpses)) - { - Message_StringID(13, CORPSEDRAG_LIMIT); - return; - } - - VERIFY_PACKET_LENGTH(OP_CorpseDrag, app, CorpseDrag_Struct); - - CorpseDrag_Struct *cds = (CorpseDrag_Struct*)app->pBuffer; - - Mob* corpse = entity_list.GetMob(cds->CorpseName); - - if(!corpse || !corpse->IsPlayerCorpse() || corpse->CastToCorpse()->IsBeingLooted()) - return; - - Client *c = entity_list.FindCorpseDragger(corpse->GetID()); - - if(c) - { - if(c == this) - Message_StringID(MT_DefaultText, CORPSEDRAG_ALREADY, corpse->GetCleanName()); - else - Message_StringID(MT_DefaultText, CORPSEDRAG_SOMEONE_ELSE, corpse->GetCleanName()); - - return; - } - - if(!corpse->CastToCorpse()->Summon(this, false, true)) - return; - - DraggedCorpses.push_back(std::pair(cds->CorpseName, corpse->GetID())); - - Message_StringID(MT_DefaultText, CORPSEDRAG_BEGIN, cds->CorpseName); -} - -void Client::Handle_OP_CorpseDrop(const EQApplicationPacket *app) -{ - if(app->size == 1) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOPALL); - ClearDraggedCorpses(); - return; - } - - for (auto Iterator = DraggedCorpses.begin(); Iterator != DraggedCorpses.end(); ++Iterator) - { - if(!strcasecmp(Iterator->first.c_str(), (const char *)app->pBuffer)) - { - Message_StringID(MT_DefaultText, CORPSEDRAG_STOP); - Iterator = DraggedCorpses.erase(Iterator); return; } - } -} -void Client::Handle_OP_GroupMakeLeader(const EQApplicationPacket *app) -{ - VERIFY_PACKET_LENGTH(OP_GroupMakeLeader, app, GroupMakeLeader_Struct); + GuildBankPromote_Struct *gbps = (GuildBankPromote_Struct*)app->pBuffer; - GroupMakeLeader_Struct *gmls = (GroupMakeLeader_Struct *)app->pBuffer; + int Slot = GuildBanks->Promote(GuildID(), gbps->Slot); - Mob* NewLeader = entity_list.GetClientByName(gmls->NewLeader); + if (Slot >= 0) + { + ItemInst* inst = GuildBanks->GetItem(GuildID(), GuildBankMainArea, Slot, 1); - Group* g = GetGroup(); - - if (NewLeader && g) - { - if(g->IsLeader(this)) - g->ChangeLeader(NewLeader); - else { - LogFile->write(EQEMuLog::Debug, "Group /makeleader request originated from non-leader member: %s", GetName()); - DumpPacket(app); + if (inst) + { + Message_StringID(clientMessageWhite, GUILD_BANK_TRANSFERRED, inst->GetItem()->Name); + safe_delete(inst); + } } + else + Message(13, "Unexpected error while moving item into Guild Bank."); + + GuildBankAck(); + + break; + } + + case GuildBankViewItem: + { + GuildBankViewItem_Struct *gbvis = (GuildBankViewItem_Struct*)app->pBuffer; + + ItemInst* inst = GuildBanks->GetItem(GuildID(), gbvis->Area, gbvis->SlotID, 1); + + if (!inst) + break; + + SendItemPacket(0, inst, ItemPacketViewLink); + + safe_delete(inst); + + break; + } + + case GuildBankDeposit: // Deposit Item + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankDepositArea)) + { + Message_StringID(13, GUILD_BANK_FULL); + + GuildBankDepositAck(true); + + return; + } + + ItemInst *CursorItemInst = GetInv().GetItem(MainCursor); + + bool Allowed = true; + + if (!CursorItemInst) + { + Message(13, "No Item on the cursor."); + + GuildBankDepositAck(true); + + return; + } + + const Item_Struct* CursorItem = CursorItemInst->GetItem(); + + if (!CursorItem->NoDrop || CursorItemInst->IsInstNoDrop()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItemInst->IsNoneEmptyContainer()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItemInst->IsAugmented()) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItem->NoRent == 0) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + else if (CursorItem->LoreFlag && GuildBanks->HasItem(GuildID(), CursorItem->ID)) + { + Message_StringID(13, GUILD_BANK_CANNOT_DEPOSIT); + + Allowed = false; + } + + if (!Allowed) + { + GuildBankDepositAck(true); + + return; + } + + if (GuildBanks->AddItem(GuildID(), GuildBankDepositArea, CursorItem->ID, CursorItemInst->GetCharges(), GetName(), GuildBankBankerOnly, "")) + { + GuildBankDepositAck(false); + + DeleteItemInInventory(MainCursor, 0, false); + } + + break; + } + + case GuildBankPermissions: + { + GuildBankPermissions_Struct *gbps = (GuildBankPermissions_Struct*)app->pBuffer; + + if (gbps->Permissions == 1) + GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, gbps->MemberName); + else + GuildBanks->SetPermissions(GuildID(), gbps->SlotID, gbps->Permissions, ""); + + GuildBankAck(); + break; + } + + case GuildBankWithdraw: + { + if (GetInv()[MainCursor]) + { + Message_StringID(13, GUILD_BANK_EMPTY_HANDS); + + GuildBankAck(); + + break; + } + + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + ItemInst* inst = GuildBanks->GetItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); + + if (!inst) + { + GuildBankAck(); + + break; + } + + if (!IsGuildBanker() && !GuildBanks->AllowedToWithdraw(GuildID(), gbwis->Area, gbwis->SlotID, GetName())) + { + _log(GUILDS__BANK_ERROR, "Suspected attempted hack on the guild bank from %s", GetName()); + + GuildBankAck(); + + safe_delete(inst); + + break; + } + + if (CheckLoreConflict(inst->GetItem())) + { + Message_StringID(13, DUP_LORE); + + GuildBankAck(); + + safe_delete(inst); + + break; + } + + if (gbwis->Quantity > 0) + { + PushItemOnCursor(*inst); + + SendItemPacket(MainCursor, inst, ItemPacketSummonItem); + + GuildBanks->DeleteItem(GuildID(), gbwis->Area, gbwis->SlotID, gbwis->Quantity); + } + else + { + Message(0, "Unable to withdraw 0 quantity of %s", inst->GetItem()->Name); + } + + safe_delete(inst); + + GuildBankAck(); + + break; + } + + case GuildBankSplitStacks: + { + if (GuildBanks->IsAreaFull(GuildID(), GuildBankMainArea)) + Message_StringID(13, GUILD_BANK_FULL); + else + { + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + GuildBanks->SplitStack(GuildID(), gbwis->SlotID, gbwis->Quantity); + } + + GuildBankAck(); + + break; + } + + case GuildBankMergeStacks: + { + GuildBankWithdrawItem_Struct *gbwis = (GuildBankWithdrawItem_Struct*)app->pBuffer; + + GuildBanks->MergeStacks(GuildID(), gbwis->SlotID); + + GuildBankAck(); + + break; + } + + default: + { + Message(13, "Unexpected GuildBank action."); + + _log(GUILDS__BANK_ERROR, "Received unexpected guild bank action code %i from %s", Action, GetName()); + } } } void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) { - if(IsInAGuild()) + if (IsInAGuild()) { Message(clientMessageError, "You are already in a guild!"); return; } - if(!RuleB(Guild, PlayerCreationAllowed)) + if (!RuleB(Guild, PlayerCreationAllowed)) { Message(clientMessageError, "This feature is disabled on this server. Contact a GM or post on your server message boards to create a guild."); return; @@ -12645,19 +7308,19 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) #if __DARWIN_C_LEVEL < 200809L if (strlen(GuildName) > 60) #else - if(strnlen(GuildName, 64) > 60) + if (strnlen(GuildName, 64) > 60) #endif // __DARWIN_C_LEVEL #else - if(strnlen(GuildName, 64) > 60) + if (strnlen(GuildName, 64) > 60) #endif // DARWIN { Message(clientMessageError, "Guild name too long."); return; } - for(unsigned int i = 0; i < strlen(GuildName); ++i) + for (unsigned int i = 0; i < strlen(GuildName); ++i) { - if(!isalpha(GuildName[i]) && (GuildName[i] != ' ')) + if (!isalpha(GuildName[i]) && (GuildName[i] != ' ')) { Message(clientMessageError, "Invalid character in Guild name."); return; @@ -12666,13 +7329,13 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) int32 GuildCount = guild_mgr.DoesAccountContainAGuildLeader(AccountID()); - if(GuildCount >= RuleI(Guild, PlayerCreationLimit)) + if (GuildCount >= RuleI(Guild, PlayerCreationLimit)) { Message(clientMessageError, "You cannot create this guild because this account may only be leader of %i guilds.", RuleI(Guild, PlayerCreationLimit)); return; } - if(guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) + if (guild_mgr.GetGuildIDByName(GuildName) != GUILD_NONE) { Message_StringID(clientMessageError, GUILD_NAME_IN_USE); return; @@ -12687,820 +7350,1047 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app) Message(clientMessageError, "Guild creation failed."); else { - if(!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) + if (!guild_mgr.SetGuild(CharacterID(), NewGuildID, GUILD_LEADER)) Message(clientMessageError, "Unable to set guild leader's guild in the database. Contact a GM."); else { Message(clientMessageYellow, "You are now the leader of %s", GuildName); - if(zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) GuildBanks->SendGuildBank(this); SendGuildRanks(); } } } -void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32); +void Client::Handle_OP_GuildDelete(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildDelete"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - NPC* tar = entity_list.GetNPCByID(*((uint32*)app->pBuffer)); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - std::list::iterator altc_iter = zone->AlternateCurrencies.begin(); - bool found = false; - while(altc_iter != zone->AlternateCurrencies.end()) { - if((*altc_iter).id == alt_cur_id) { - found = true; - break; - } - ++altc_iter; - } - - if(!found) { - return; - } - - std::stringstream ss(std::stringstream::in | std::stringstream::out); - std::stringstream item_ss(std::stringstream::in | std::stringstream::out); - ss << alt_cur_id << "|1|" << alt_cur_id; - uint32 count = 0; - uint32 merchant_id = tar->MerchantType; - const Item_Struct *item = nullptr; - - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end() && count < 255; ++itr){ - const MerchantList &ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(item) - { - item_ss << "^" << item->Name << "|"; - item_ss << item->ID << "|"; - item_ss << ml.alt_currency_cost << "|"; - item_ss << "0|"; - item_ss << "1|"; - item_ss << item->Races << "|"; - item_ss << item->Classes; - count++; - } - } - - if(count > 0) { - ss << "|" << count << item_ss.str(); - } else { - ss << "|0"; - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencyMerchantReply, ss.str().length() + 1); - memcpy(outapp->pBuffer, ss.str().c_str(), ss.str().length()); - FastQueuePacket(&outapp); - } -} - -void Client::Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencySellSelection, app, AltCurrencySelectItem_Struct); - - AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; - - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - ItemInst *inst = m_inv.GetItem(select->slot_id); - if(!inst) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - - if (RuleB(Merchant, EnableAltCurrencySell)) { - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for (itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if (GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if (!item) - continue; - - if (item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!found) { - cost = 0; - } - } + if (!IsInAGuild() || !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + Message(0, "You are not a guild leader or not in a guild."); + else { + mlog(GUILDS__ACTIONS, "Deleting guild %s (%d)", guild_mgr.GetGuildName(GuildID()), GuildID()); + if (!guild_mgr.DeleteGuild(GuildID())) + Message(0, "Guild delete failed."); else { - cost = 0; + Message(0, "Guild successfully deleted."); } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AltCurrencySellSelection, sizeof(AltCurrencySelectItemReply_Struct)); - AltCurrencySelectItemReply_Struct *reply = (AltCurrencySelectItemReply_Struct*)outapp->pBuffer; - reply->unknown004 = 0xFF; - reply->unknown005 = 0xFF; - reply->unknown006 = 0xFF; - reply->unknown007 = 0xFF; - strcpy(reply->item_name, inst->GetItem()->Name); - reply->cost = cost; - FastQueuePacket(&outapp); } } -void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyPurchase, app, AltCurrencyPurchaseItem_Struct); - AltCurrencyPurchaseItem_Struct *purchase = (AltCurrencyPurchaseItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(purchase->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; +void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } - - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } - - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; - } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - - if(item->ID == purchase->item_id) { //This check to make sure that the item is actually on the NPC, people attempt to inject packets to get items summoned... - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if (!item || !found) { - Message(13, "Error: The item you purchased does not exist!"); - return; - } - - if(cost > current_currency) { - Message(13, "You cannot afford that item right now."); - return; - } - - if(CheckLoreConflict(item)) - { - Message(15,"You can only have one of a lore item."); - return; - } - - /* QS: PlayerLogAlternateCurrencyTransactions :: Merchant Purchase */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Merchant Purchase :: Spent alt_currency_id:%i cost:%i for itemid:%i in zoneid:%i instid:%i", alt_cur_id, cost, item->ID, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); - } - - AddAlternateCurrencyValue(alt_cur_id, -((int32)cost)); - int16 charges = 1; - if(item->MaxCharges != 0) - charges = item->MaxCharges; - - ItemInst *inst = database.CreateItem(item, charges); - if(!AutoPutLootInInventory(*inst, true, true)) - { - PutLootInInventory(MainCursor, *inst); - } - - Save(1); - } -} - -void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencyReclaim, app, AltCurrencyReclaim_Struct); - AltCurrencyReclaim_Struct *reclaim = (AltCurrencyReclaim_Struct*)app->pBuffer; - uint32 item_id = 0; - std::list::iterator iter = zone->AlternateCurrencies.begin(); - while(iter != zone->AlternateCurrencies.end()) { - if((*iter).id == reclaim->currency_id) { - item_id = (*iter).item_id; - } - ++iter; - } - - if(item_id == 0) { + if (app->size != sizeof(GuildDemoteStruct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildDemoteStruct)); return; } - /* Item to Currency Storage */ - if(reclaim->reclaim_flag == 1) { - uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); - if(removed > 0) { - AddAlternateCurrencyValue(reclaim->currency_id, removed); + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) + Message(0, "You dont have permission to invite."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + GuildDemoteStruct* demote = (GuildDemoteStruct*)app->pBuffer; - /* QS: PlayerLogAlternateCurrencyTransactions :: Item to Currency */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Item to Currency :: alt_currency_id:%i amount:%i to currency tab in zoneid:%i instid:%i", reclaim->currency_id, removed, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(demote->target, gci)) { + Message(0, "Unable to find '%s'", demote->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + if (gci.rank < 1) { + Message(0, "%s cannot be demoted any further!", demote->target); + return; + } + uint8 rank = gci.rank - 1; + + + mlog(GUILDS__ACTIONS, "Demoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + demote->target, gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, + guild_mgr.GetRankName(GuildID(), rank), rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { + Message(13, "Error while setting rank %d on '%s'.", rank, demote->target); + return; + } + Message(0, "Successfully demoted %s to rank %d", demote->target, rank); + } + // SendGuildMembers(GuildID(), true); + return; +} + +void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildInvite"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildCommand_Struct)) { + std::cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; + return; + } + + GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; + + if (!IsInAGuild()) + Message(0, "Error: You are not in a guild!"); + else if (gc->officer > GUILD_MAX_RANK) + Message(13, "Invalid rank."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + + //ok, the invite is also used for changing rank as well. + Mob* invitee = entity_list.GetMob(gc->othername); + + if (!invitee) { + Message(13, "Prospective guild member %s must be in zone to preform guild operations on them.", gc->othername); + return; + } + + if (invitee->IsClient()) { + Client* client = invitee->CastToClient(); + + //ok, figure out what they are trying to do. + if (client->GuildID() == GuildID()) { + //they are already in this guild, must be a promotion or demotion + if (gc->officer < client->GuildRank()) { + //demotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_DEMOTE)) { + Message(13, "You dont have permission to demote."); + return; + } + + //we could send this to the member and prompt them to see if they want to + //be demoted (I guess), but I dont see a point in that. + + mlog(GUILDS__ACTIONS, "%s (%d) is demoting %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + gc->officer, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(client->CharacterID(), gc->officer)) { + Message(13, "There was an error during the demotion, DB may now be inconsistent."); + return; + } + + } + else if (gc->officer > client->GuildRank()) { + //promotion + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) { + Message(13, "You dont have permission to demote."); + return; + } + + mlog(GUILDS__ACTIONS, "%s (%d) is asking to promote %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + client->GetName(), client->CharacterID(), + gc->officer, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + //record the promotion with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); + + if (gc->guildeqid == 0) + gc->guildeqid = GuildID(); + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for promotion to %s, length %d", client->GetName(), app->size); + mpkt(GUILDS__OUT_PACKET_TRACE, app); + client->QueuePacket(app); + + } + else { + Message(13, "That member is already that rank."); + return; + } + } + else if (!client->IsInAGuild()) { + //they are not in this or any other guild, this is an invite + // + if (client->GetPendingGuildInvitation()) + { + Message(13, "That person is already considering a guild invitation."); + return; + } + + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_INVITE)) { + Message(13, "You dont have permission to invite."); + return; + } + + mlog(GUILDS__ACTIONS, "Inviting %s (%d) into guild %s (%d)", + client->GetName(), client->CharacterID(), + guild_mgr.GetGuildName(GuildID()), GuildID()); + + //record the invite with guild manager so we know its valid when we get the reply + guild_mgr.RecordInvite(client->CharacterID(), GuildID(), gc->officer); + + if (gc->guildeqid == 0) + gc->guildeqid = GuildID(); + + mlog(GUILDS__OUT_PACKETS, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size); + mpkt(GUILDS__OUT_PACKET_TRACE, app); + client->SetPendingGuildInvitation(true); + client->QueuePacket(app); + + } + else { + //they are in some other guild + Message(13, "Player is in a guild."); + return; } } +#ifdef BOTS + else if (invitee->IsBot()) { + // The guild system is too tightly coupled with the character_data table so we have to avoid using much of the system + Bot::ProcessGuildInvite(this, invitee->CastToBot()); + return; + } +#endif } - /* Cursor to Item storage */ - else { - uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); +} - /* If you input more than you have currency wise, just give the max of the currency you currently have */ - if(reclaim->count > max_currency) { - SummonItem(item_id, max_currency); - SetAlternateCurrencyValue(reclaim->currency_id, 0); +void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildInviteAccept"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + SetPendingGuildInvitation(false); + + if (app->size != sizeof(GuildInviteAccept_Struct)) { + std::cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildJoin_Struct) << std::endl; + return; + } + + GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer; + + if (GetClientVersion() >= EQClientRoF) + { + if (gj->response > 9) + { + //dont care if the check fails (since we dont know the rank), just want to clear the entry. + guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); + return; + } + } + if (gj->response == 5 || gj->response == 4) { + //dont care if the check fails (since we dont know the rank), just want to clear the entry. + guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response); + + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has declined to join the guild.", this->GetName()); + + return; + } + + //uint32 tmpeq = gj->guildeqid; + if (IsInAGuild() && gj->response == GuildRank()) + Message(0, "Error: You're already in a guild!"); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + mlog(GUILDS__ACTIONS, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s", + gj->guildeqid, gj->response, gj->inviter, gj->newmember); + + //we dont really care a lot about what this packet means, as long as + //it has been authorized with the guild manager + if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) { + worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName()); + Message(13, "Invalid invite response packet!"); + return; + } + + if (gj->guildeqid == GuildID()) { + //only need to change rank. + + mlog(GUILDS__ACTIONS, "Changing guild rank of %s (%d) to rank %d in guild %s (%d)", + GetName(), CharacterID(), + gj->response, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(CharacterID(), gj->response)) { + Message(13, "There was an error during the rank change, DB may now be inconsistent."); + return; + } } else { - SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, false, MainCursor); - AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count)); - } - /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Reclaim :: Cursor to Item :: alt_currency_id:%i amount:-%i in zoneid:%i instid:%i", reclaim->currency_id, reclaim->count, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + + mlog(GUILDS__ACTIONS, "Adding %s (%d) to guild %s (%d) at rank %d", + GetName(), CharacterID(), + guild_mgr.GetGuildName(gj->guildeqid), gj->guildeqid, + gj->response); + + //change guild and rank + + uint32 guildrank = gj->response; + + if (GetClientVersion() == EQClientRoF) + { + if (gj->response == 8) + { + guildrank = 0; + } + } + + if (!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, guildrank)) { + Message(13, "There was an error during the invite, DB may now be inconsistent."); + return; + } + if (zone->GetZoneID() == RuleI(World, GuildBankZoneID) && GuildBanks) + GuildBanks->SendGuildBank(this); } } } -void Client::Handle_OP_AltCurrencySell(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_AltCurrencySell, app, AltCurrencySellItem_Struct); - EQApplicationPacket *outapp = app->Copy(); - AltCurrencySellItem_Struct *sell = (AltCurrencySellItem_Struct*)outapp->pBuffer; +void Client::Handle_OP_GuildLeader(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; + if (app->size < 2) { + mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); + return; + } - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } + app->pBuffer[app->size - 1] = 0; + GuildMakeLeader* gml = (GuildMakeLeader*)app->pBuffer; + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (GuildRank() != GUILD_LEADER) + Message(0, "Error: You arent the guild leader!"); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { - return; - } + //NOTE: we could do cross-zone lookups here... - ItemInst* inst = GetInv().GetItem(sell->slot_id); - if(!inst) { - return; - } + Client* newleader = entity_list.GetClientByName(gml->target); + if (newleader) { - if (!RuleB(Merchant, EnableAltCurrencySell)) { - return; - } + mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", + guild_mgr.GetGuildName(GuildID()), GuildID(), + newleader->GetName(), newleader->CharacterID()); - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; - uint32 npc_id = tar->GetNPCTypeID(); - bool found = false; - std::list merlist = zone->merchanttable[merchant_id]; - std::list::const_iterator itr; - for(itr = merlist.begin(); itr != merlist.end(); ++itr) { - MerchantList ml = *itr; - if(GetLevel() < ml.level_required) { - continue; + if (guild_mgr.SetGuildLeader(GuildID(), newleader->CharacterID())){ + Message(0, "Successfully Transfered Leadership to %s.", gml->target); + newleader->Message(15, "%s has transfered the guild leadership into your hands.", GetName()); } - - int32 fac = tar->GetPrimaryFaction(); - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { - continue; - } - - item = database.GetItem(ml.item); - if(!item) - continue; - - if(item->ID == inst->GetItem()->ID) { - cost = ml.alt_currency_cost; - found = true; - break; - } - } - - if(!found) { - return; - } - - if(!inst->IsStackable()) - { - DeleteItemInInventory(sell->slot_id, 0, false); + else + Message(0, "Could not change leadership at this time."); } else - { - if(inst->GetCharges() < sell->charges) - { - sell->charges = inst->GetCharges(); - } + Message(0, "Failed to change leader, could not find target."); + } + // SendGuildMembers(GuildID(), true); + return; +} - if(sell->charges == 0) - { - Message(13, "Charge mismatch error."); +void Client::Handle_OP_GuildManageBanker(const EQApplicationPacket *app) +{ + + mlog(GUILDS__IN_PACKETS, "Got OP_GuildManageBanker of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + if (app->size != sizeof(GuildManageBanker_Struct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of OP_GuildManageBanker of %i\n", app->size, sizeof(GuildManageBanker_Struct)); + return; + } + GuildManageBanker_Struct* gmb = (GuildManageBanker_Struct*)app->pBuffer; + + if (!IsInAGuild()) { + Message(13, "Your not in a guild!"); + return; + } + + CharGuildInfo gci; + + if (!guild_mgr.GetCharInfo(gmb->member, gci)) + { + Message(0, "Unable to find '%s'", gmb->member); + return; + } + bool IsCurrentlyABanker = guild_mgr.GetBankerFlag(gci.char_id); + + bool IsCurrentlyAnAlt = guild_mgr.GetAltFlag(gci.char_id); + + bool NewBankerStatus = gmb->enabled & 0x01; + + bool NewAltStatus = gmb->enabled & 0x02; + + if ((IsCurrentlyABanker != NewBankerStatus) && !guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + { + Message(13, "Only the guild leader can assign guild bankers!"); + return; + } + + if (IsCurrentlyAnAlt != NewAltStatus) + { + bool IsAllowed = !strncasecmp(GetName(), gmb->member, strlen(GetName())) || (GuildRank() >= GUILD_OFFICER); + + if (!IsAllowed) + { + Message(13, "You are not allowed to change the alt status of %s", gmb->member); + return; + } + } + + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + if (IsCurrentlyABanker != NewBankerStatus) + { + if (!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { + Message(13, "Error setting guild banker flag."); + return; + } + + if (NewBankerStatus) + Message(0, "%s has been made a guild banker.", gmb->member); + else + Message(0, "%s is no longer a guild banker.", gmb->member); + } + if (IsCurrentlyAnAlt != NewAltStatus) + { + if (!guild_mgr.SetAltFlag(gci.char_id, NewAltStatus)) { + Message(13, "Error setting guild alt flag."); + return; + } + + if (NewAltStatus) + Message(0, "%s has been marked as an alt.", gmb->member); + else + Message(0, "%s is no longer marked as an alt.", gmb->member); + } +} + +void Client::Handle_OP_GuildPeace(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Got OP_GuildPeace of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + return; +} + +void Client::Handle_OP_GuildPromote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildPromote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildPromoteStruct)) { + mlog(GUILDS__ERROR, "Error: app size of %i != size of GuildDemoteStruct of %i\n", app->size, sizeof(GuildPromoteStruct)); + return; + } + + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_PROMOTE)) + Message(0, "You dont have permission to invite."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + GuildPromoteStruct* promote = (GuildPromoteStruct*)app->pBuffer; + + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(promote->target, gci)) { + Message(0, "Unable to find '%s'", promote->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + uint8 rank = gci.rank + 1; + + if (rank > GUILD_OFFICER) + return; + + + mlog(GUILDS__ACTIONS, "Promoting %s (%d) from rank %s (%d) to %s (%d) in %s (%d)", + promote->target, gci.char_id, + guild_mgr.GetRankName(GuildID(), gci.rank), gci.rank, + guild_mgr.GetRankName(GuildID(), rank), rank, + guild_mgr.GetGuildName(GuildID()), GuildID()); + + if (!guild_mgr.SetGuildRank(gci.char_id, rank)) { + Message(13, "Error while setting rank %d on '%s'.", rank, promote->target); + return; + } + Message(0, "Successfully promoted %s to rank %d", promote->target, rank); + } + return; +} + +void Client::Handle_OP_GuildPublicNote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildPublicNote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size < sizeof(GuildUpdate_PublicNote)) { + // client calls for a motd on login even if they arent in a guild + printf("Error: app size of %i < size of OP_GuildPublicNote of %zu\n", app->size, sizeof(GuildUpdate_PublicNote)); + return; + } + GuildUpdate_PublicNote* gpn = (GuildUpdate_PublicNote*)app->pBuffer; + + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(gpn->target, gci)) { + Message(0, "Unable to find '%s'", gpn->target); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + + mlog(GUILDS__ACTIONS, "Setting public note on %s (%d) in guild %s (%d) to: %s", + gpn->target, gci.char_id, + guild_mgr.GetGuildName(GuildID()), GuildID(), + gpn->note); + + if (!guild_mgr.SetPublicNote(gci.char_id, gpn->note)) { + Message(13, "Failed to set public note on %s", gpn->target); + } + else { + Message(0, "Successfully changed public note on %s", gpn->target); + } + // SendGuildMembers(GuildID(), true); + return; +} + +void Client::Handle_OP_GuildRemove(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildRemove"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildCommand_Struct)) { + std::cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << std::endl; + return; + } + GuildCommand_Struct* gc = (GuildCommand_Struct*)app->pBuffer; + if (!IsInAGuild()) + Message(0, "Error: You arent in a guild!"); + // we can always remove ourself, otherwise, our rank needs remove permissions + else if (strcasecmp(gc->othername, GetName()) != 0 && + !guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_REMOVE)) + Message(0, "You dont have permission to remove guild members."); + else if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { +#ifdef BOTS + if (Bot::ProcessGuildRemoval(this, gc->othername)) + return; +#endif + uint32 char_id; + Client* client = entity_list.GetClientByName(gc->othername); + + if (client) { + if (!client->IsInGuild(GuildID())) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); return; } + char_id = client->CharacterID(); - DeleteItemInInventory(sell->slot_id, sell->charges, false); - cost *= sell->charges; + mlog(GUILDS__ACTIONS, "Removing %s (%d) from guild %s (%d)", + client->GetName(), client->CharacterID(), + guild_mgr.GetGuildName(GuildID()), GuildID()); + } + else { + CharGuildInfo gci; + if (!guild_mgr.GetCharInfo(gc->othername, gci)) { + Message(0, "Unable to find '%s'", gc->othername); + return; + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } + char_id = gci.char_id; + + mlog(GUILDS__ACTIONS, "Removing remote/offline %s (%d) into guild %s (%d)", + gci.char_name.c_str(), gci.char_id, + guild_mgr.GetGuildName(GuildID()), GuildID()); } - sell->cost = cost; - - /* QS: PlayerLogAlternateCurrencyTransactions :: Sold to Merchant*/ - if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ - std::string event_desc = StringFormat("Sold to Merchant :: itemid:%u npcid:%u alt_currency_id:%u cost:%u in zoneid:%u instid:%i", item->ID, npc_id, alt_cur_id, cost, this->GetZoneID(), this->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + if (!guild_mgr.SetGuild(char_id, GUILD_NONE, 0)) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_GuildManageRemove, sizeof(GuildManageRemove_Struct)); + GuildManageRemove_Struct* gm = (GuildManageRemove_Struct*)outapp->pBuffer; + gm->guildeqid = GuildID(); + strcpy(gm->member, gc->othername); + Message(0, "%s successfully removed from your guild.", gc->othername); + entity_list.QueueClientsGuild(this, outapp, false, GuildID()); + safe_delete(outapp); } + else + Message(0, "Unable to remove %s from your guild.", gc->othername); + } + // SendGuildMembers(GuildID(), true); + return; +} +void Client::Handle_OP_GuildStatus(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildStatus_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildStatus expected %i got %i", + sizeof(GuildStatus_Struct), app->size); + + DumpPacket(app); + + return; + } + GuildStatus_Struct *gss = (GuildStatus_Struct*)app->pBuffer; + + Client *c = entity_list.GetClientByName(gss->Name); + + if (!c) + { + Message_StringID(clientMessageWhite, TARGET_PLAYER_FOR_GUILD_STATUS); + return; + } + + uint32 TargetGuildID = c->GuildID(); + + if (TargetGuildID == GUILD_NONE) + { + Message_StringID(clientMessageWhite, NOT_IN_A_GUILD, c->GetName()); + return; + } + + const char *GuildName = guild_mgr.GetGuildName(TargetGuildID); + + if (!GuildName) + return; + + bool IsLeader = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_PROMOTE); + bool IsOfficer = guild_mgr.CheckPermission(TargetGuildID, c->GuildRank(), GUILD_INVITE); + + if ((TargetGuildID == GuildID()) && (c != this)) + { + if (IsLeader) + Message_StringID(clientMessageWhite, LEADER_OF_YOUR_GUILD, c->GetName()); + else if (IsOfficer) + Message_StringID(clientMessageWhite, OFFICER_OF_YOUR_GUILD, c->GetName()); + else + Message_StringID(clientMessageWhite, MEMBER_OF_YOUR_GUILD, c->GetName()); + + return; + } + + if (IsLeader) + Message_StringID(clientMessageWhite, LEADER_OF_X_GUILD, c->GetName(), GuildName); + else if (IsOfficer) + Message_StringID(clientMessageWhite, OFFICER_OF_X_GUILD, c->GetName(), GuildName); + else + Message_StringID(clientMessageWhite, MEMBER_OF_X_GUILD, c->GetName(), GuildName); +} + +void Client::Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app) +{ + if (app->size != sizeof(GuildUpdateURLAndChannel_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_GuildUpdateURLAndChannel expected %i got %i", + sizeof(GuildUpdateURLAndChannel_Struct), app->size); + + DumpPacket(app); + + return; + } + + GuildUpdateURLAndChannel_Struct *guuacs = (GuildUpdateURLAndChannel_Struct*)app->pBuffer; + + if (!IsInAGuild()) + return; + + if (!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) + { + Message(13, "Only the guild leader can change the Channel or URL.!"); + return; + } + + if (guuacs->Action == 0) + guild_mgr.SetGuildURL(GuildID(), guuacs->Text); + else + guild_mgr.SetGuildChannel(GuildID(), guuacs->Text); + +} + +void Client::Handle_OP_GuildWar(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Got OP_GuildWar of len %d", app->size); + mpkt(GUILDS__IN_PACKET_TRACE, app); + return; +} + +void Client::Handle_OP_Heartbeat(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_Hide(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) + { + //Can not be able to train hide but still have it from racial though + return; //You cannot hide if you do not have hide + } + + if (!p_timers.Expired(&database, pTimerHide, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = HideReuseTime - GetAA(209); + p_timers.Start(pTimerHide, reuse - 1); + + float hidechance = ((GetSkill(SkillHide) / 250.0f) + .25) * 100; + float random = MakeRandomFloat(0, 100); + CheckIncreaseSkill(SkillHide, nullptr, 5); + if (random < hidechance) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 1; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + if (GetAA(aaShroudofStealth)){ + improved_hidden = true; + hidden = true; + } + else + hidden = true; + } + if (GetClass() == ROGUE){ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_SimpleMessage, sizeof(SimpleMessage_Struct)); + SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; + msg->color = 0x010E; + Mob *evadetar = GetTarget(); + if (!auto_attack && (evadetar && evadetar->CheckAggro(this) + && evadetar->IsNPC())) { + if (MakeRandomInt(0, 260) < (int)GetSkill(SkillHide)) { + msg->string_id = EVADE_SUCCESS; + RogueEvade(evadetar); + } + else { + msg->string_id = EVADE_FAIL; + } + } + else { + if (hidden){ + msg->string_id = HIDE_SUCCESS; + } + else { + msg->string_id = HIDE_FAIL; + } + } FastQueuePacket(&outapp); - AddAlternateCurrencyValue(alt_cur_id, cost); - Save(1); } + return; } -void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app) { - uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor); - if((ebon + radiant) > 0) { - AddCrystals(radiant, ebon); - } -} - -void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app) { - VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct); - CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer; - - if(cr->type == 5) { - if(cr->amount > GetEbonCrystals()) { - SummonItem(RuleI(Zone, EbonCrystalItemID), GetEbonCrystals()); - m_pp.currentEbonCrystals = 0; - m_pp.careerEbonCrystals = 0; - Save(); - SendCrystalCounts(); - } else { - SummonItem(RuleI(Zone, EbonCrystalItemID), cr->amount); - m_pp.currentEbonCrystals -= cr->amount; - m_pp.careerEbonCrystals -= cr->amount; - Save(); - SendCrystalCounts(); - } - } else if(cr->type == 4) { - if(cr->amount > GetRadiantCrystals()) { - SummonItem(RuleI(Zone, RadiantCrystalItemID), GetRadiantCrystals()); - m_pp.currentRadCrystals = 0; - m_pp.careerRadCrystals = 0; - Save(); - SendCrystalCounts(); - } else { - SummonItem(RuleI(Zone, RadiantCrystalItemID), cr->amount); - m_pp.currentRadCrystals -= cr->amount; - m_pp.careerRadCrystals -= cr->amount; - Save(); - SendCrystalCounts(); - } - } -} - -void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) +void Client::Handle_OP_HideCorpse(const EQApplicationPacket *app) { - if(app->size < 4) + // New OPCode for SOD+ as /hidecorpse is handled serverside now. + // + if (app->size != sizeof(HideCorpse_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_HideCorpse expected %i got %i", + sizeof(HideCorpse_Struct), app->size); + + DumpPacket(app); + + return; + } + + HideCorpse_Struct *hcs = (HideCorpse_Struct*)app->pBuffer; + + if (hcs->Action == HideCorpseLooted) return; - uint32 Command = *((uint32 *) app->pBuffer); + if ((HideCorpseMode == HideCorpseNone) && (hcs->Action == HideCorpseNone)) + return; - switch(Command) - { - case 0: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); - LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; + entity_list.HideCorpses(this, HideCorpseMode, hcs->Action); -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(pts->Comment) > 256) -#else - if(strnlen(pts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if(strnlen(pts->Comment, 256) > 256) -#endif // DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo); - pack->WriteUInt32(GetBaseClass()); - pack->WriteUInt32(GetLevel()); - pack->WriteUInt32(GetAAPointsSpent()); - pack->WriteString(pts->Comment); - pack->WriteUInt32(pts->Toggle); - pack->WriteUInt32(pts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 1: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); - LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; - -#ifdef DARWIN -#if __DARWIN_C_LEVEL < 200809L - if (strlen(gts->Comment) > 256) -#else - if(strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN_C_LEVEL -#else - if(strnlen(gts->Comment, 256) > 256) -#endif // __DARWIN - return; - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_UpdateGuildInfo); - pack->WriteString(guild_mgr.GetGuildName(GuildID())); - pack->WriteString(gts->Comment); - pack->WriteUInt32(gts->FromLevel); - pack->WriteUInt32(gts->ToLevel); - pack->WriteUInt32(gts->Classes); - pack->WriteUInt32(gts->AACount); - pack->WriteUInt32(gts->Toggle); - pack->WriteUInt32(gts->TimeZone); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 3: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchPlayer_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 37); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_PlayerMatches); - - LFGuild_SearchPlayer_Struct *sps = (LFGuild_SearchPlayer_Struct *)app->pBuffer; - pack->WriteUInt32(sps->FromLevel); - pack->WriteUInt32(sps->ToLevel); - pack->WriteUInt32(sps->MinAA); - pack->WriteUInt32(sps->TimeZone); - pack->WriteUInt32(sps->Classes); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - case 4: - { - VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchGuild_Struct); - - ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 33); - - pack->WriteUInt32(zone->GetZoneID()); - pack->WriteUInt32(zone->GetInstanceID()); - pack->WriteString(GetName()); - pack->WriteUInt32(QSG_LFGuild); - pack->WriteUInt32(QSG_LFGuild_GuildMatches); - - LFGuild_SearchGuild_Struct *sgs = (LFGuild_SearchGuild_Struct *)app->pBuffer; - - pack->WriteUInt32(sgs->Level); - pack->WriteUInt32(sgs->AAPoints); - pack->WriteUInt32(sgs->TimeZone); - pack->WriteUInt32(sgs->Class); - - worldserver.SendPacket(pack); - safe_delete(pack); - - break; - } - default: - break; - } + HideCorpseMode = hcs->Action; } -void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) +void Client::Handle_OP_Ignore(const EQApplicationPacket *app) { - if(app->size < 12) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); + return; +} + +void Client::Handle_OP_Illusion(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Illusion_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized OP_Illusion: got %d, expected %d", app->size, + sizeof(Illusion_Struct)); DumpPacket(app); return; } - uint32 Unknown000 = app->ReadUInt32(0); - - if(Unknown000 != 1) - return; - - uint32 Slot = app->ReadUInt32(4); - - if(Slot >= XTARGET_HARDCAP) - return; - - XTargetType Type = (XTargetType)app->ReadUInt32(8); - - XTargets[Slot].Type = Type; - XTargets[Slot].ID = 0; - XTargets[Slot].Name[0] = 0; - - switch(Type) + if (!GetGM()) { - case Empty: - case Auto: - { - break; - } - - case CurrentTargetPC: - { - char Name[65]; - - app->ReadString(Name, 12, 64); - Client *c = entity_list.GetClientByName(Name); - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, Name, 64); - } - SendXTargetPacket(Slot, c); - - break; - } - - case CurrentTargetNPC: - { - char Name[65]; - app->ReadString(Name, 12, 64); - Mob *m = entity_list.GetMob(Name); - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - break; - } - } - - case TargetsTarget: - { - if(GetTarget()) - UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); - else - UpdateXTargetType(TargetsTarget, nullptr); - - break; - } - - case GroupTank: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetMainTankName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainTankName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - case GroupTankTarget: - { - Group *g = GetGroup(); - - if(g) - g->NotifyTankTarget(this); - - break; - } - - case GroupAssist: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetMainAssistName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetMainAssistName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case GroupAssistTarget: - { - - Group *g = GetGroup(); - - if(g) - g->NotifyAssistTarget(this); - - break; - } - - case Puller: - { - Group *g = GetGroup(); - - if(g) - { - Client *c = entity_list.GetClientByName(g->GetPullerName()); - - if(c) - { - XTargets[Slot].ID = c->GetID(); - strncpy(XTargets[Slot].Name, c->GetName(), 64); - } - else - { - strncpy(XTargets[Slot].Name, g->GetPullerName(), 64); - } - SendXTargetPacket(Slot, c); - } - break; - } - - case PullerTarget: - { - - Group *g = GetGroup(); - - if(g) - g->NotifyPullerTarget(this); - - break; - } - - case GroupMarkTarget1: - case GroupMarkTarget2: - case GroupMarkTarget3: - { - Group *g = GetGroup(); - - if(g) - g->SendMarkedNPCsToMember(this); - - break; - } - - case RaidAssist1: - case RaidAssist2: - case RaidAssist3: - case RaidAssist1Target: - case RaidAssist2Target: - case RaidAssist3Target: - case RaidMarkTarget1: - case RaidMarkTarget2: - case RaidMarkTarget3: - { - // Not implemented yet. - break; - } - - case MyPet: - { - Mob *m = GetPet(); - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - case MyPetTarget: - { - Mob *m = GetPet(); - - if(m) - m = m->GetTarget(); - - if(m) - { - XTargets[Slot].ID = m->GetID(); - SendXTargetPacket(Slot, m); - - } - break; - } - - default: - LogFile->write(EQEMuLog::Debug, "Unhandled XTarget Type %i", Type); - break; + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_Illusion sent by non Game Master.", zone->GetShortName()); + return; } + Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer; + //these need to be implemented + /* + texture = bnpc->texture; + helmtexture = bnpc->helmtexture; + luclinface = bnpc->luclinface; + */ + race = bnpc->race; + size = 0; + + entity_list.QueueClients(this, app); + return; } -void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) +void Client::Handle_OP_InspectAnswer(const EQApplicationPacket *app) { - if(app->size != 1) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); + + if (app->size != sizeof(InspectResponse_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectAnswer, size=%i, expected %i", app->size, sizeof(InspectResponse_Struct)); + return; + } + + //Fills the app sent from client. + EQApplicationPacket* outapp = app->Copy(); + InspectResponse_Struct* insr = (InspectResponse_Struct*)outapp->pBuffer; + Mob* tmp = entity_list.GetMob(insr->TargetID); + const Item_Struct* item = nullptr; + + for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { + const ItemInst* inst = GetInv().GetItem(L); + item = inst ? inst->GetItem() : nullptr; + + if (item) { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = item->Icon; + } + else { insr->itemicons[L] = 0xFFFFFFFF; } + } + + const ItemInst* inst = GetInv().GetItem(MainAmmo); + item = inst ? inst->GetItem() : nullptr; + + if (item) { + // another one..I did these, didn't I!!? + strcpy(insr->itemnames[SoF::slots::MainAmmo], item->Name); + insr->itemicons[SoF::slots::MainAmmo] = item->Icon; + } + else { insr->itemicons[SoF::slots::MainAmmo] = 0xFFFFFFFF; } + + InspectMessage_Struct* newmessage = (InspectMessage_Struct*)insr->text; + InspectMessage_Struct& playermessage = this->GetInspectMessage(); + memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); + + if (tmp != 0 && tmp->IsClient()) { tmp->CastToClient()->QueuePacket(outapp); } // Send answer to requester + + return; +} + +void Client::Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(InspectMessage_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectMessageUpdate, size=%i, expected %i", app->size, sizeof(InspectMessage_Struct)); + return; + } + + InspectMessage_Struct* newmessage = (InspectMessage_Struct*)app->pBuffer; + InspectMessage_Struct& playermessage = this->GetInspectMessage(); + memcpy(&playermessage, newmessage, sizeof(InspectMessage_Struct)); + database.SaveCharacterInspectMessage(this->CharacterID(), &playermessage); +} + +void Client::Handle_OP_InspectRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Inspect_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_InspectRequest, size=%i, expected %i", app->size, sizeof(Inspect_Struct)); + return; + } + + Inspect_Struct* ins = (Inspect_Struct*)app->pBuffer; + Mob* tmp = entity_list.GetMob(ins->TargetID); + + if (tmp != 0 && tmp->IsClient()) { + if (tmp->CastToClient()->GetClientVersion() < EQClientSoF) { tmp->CastToClient()->QueuePacket(app); } // Send request to target + // Inspecting an SoF or later client will make the server handle the request + else { ProcessInspectRequest(tmp->CastToClient(), this); } + } + +#ifdef BOTS + if (tmp != 0 && tmp->IsBot()) { Bot::ProcessBotInspectionRequest(tmp->CastToBot(), this); } +#endif + + return; +} + +void Client::Handle_OP_InstillDoubt(const EQApplicationPacket *app) +{ + //packet is empty as of 12/14/04 + + if (!p_timers.Expired(&database, pTimerInstillDoubt, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerInstillDoubt, InstillDoubtReuseTime - 1); + + InstillDoubt(GetTarget()); + return; +} + +void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemViewRequest_Struct)){ + LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick. Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct)); DumpPacket(app); return; } - XTargetAutoAddHaters = app->ReadUInt8(0); + DumpPacket(app); + ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer; + + //todo: verify ivrs->link_hash based on a rule, in case we don't care about people being able to sniff data from the item DB + + const Item_Struct* item = database.GetItem(ivrs->item_id); + if (!item) { + if (ivrs->item_id > 500000) + { + std::string response = ""; + int sayid = ivrs->item_id - 500000; + bool silentsaylink = false; + + if (sayid > 250000) //Silent Saylink + { + sayid = sayid - 250000; + silentsaylink = true; + } + + if (sayid > 0) + { + + std::string query = StringFormat("SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); + return; + } + + if (results.RowCount() != 1) { + Message(13, "Error: The saylink (%s) was not found in the database.", response.c_str()); + return; + } + + auto row = results.begin(); + response = row[0]; + + } + + if ((response).size() > 0) + { + if (!mod_saylink(response, silentsaylink)) { return; } + + if (GetTarget() && GetTarget()->IsNPC()) + { + if (silentsaylink) + { + parse->EventNPC(EVENT_SAY, GetTarget()->CastToNPC(), this, response.c_str(), 0); + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } + else + { + Message(7, "You say, '%s'", response.c_str()); + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + return; + } + else + { + if (silentsaylink) + { + parse->EventPlayer(EVENT_SAY, this, response.c_str(), 0); + } + else + { + Message(7, "You say, '%s'", response.c_str()); + ChannelMessageReceived(8, 0, 100, response.c_str()); + } + return; + } + } + else + { + Message(13, "Error: Say Link not found or is too long."); + return; + } + } + else { + Message(13, "Error: The item for the link you have clicked on does not exist!"); + return; + } + + } + + ItemInst* inst = database.CreateItem(item, item->MaxCharges, ivrs->augments[0], ivrs->augments[1], ivrs->augments[2], ivrs->augments[3], ivrs->augments[4]); + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + return; +} + +void Client::Handle_OP_ItemLinkResponse(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LDONItemViewRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemLinkResponse expected:%i got:%i", sizeof(LDONItemViewRequest_Struct), app->size); + return; + } + LDONItemViewRequest_Struct* item = (LDONItemViewRequest_Struct*)app->pBuffer; + ItemInst* inst = database.CreateItem(item->item_id); + if (inst) { + SendItemPacket(0, inst, ItemPacketViewLink); + safe_delete(inst); + } + return; +} + +void Client::Handle_OP_ItemName(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemNamePacket_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for ItemNamePacket_Struct: Expected: %i, Got: %i", + sizeof(ItemNamePacket_Struct), app->size); + return; + } + ItemNamePacket_Struct *p = (ItemNamePacket_Struct*)app->pBuffer; + const Item_Struct *item = 0; + if ((item = database.GetItem(p->item_id)) != nullptr) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ItemName, sizeof(ItemNamePacket_Struct)); + p = (ItemNamePacket_Struct*)outapp->pBuffer; + memset(p, 0, sizeof(ItemNamePacket_Struct)); + strcpy(p->name, item->Name); + FastQueuePacket(&outapp); + } + return; } void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) @@ -13676,14 +8566,983 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp); - } else + } + else return; } +void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ItemVerifyRequest_Struct)) + { + LogFile->write(EQEMuLog::Error, "OP size error: OP_ItemVerifyRequest expected:%i got:%i", sizeof(ItemVerifyRequest_Struct), app->size); + return; + } + + ItemVerifyRequest_Struct* request = (ItemVerifyRequest_Struct*)app->pBuffer; + int32 slot_id; + int32 target_id; + int32 spell_id = 0; + slot_id = request->slot; + target_id = request->target; + + + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_ItemVerifyReply, sizeof(ItemVerifyReply_Struct)); + ItemVerifyReply_Struct* reply = (ItemVerifyReply_Struct*)outapp->pBuffer; + reply->slot = slot_id; + reply->target = target_id; + + QueuePacket(outapp); + safe_delete(outapp); + + + if (IsAIControlled()) { + this->Message_StringID(13, NOT_IN_CONTROL); + return; + } + + if (slot_id < 0) { + LogFile->write(EQEMuLog::Debug, "Unknown slot being used by %s, slot being used is: %i", GetName(), request->slot); + return; + } + + const ItemInst* inst = m_inv[slot_id]; + if (!inst) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id, 0, true); + return; + } + + const Item_Struct* item = inst->GetItem(); + if (!item) { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + DeleteItemInInventory(slot_id, 0, true); + return; + } + + spell_id = item->Click.Effect; + + if + ( + spell_id > 0 && + ( + !IsValidSpell(spell_id) || + casting_spell_id || + delaytimer || + spellend_timer.Enabled() || + IsStunned() || + IsFeared() || + IsMezzed() || + DivineAura() || + (IsSilenced() && !IsDiscipline(spell_id)) || + (IsAmnesiad() && IsDiscipline(spell_id)) || + (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) + ) + ) + { + SendSpellBarEnable(spell_id); + return; + } + + LogFile->write(EQEMuLog::Debug, "OP ItemVerifyRequest: spell=%i, target=%i, inv=%i", spell_id, target_id, slot_id); + + if (m_inv.SupportsClickCasting(slot_id) || ((item->ItemType == ItemTypePotion || item->PotionBelt) && m_inv.SupportsPotionBeltCasting(slot_id))) // sanity check + { + ItemInst* p_inst = (ItemInst*)inst; + + parse->EventItem(EVENT_ITEM_CLICK, this, p_inst, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + int r; + bool tryaug = false; + ItemInst* clickaug = 0; + Item_Struct* augitem = 0; + + for (r = 0; r < EmuConstants::ITEM_COMMON_SIZE; r++) { + const ItemInst* aug_i = inst->GetAugment(r); + if (!aug_i) + continue; + const Item_Struct* aug = aug_i->GetItem(); + if (!aug) + continue; + + if ((aug->Click.Type == ET_ClickEffect) || (aug->Click.Type == ET_Expendable) || (aug->Click.Type == ET_EquipClick) || (aug->Click.Type == ET_ClickEffect2)) + { + tryaug = true; + clickaug = (ItemInst*)aug_i; + augitem = (Item_Struct*)aug; + spell_id = aug->Click.Effect; + break; + } + } + + if ((spell_id <= 0) && (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol && item->ItemType != ItemTypeSpell)) + { + LogFile->write(EQEMuLog::Debug, "Item with no effect right clicked by %s", GetName()); + } + else if (inst->IsType(ItemClassCommon)) + { + if (item->ItemType == ItemTypeSpell && (strstr((const char*)item->Name, "Tome of ") || strstr((const char*)item->Name, "Skill: "))) + { + DeleteItemInInventory(slot_id, 1, true); + TrainDiscipline(item->ID); + } + else if (item->ItemType == ItemTypeSpell) + { + return; + } + else if ((item->Click.Type == ET_ClickEffect) || (item->Click.Type == ET_Expendable) || (item->Click.Type == ET_EquipClick) || (item->Click.Type == ET_ClickEffect2)) + { + if (inst->GetCharges() == 0) + { + //Message(0, "This item is out of charges."); + Message_StringID(13, ITEM_OUT_OF_CHARGES); + return; + } + if (GetLevel() >= item->Click.Level2) + { + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, p_inst, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + if (i == 0) { + CastSpell(item->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, item->CastTime, 0, 0, slot_id); + } + } + else + { + Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); + return; + } + } + else if (tryaug) + { + if (clickaug->GetCharges() == 0) + { + //Message(0, "This item is out of charges."); + Message_StringID(13, ITEM_OUT_OF_CHARGES); + return; + } + if (GetLevel() >= augitem->Click.Level2) + { + int i = parse->EventItem(EVENT_ITEM_CLICK_CAST, this, clickaug, nullptr, "", slot_id); + inst = m_inv[slot_id]; + if (!inst) + { + return; + } + + if (i == 0) { + CastSpell(augitem->Click.Effect, target_id, USE_ITEM_SPELL_SLOT, augitem->CastTime, 0, 0, slot_id); + } + } + else + { + Message_StringID(13, ITEMS_INSUFFICIENT_LEVEL); + return; + } + } + else + { + if (GetClientVersion() >= EQClientSoD && !inst->IsEquipable(GetBaseRace(), GetClass())) + { + if (item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink && item->ItemType != ItemTypeAlcohol) + { + LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); + } + else + { + //This is food/drink - consume it + if (item->ItemType == ItemTypeFood && m_pp.hunger_level < 5000) + { + Consume(item, item->ItemType, slot_id, false); + } + else if (item->ItemType == ItemTypeDrink && m_pp.thirst_level < 5000) + { + Consume(item, item->ItemType, slot_id, false); + } + else if (item->ItemType == ItemTypeAlcohol) + { +#if EQDEBUG >= 1 + LogFile->write(EQEMuLog::Debug, "Drinking Alcohol from slot:%i", slot_id); +#endif + // This Seems to be handled in OP_DeleteItem handling + //DeleteItemInInventory(slot_id, 1, false); + //entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name); + //Should add intoxication level to the PP at some point + //CheckIncreaseSkill(ALCOHOL_TOLERANCE, nullptr, 25); + } + + if (m_pp.hunger_level > 6000) + m_pp.hunger_level = 6000; + if (m_pp.thirst_level > 6000) + m_pp.thirst_level = 6000; + + EQApplicationPacket *outapp2; + outapp2 = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct)); + Stamina_Struct* sta = (Stamina_Struct*)outapp2->pBuffer; + sta->food = m_pp.hunger_level; + sta->water = m_pp.thirst_level; + + QueuePacket(outapp2); + safe_delete(outapp2); + } + + } + else + { + LogFile->write(EQEMuLog::Debug, "Error: unknown item->Click.Type (%i)", item->Click.Type); + } + } + } + else + { + Message(0, "Error: item not found in inventory slot #%i", slot_id); + } + } + else + { + Message(0, "Error: Invalid inventory slot for using effects (inventory slot #%i)", slot_id); + } + + return; +} + +void Client::Handle_OP_Jump(const EQApplicationPacket *app) +{ + SetEndurance(GetEndurance() - (GetLevel()<20 ? (225 * GetLevel() / 100) : 50)); + return; +} + +void Client::Handle_OP_KeyRing(const EQApplicationPacket *app) +{ + KeyRingList(); +} + +void Client::Handle_OP_LDoNButton(const EQApplicationPacket *app) +{ + if (app->size < sizeof(bool)) + { + return; + } + + if (GetPendingAdventureCreate()) + { + return; + } + + if (IsOnAdventure()) + { + return; + } + + bool* p = (bool*)app->pBuffer; + if (*p == true) + { + ServerPacket *pack = new ServerPacket(ServerOP_AdventureRequestCreate, sizeof(ServerAdventureRequestCreate_Struct)+(64 * adv_requested_member_count)); + ServerAdventureRequestCreate_Struct *sac = (ServerAdventureRequestCreate_Struct*)pack->pBuffer; + strcpy(sac->leader, GetName()); + sac->id = adv_requested_id; + sac->theme = adv_requested_theme; + sac->member_count = adv_requested_member_count; + memcpy((pack->pBuffer + sizeof(ServerAdventureRequestCreate_Struct)), adv_requested_data, (64 * adv_requested_member_count)); + pack->Deflate(); + worldserver.SendPacket(pack); + delete pack; + PendingAdventureCreate(); + ClearPendingAdventureData(); + } + else + { + ClearPendingAdventureData(); + } +} + +void Client::Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillDisarmTraps)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNDisarm(target->CastToNPC(), GetSkill(SkillDisarmTraps), LDoNTypeMechanical); + } + else + Message(13, "You do not have the disarm trap skill."); + } +} + +void Client::Handle_OP_LDoNInspect(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target && target->GetClass() == LDON_TREASURE) + Message(15, "%s", target->GetCleanName()); +} + +void Client::Handle_OP_LDoNOpen(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target && target->IsNPC()) + HandleLDoNOpen(target->CastToNPC()); +} + +void Client::Handle_OP_LDoNPickLock(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillPickLock)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNPickLock(target->CastToNPC(), GetSkill(SkillPickLock), LDoNTypeMechanical); + } + else + Message(13, "You do not have the pick locks skill."); + } +} + +void Client::Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app) +{ + Mob * target = GetTarget(); + if (target->IsNPC()) + { + if (HasSkill(SkillSenseTraps)) + { + if (DistNoRootNoZ(*target) > RuleI(Adventure, LDoNTrapDistanceUse)) + { + Message(13, "%s is too far away.", target->GetCleanName()); + return; + } + HandleLDoNSenseTraps(target->CastToNPC(), GetSkill(SkillSenseTraps), LDoNTypeMechanical); + } + else + Message(13, "You do not have the sense traps skill."); + } +} + +void Client::Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app) +{ + if (app->size != 1) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + DumpPacket(app); + return; + } + uint8 *mode = (uint8 *)app->pBuffer; + if (*mode) { + m_pp.leadAAActive = 1; + Save(); + Message_StringID(clientMessageYellow, LEADERSHIP_EXP_ON); + } + else { + m_pp.leadAAActive = 0; + Save(); + Message_StringID(clientMessageYellow, LEADERSHIP_EXP_OFF); + } +} + +void Client::Handle_OP_LeaveAdventure(const EQApplicationPacket *app) +{ + if (!IsOnAdventure()) + { + return; + } + LeaveAdventure(); +} + +void Client::Handle_OP_LeaveBoat(const EQApplicationPacket *app) +{ + Mob* boat = entity_list.GetMob(this->BoatID); // find the mob corresponding to the boat id + if (boat) { + if ((boat->GetTarget() == this) && boat->GetHateAmount(this) == 0) // if the client somehow left while still controlling the boat (and the boat isn't attacking them) + boat->SetTarget(0); // fix it to stop later problems + } + this->BoatID = 0; + return; +} + +void Client::Handle_OP_LFGCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LFG_Struct)) { + std::cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << std::endl; + DumpPacket(app); + return; + } + + // Process incoming packet + LFG_Struct* lfg = (LFG_Struct*)app->pBuffer; + + switch (lfg->value & 0xFF) { + case 0: + if (LFG) { + database.SetLFG(CharacterID(), false); + LFG = false; + LFGComments[0] = '\0'; + } + break; + case 1: + if (!LFG) { + LFG = true; + database.SetLFG(CharacterID(), true); + } + LFGFromLevel = lfg->FromLevel; + LFGToLevel = lfg->ToLevel; + LFGMatchFilter = lfg->MatchFilter; + strcpy(LFGComments, lfg->Comments); + break; + default: + Message(0, "Error: unknown LFG value %i", lfg->value); + } + + UpdateWho(); + + // Issue outgoing packet to notify other clients + EQApplicationPacket* outapp = new EQApplicationPacket(OP_LFGAppearance, sizeof(LFG_Appearance_Struct)); + LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer; + lfga->spawn_id = this->GetID(); + lfga->lfg = (uint8)LFG; + + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFGGetMatchesRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFGGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFGGetMatchesRequest_Struct)); + DumpPacket(app); + return; + } + LFGGetMatchesRequest_Struct* gmrs = (LFGGetMatchesRequest_Struct*)app->pBuffer; + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_LFGMatches, sizeof(ServerLFGMatchesRequest_Struct)); + ServerLFGMatchesRequest_Struct* smrs = (ServerLFGMatchesRequest_Struct*)pack->pBuffer; + smrs->FromID = GetID(); + smrs->QuerierLevel = GetLevel(); + strcpy(smrs->FromName, GetName()); + smrs->FromLevel = gmrs->FromLevel; + smrs->ToLevel = gmrs->ToLevel; + smrs->Classes = gmrs->Classes; + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +void Client::Handle_OP_LFGuild(const EQApplicationPacket *app) +{ + if (app->size < 4) + return; + + uint32 Command = *((uint32 *)app->pBuffer); + + switch (Command) + { + case 0: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct); + LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer; + +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(pts->Comment) > 256) +#else + if (strnlen(pts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if (strnlen(pts->Comment, 256) > 256) +#endif // DARWIN + return; + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_UpdatePlayerInfo); + pack->WriteUInt32(GetBaseClass()); + pack->WriteUInt32(GetLevel()); + pack->WriteUInt32(GetAAPointsSpent()); + pack->WriteString(pts->Comment); + pack->WriteUInt32(pts->Toggle); + pack->WriteUInt32(pts->TimeZone); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 1: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct); + LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer; + +#ifdef DARWIN +#if __DARWIN_C_LEVEL < 200809L + if (strlen(gts->Comment) > 256) +#else + if (strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN_C_LEVEL +#else + if (strnlen(gts->Comment, 256) > 256) +#endif // __DARWIN + return; + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_UpdateGuildInfo); + pack->WriteString(guild_mgr.GetGuildName(GuildID())); + pack->WriteString(gts->Comment); + pack->WriteUInt32(gts->FromLevel); + pack->WriteUInt32(gts->ToLevel); + pack->WriteUInt32(gts->Classes); + pack->WriteUInt32(gts->AACount); + pack->WriteUInt32(gts->Toggle); + pack->WriteUInt32(gts->TimeZone); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 3: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchPlayer_Struct); + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 37); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_PlayerMatches); + + LFGuild_SearchPlayer_Struct *sps = (LFGuild_SearchPlayer_Struct *)app->pBuffer; + pack->WriteUInt32(sps->FromLevel); + pack->WriteUInt32(sps->ToLevel); + pack->WriteUInt32(sps->MinAA); + pack->WriteUInt32(sps->TimeZone); + pack->WriteUInt32(sps->Classes); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + case 4: + { + VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_SearchGuild_Struct); + + ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + 33); + + pack->WriteUInt32(zone->GetZoneID()); + pack->WriteUInt32(zone->GetInstanceID()); + pack->WriteString(GetName()); + pack->WriteUInt32(QSG_LFGuild); + pack->WriteUInt32(QSG_LFGuild_GuildMatches); + + LFGuild_SearchGuild_Struct *sgs = (LFGuild_SearchGuild_Struct *)app->pBuffer; + + pack->WriteUInt32(sgs->Level); + pack->WriteUInt32(sgs->AAPoints); + pack->WriteUInt32(sgs->TimeZone); + pack->WriteUInt32(sgs->Class); + + worldserver.SendPacket(pack); + safe_delete(pack); + + break; + } + default: + break; + } +} + +void Client::Handle_OP_LFPCommand(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFP_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPCommand, size=%i, expected %i", app->size, sizeof(LFP_Struct)); + DumpPacket(app); + return; + } + LFP_Struct *lfp = (LFP_Struct*)app->pBuffer; + + LFP = lfp->Action != LFPOff; + database.SetLFP(CharacterID(), LFP); + + if (!LFP) { + worldserver.StopLFP(CharacterID()); + return; + } + + GroupLFPMemberEntry LFPMembers[MAX_GROUP_MEMBERS]; + + for (unsigned int i = 0; iGetZoneID(); + LFPMembers[0].GuildID = GuildID(); + + if (g) { + // This should not happen. The client checks if you are in a group and will not let you put LFP on if + // you are not the leader. + if (!g->IsLeader(this)) { + LogFile->write(EQEMuLog::Error, "Client sent LFP on for character %s who is grouped but not leader.", GetName()); + return; + } + // Fill the LFPMembers array with the rest of the group members, excluding ourself + // We don't fill in the class, level or zone, because we may not be able to determine + // them if the other group members are not in this zone. World will fill in this information + // for us, if it can. + int NextFreeSlot = 1; + for (unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (strcasecmp(g->membername[i], LFPMembers[0].Name)) + strcpy(LFPMembers[NextFreeSlot++].Name, g->membername[i]); + } + } + + + worldserver.UpdateLFP(CharacterID(), lfp->Action, lfp->MatchFilter, lfp->FromLevel, lfp->ToLevel, lfp->Classes, + lfp->Comments, LFPMembers); + + +} + +void Client::Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(LFPGetMatchesRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LFPGetMatchesRequest, size=%i, expected %i", app->size, sizeof(LFPGetMatchesRequest_Struct)); + DumpPacket(app); + return; + } + LFPGetMatchesRequest_Struct* gmrs = (LFPGetMatchesRequest_Struct*)app->pBuffer; + + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + ServerPacket* pack = new ServerPacket(ServerOP_LFPMatches, sizeof(ServerLFPMatchesRequest_Struct)); + ServerLFPMatchesRequest_Struct* smrs = (ServerLFPMatchesRequest_Struct*)pack->pBuffer; + smrs->FromID = GetID(); + smrs->FromLevel = gmrs->FromLevel; + smrs->ToLevel = gmrs->ToLevel; + smrs->QuerierLevel = GetLevel(); + smrs->QuerierClass = GetClass(); + strcpy(smrs->FromName, GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + } + + return; +} + +void Client::Handle_OP_LoadSpellSet(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LoadSpellSet_Struct)) { + printf("Wrong size of LoadSpellSet_Struct! Expected: %zu, Got: %i\n", sizeof(LoadSpellSet_Struct), app->size); + return; + } + int i; + LoadSpellSet_Struct* ss = (LoadSpellSet_Struct*)app->pBuffer; + for (i = 0; ispell[i] != 0xFFFFFFFF) + UnmemSpell(i, true); + } +} + +void Client::Handle_OP_Logout(const EQApplicationPacket *app) +{ + //LogFile->write(EQEMuLog::Debug, "%s sent a logout packet.", GetName()); + //we will save when we get destroyed soon anyhow + //Save(); + + SendLogoutPackets(); + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); + FastQueuePacket(&outapp); + + Disconnect(); + return; +} + +void Client::Handle_OP_LootItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(LootingItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct)); + return; + } + /* + ** fixed the looting code so that it sends the correct opcodes + ** and now correctly removes the looted item the player selected + ** as well as gives the player the proper item. + ** Also fixed a few UI lock ups that would occur. + */ + + EQApplicationPacket* outapp = 0; + Entity* entity = entity_list.GetID(*((uint16*)app->pBuffer)); + if (entity == 0) { + Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)"); + outapp = new EQApplicationPacket(OP_LootComplete, 0); + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + if (entity->IsCorpse()) { + entity->CastToCorpse()->LootItem(this, app); + return; + } + else { + Message(13, "Error: Corpse not found! (!ent->IsCorpse())"); + Corpse::SendEndLootErrorPacket(this); + } + + return; +} + +void Client::Handle_OP_LootRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + + SetLooting(true); + + Entity* ent = entity_list.GetID(*((uint32*)app->pBuffer)); + if (ent == 0) { + Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)"); + Corpse::SendLootReqErrorPacket(this); + return; + } + if (ent->IsCorpse()) + { + Corpse *ent_corpse = ent->CastToCorpse(); + if (DistNoRootNoZ(ent_corpse->GetX(), ent_corpse->GetY()) > 625) + { + Message(13, "Corpse too far away."); + Corpse::SendLootReqErrorPacket(this); + return; + } + + if (invisible) { + BuffFadeByEffect(SE_Invisibility); + BuffFadeByEffect(SE_Invisibility2); + invisible = false; + } + if (invisible_undead) { + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + invisible_undead = false; + } + if (invisible_animals){ + BuffFadeByEffect(SE_InvisVsAnimals); + invisible_animals = false; + } + if (hidden || improved_hidden){ + hidden = false; + improved_hidden = false; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 0; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + } + ent->CastToCorpse()->MakeLootRequestPackets(this, app); + return; + } + else { + std::cout << "npc == 0 LOOTING FOOKED3" << std::endl; + Message(13, "Error: OP_LootRequest: Corpse not a corpse?"); + Corpse::SendLootReqErrorPacket(this); + } + return; +} + +void Client::Handle_OP_ManaChange(const EQApplicationPacket *app) +{ + if (app->size == 0) { + // i think thats the sign to stop the songs + if (IsBardSong(casting_spell_id) || bardsong != 0) + InterruptSpell(SONG_ENDS, 0x121); + else + InterruptSpell(INTERRUPT_SPELL, 0x121); + + return; + } + else // I don't think the client sends proper manachanges + { // with a length, just the 0 len ones for stopping songs + //ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer; + printf("OP_ManaChange from client:\n"); + DumpPacket(app); + } + return; +} + +/* +#if 0 // I dont think there's an op for this now, and we check this +// when the client is sitting +void Client::Handle_OP_Medding(const EQApplicationPacket *app) +{ + if (app->pBuffer[0]) + medding = true; + else + medding = false; + return; +} +#endif +*/ + +void Client::Handle_OP_MemorizeSpell(const EQApplicationPacket *app) +{ + OPMemorizeSpell(app); + return; +} + +void Client::Handle_OP_Mend(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillMend)) + return; + + if (!p_timers.Expired(&database, pTimerMend, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerMend, MendReuseTime - 1); + + int mendhp = GetMaxHP() / 4; + int currenthp = GetHP(); + if (MakeRandomInt(0, 199) < (int)GetSkill(SkillMend)) { + + int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend; + + if (MakeRandomInt(0, 99) < criticalchance){ + mendhp *= 2; + Message_StringID(4, MEND_CRITICAL); + } + SetHP(GetHP() + mendhp); + SendHPUpdate(); + Message_StringID(4, MEND_SUCCESS); + } + else { + /* the purpose of the following is to make the chance to worsen wounds much less common, + which is more consistent with the way eq live works. + according to my math, this should result in the following probability: + 0 skill - 25% chance to worsen + 20 skill - 23% chance to worsen + 50 skill - 16% chance to worsen */ + if ((GetSkill(SkillMend) <= 75) && (MakeRandomInt(GetSkill(SkillMend), 100) < 75) && (MakeRandomInt(1, 3) == 1)) + { + SetHP(currenthp > mendhp ? (GetHP() - mendhp) : 1); + SendHPUpdate(); + Message_StringID(4, MEND_WORSEN); + } + else + Message_StringID(4, MEND_FAIL); + } + + CheckIncreaseSkill(SkillMend, nullptr, 10); + return; +} + +void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MercenaryCommand_Struct)) + { + Message(13, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); + DumpPacket(app); + return; + } + + MercenaryCommand_Struct* mc = (MercenaryCommand_Struct*)app->pBuffer; + uint32 merc_command = mc->MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 20 (unknown), 36 (zone in with merc) + int32 option = mc->Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); + + if (!RuleB(Mercs, AllowMercs)) + return; + + // Handle the Command here... + // Will need a list of what every type of command is supposed to do + // Unsure if there is a server response to this packet + if (option >= 0) + { + Merc* merc = GetMerc(); + GetMercInfo().State = option; + + if (merc) { + uint8 numStances = 0; + + //get number of available stances for the current merc + std::list mercStanceList = zone->merc_stance_list[merc->GetMercTemplateID()]; + std::list::iterator iter = mercStanceList.begin(); + while (iter != mercStanceList.end()) { + numStances++; + ++iter; + } + + MercTemplate* mercTemplate = zone->GetMercTemplate(GetMerc()->GetMercTemplateID()); + if (mercTemplate) { + + //check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance) + if (option >= 0 && option < numStances) { + merc->SetStance(mercTemplate->Stances[option]); + GetMercInfo().Stance = mercTemplate->Stances[option]; + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Set Stance: %u", merc->GetStance()); + } + } + } + } +} + void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) { // The payload is 4 bytes. The EntityID of the Mercenary Liason which are of class 71. - if(app->size != sizeof(MercenaryMerchantShopRequest_Struct)) + if (app->size != sizeof(MercenaryMerchantShopRequest_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataRequest expected 4 got %i", app->size); @@ -13692,41 +9551,41 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) return; } - MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*) app->pBuffer; + MercenaryMerchantShopRequest_Struct* mmsr = (MercenaryMerchantShopRequest_Struct*)app->pBuffer; uint32 merchant_id = mmsr->MercMerchantID; uint32 altCurrentType = 19; - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Data Request for Merchant ID (%i)", merchant_id); //client is requesting data about currently owned mercenary - if(merchant_id == 0) { + if (merchant_id == 0) { //send info about your current merc(s) } - if(!RuleB(Mercs, AllowMercs)) { + if (!RuleB(Mercs, AllowMercs)) { return; } NPC* tar = entity_list.GetNPCByID(merchant_id); - if(tar) { + if (tar) { int mercTypeCount = 0; int mercCount = 0; - if(DistNoRoot(*tar) > USE_NPC_RANGE2) + if (DistNoRoot(*tar) > USE_NPC_RANGE2) return; - if(tar->GetClass() != MERCERNARY_MASTER) { + if (tar->GetClass() != MERCERNARY_MASTER) { return; } mercTypeCount = tar->GetNumMercTypes(GetClientVersion()); mercCount = tar->GetNumMercs(GetClientVersion()); - if(mercCount > MAX_MERC) - return; + if (mercCount > MAX_MERC) + return; std::list mercTypeList = tar->GetMercTypesList(GetClientVersion()); std::list mercDataList = tar->GetMercsList(GetClientVersion()); @@ -13734,10 +9593,10 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) int i = 0; int StanceCount = 0; - for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) + for (std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) { std::list::iterator siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); - for(siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); siter != zone->merc_stance_list[mercListItr->MercTemplateID].end(); ++siter) + for (siter = zone->merc_stance_list[mercListItr->MercTemplateID].begin(); siter != zone->merc_stance_list[mercListItr->MercTemplateID].end(); ++siter) { StanceCount++; } @@ -13747,28 +9606,28 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; mml->MercTypeCount = mercTypeCount; - if(mercTypeCount > 0) + if (mercTypeCount > 0) { - for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { - mml->MercGrades[i] = mercTypeListItr->Type; // DBStringID for Type - i++; + for (std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { + mml->MercGrades[i] = mercTypeListItr->Type; // DBStringID for Type + i++; } } mml->MercCount = mercCount; - if(mercCount > 0) + if (mercCount > 0) { i = 0; - for(std::list::iterator mercListIter = mercDataList.begin(); mercListIter != mercDataList.end(); ++mercListIter) + for (std::list::iterator mercListIter = mercDataList.begin(); mercListIter != mercDataList.end(); ++mercListIter) { mml->Mercs[i].MercID = mercListIter->MercTemplateID; mml->Mercs[i].MercType = mercListIter->MercType; mml->Mercs[i].MercSubType = mercListIter->MercSubType; - mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), 0): 0; - mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), 0): 0; + mml->Mercs[i].PurchaseCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; + mml->Mercs[i].UpkeepCost = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), 0) : 0; mml->Mercs[i].Status = 0; - mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType): 0; - mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType): 0; + mml->Mercs[i].AltCurrencyCost = RuleB(Mercs, ChargeMercPurchaseCost) ? Merc::CalcPurchaseCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; + mml->Mercs[i].AltCurrencyUpkeep = RuleB(Mercs, ChargeMercUpkeepCost) ? Merc::CalcUpkeepCost(mercListIter->MercTemplateID, GetLevel(), altCurrentType) : 0; mml->Mercs[i].AltCurrencyType = altCurrentType; mml->Mercs[i].MercUnk01 = 0; mml->Mercs[i].TimeLeft = -1; @@ -13776,19 +9635,19 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) mml->Mercs[i].MercUnk02 = 1; int mercStanceCount = 0; std::list::iterator iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - for(iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); iter != zone->merc_stance_list[mercListIter->MercTemplateID].end(); ++iter) + for (iter = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); iter != zone->merc_stance_list[mercListIter->MercTemplateID].end(); ++iter) { - mercStanceCount++; + mercStanceCount++; } mml->Mercs[i].StanceCount = mercStanceCount; mml->Mercs[i].MercUnk03 = 519044964; mml->Mercs[i].MercUnk04 = 1; //mml->Mercs[i].MercName; int stanceindex = 0; - if(mercStanceCount > 0) + if (mercStanceCount > 0) { std::list::iterator iter2 = zone->merc_stance_list[mercListIter->MercTemplateID].begin(); - while(iter2 != zone->merc_stance_list[mercListIter->MercTemplateID].end()) + while (iter2 != zone->merc_stance_list[mercListIter->MercTemplateID].end()) { mml->Mercs[i].Stances[stanceindex].StanceIndex = stanceindex; mml->Mercs[i].Stances[stanceindex].Stance = (iter2->StanceID); @@ -13803,10 +9662,65 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) } } +void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) +{ + // The payload is 0 bytes. + if (app->size != 0) + { + Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Data Update Request Received."); + + if (GetMercID()) + { + SendMercPersonalInfo(); + } +} + +void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) +{ + // The payload is 0 or 1 bytes. + if (app->size > 1) + { + Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); + DumpPacket(app); + return; + } + + uint8 Command = 0; + if (app->size > 0) + { + char *InBuffer = (char *)app->pBuffer; + Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); + + // Handle the dismiss here... + if (GetMercID()) { + Merc* merc = GetMerc(); + + if (merc) { + if (CheckCanDismissMerc()) { + merc->Dismiss(); + } + } + } + + //SendMercMerchantResponsePacket(10); +} + void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) { // The payload is 16 bytes. First four bytes are the Merc ID (Template ID) - if(app->size != sizeof(MercenaryMerchantRequest_Struct)) + if (app->size != sizeof(MercenaryMerchantRequest_Struct)) { LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryHire expected %i got %i", sizeof(MercenaryMerchantRequest_Struct), app->size); @@ -13815,32 +9729,32 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) return; } - MercenaryMerchantRequest_Struct* mmrq = (MercenaryMerchantRequest_Struct*) app->pBuffer; + MercenaryMerchantRequest_Struct* mmrq = (MercenaryMerchantRequest_Struct*)app->pBuffer; uint32 merc_template_id = mmrq->MercID; uint32 merchant_id = mmrq->MercMerchantID; uint32 merc_unk1 = mmrq->MercUnk01; uint32 merc_unk2 = mmrq->MercUnk02; - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Template ID (%i), Merchant ID (%i), Unknown1 (%i), Unknown2 (%i)", merc_template_id, merchant_id, merc_unk1, merc_unk2); //HirePending = true; SetHoTT(0); SendTargetCommand(0); - if(!RuleB(Mercs, AllowMercs)) + if (!RuleB(Mercs, AllowMercs)) return; MercTemplate* merc_template = zone->GetMercTemplate(merc_template_id); - if(merc_template) { + if (merc_template) { Mob* merchant = entity_list.GetNPCByID(merchant_id); - if(!CheckCanHireMerc(merchant, merc_template_id)) { + if (!CheckCanHireMerc(merchant, merc_template_id)) { return; } - if(RuleB(Mercs, ChargeMercPurchaseCost)) { + if (RuleB(Mercs, ChargeMercPurchaseCost)) { uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold TakeMoneyFromPP(cost, true); } @@ -13851,7 +9765,7 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) // Get merc, assign it to client & spawn Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id, false); - if(merc) { + if (merc) { SpawnMerc(merc, true); merc->Save(); @@ -13871,7 +9785,7 @@ void Client::Handle_OP_MercenaryHire(const EQApplicationPacket *app) void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) { - if(app->size != sizeof(SuspendMercenary_Struct)) + if (app->size != sizeof(SuspendMercenary_Struct)) { Message(13, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenarySuspendRequest expected %i got %i", sizeof(SuspendMercenary_Struct), app->size); @@ -13879,133 +9793,23 @@ void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) return; } - SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*) app->pBuffer; + SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*)app->pBuffer; uint32 merc_suspend = sm->SuspendMerc; // Seen 30 for suspending or unsuspending - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Suspend ( %i ) received.", merc_suspend); - if(!RuleB(Mercs, AllowMercs)) + if (!RuleB(Mercs, AllowMercs)) return; // Check if the merc is suspended and if so, unsuspend, otherwise suspend it SuspendMercCommand(); } -void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app) -{ - if(app->size != sizeof(MercenaryCommand_Struct)) - { - Message(13, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryCommand expected %i got %i", sizeof(MercenaryCommand_Struct), app->size); - DumpPacket(app); - return; - } - - MercenaryCommand_Struct* mc = (MercenaryCommand_Struct*) app->pBuffer; - uint32 merc_command = mc->MercCommand; // Seen 0 (zone in with no merc or suspended), 1 (dismiss merc), 5 (normal state), 20 (unknown), 36 (zone in with merc) - int32 option = mc->Option; // Seen -1 (zone in with no merc), 0 (setting to passive stance), 1 (normal or setting to balanced stance) - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Command %i, Option %i received.", merc_command, option); - - if(!RuleB(Mercs, AllowMercs)) - return; - - // Handle the Command here... - // Will need a list of what every type of command is supposed to do - // Unsure if there is a server response to this packet - if(option >= 0) - { - Merc* merc = GetMerc(); - GetMercInfo().State = option; - - if(merc) { - uint8 numStances = 0; - - //get number of available stances for the current merc - std::list mercStanceList = zone->merc_stance_list[merc->GetMercTemplateID()]; - std::list::iterator iter = mercStanceList.begin(); - while(iter != mercStanceList.end()) { - numStances++; - ++iter; - } - - MercTemplate* mercTemplate = zone->GetMercTemplate(GetMerc()->GetMercTemplateID()); - if(mercTemplate) { - - //check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance) - if(option >= 0 && option < numStances) { - merc->SetStance(mercTemplate->Stances[option]); - GetMercInfo().Stance = mercTemplate->Stances[option]; - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Set Stance: %u", merc->GetStance()); - } - } - } - } -} - -void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app) -{ - // The payload is 0 bytes. - if(app->size != 0) - { - Message(13, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDataUpdateRequest expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Data Update Request Received."); - - if(GetMercID()) - { - SendMercPersonalInfo(); - } -} - -void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) -{ - // The payload is 0 or 1 bytes. - if(app->size > 1) - { - Message(13, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryDismiss expected 0 got %i", app->size); - DumpPacket(app); - return; - } - - uint8 Command = 0; - if(app->size > 0) - { - char *InBuffer = (char *)app->pBuffer; - Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); - } - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); - - // Handle the dismiss here... - if(GetMercID()) { - Merc* merc = GetMerc(); - - if(merc) { - if(CheckCanDismissMerc()) { - merc->Dismiss(); - } - } - } - - //SendMercMerchantResponsePacket(10); -} - void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) { // The payload is 0 bytes. - if(app->size > 1) + if (app->size > 1) { Message(13, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryTimerRequest expected 0 got %i", app->size); @@ -14013,10 +9817,10 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) return; } - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Timer Request received."); - if(!RuleB(Mercs, AllowMercs)) { + if (!RuleB(Mercs, AllowMercs)) { return; } @@ -14025,30 +9829,100 @@ void Client::Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app) uint32 entityID = 0; uint32 mercState = 5; uint32 suspendedTime = 0; - if(GetMercID()) { + if (GetMercID()) { Merc* merc = GetMerc(); - if(merc) { + if (merc) { entityID = merc->GetID(); - if(GetMercInfo().IsSuspended) { + if (GetMercInfo().IsSuspended) { mercState = 1; suspendedTime = GetMercInfo().SuspendedTime; } } } - if(entityID > 0) { + if (entityID > 0) { SendMercTimerPacket(entityID, mercState, suspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); } } -void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) { - // Does not exist in Ti, UF or RoF clients - // SoF and SoD both send a 4-byte packet with a uint32 value of '8' +void Client::Handle_OP_MoveCoin(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MoveCoin_Struct)){ + LogFile->write(EQEMuLog::Error, "Wrong size on OP_MoveCoin. Got: %i, Expected: %i", app->size, sizeof(MoveCoin_Struct)); + DumpPacket(app); + return; + } + OPMoveCoin(app); + return; } -void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) { +void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) +{ + if (!CharacterID()) + { + return; + } + + if (app->size != sizeof(MoveItem_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct)); + return; + } + + MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer; + if (spellend_timer.Enabled() && casting_spell_id && !IsBardSong(casting_spell_id)) + { + if (mi->from_slot != mi->to_slot && (mi->from_slot <= EmuConstants::GENERAL_END || mi->from_slot > 39) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) + { + char *detect = nullptr; + const ItemInst *itm_from = GetInv().GetItem(mi->from_slot); + const ItemInst *itm_to = GetInv().GetItem(mi->to_slot); + MakeAnyLenString(&detect, "Player issued a move item from %u(item id %u) to %u(item id %u) while casting %u.", + mi->from_slot, + itm_from ? itm_from->GetID() : 0, + mi->to_slot, + itm_to ? itm_to->GetID() : 0, + casting_spell_id); + database.SetMQDetectionFlag(AccountName(), GetName(), detect, zone->GetShortName()); + safe_delete_array(detect); + Kick(); // Kick client to prevent client and server from getting out-of-sync inventory slots + return; + } + } + + // Illegal bagslot useage checks. Currently, user only receives a message if this check is triggered. + bool mi_hack = false; + + if (mi->from_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->from_slot <= EmuConstants::CURSOR_BAG_END) { + if (mi->from_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } + else { + int16 from_parent = m_inv.CalcSlotId(mi->from_slot); + if (!m_inv[from_parent]) { mi_hack = true; } + else if (!m_inv[from_parent]->IsType(ItemClassContainer)) { mi_hack = true; } + else if (m_inv.CalcBagIdx(mi->from_slot) >= m_inv[from_parent]->GetItem()->BagSlots) { mi_hack = true; } + } + } + + if (mi->to_slot >= EmuConstants::GENERAL_BAGS_BEGIN && mi->to_slot <= EmuConstants::CURSOR_BAG_END) { + if (mi->to_slot >= EmuConstants::CURSOR_BAG_BEGIN) { mi_hack = true; } + else { + int16 to_parent = m_inv.CalcSlotId(mi->to_slot); + if (!m_inv[to_parent]) { mi_hack = true; } + else if (!m_inv[to_parent]->IsType(ItemClassContainer)) { mi_hack = true; } + else if (m_inv.CalcBagIdx(mi->to_slot) >= m_inv[to_parent]->GetItem()->BagSlots) { mi_hack = true; } + } + } + + if (mi_hack) { Message(15, "Caution: Illegal use of inaccessable bag slots!"); } + + if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) { SwapItemResync(mi); } + + return; +} + +void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) +{ // Does not exist in Ti client // SoF, SoD and UF clients send a 4-byte packet indicating the 'parent' slot // SoF, SoD and UF slots are defined by a uint32 value and currently untranslated @@ -14066,6 +9940,4256 @@ void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app) { // Manually looting a corpse results in a from '34' to '68' value for equipment items, '0' to '0' for inventory. } -void Client::Handle_OP_ClientTimeStamp(const EQApplicationPacket *app) { - // handle as needed or ignore like we have been doing... +void Client::Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_OpenGuildTributeMaster of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(StartTribute_Struct)) + printf("Error in OP_OpenGuildTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + //Opens the guild tribute master window + StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; + Mob* tribmast = entity_list.GetMob(st->tribute_master_id); + if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == GUILD_TRIBUTE_MASTER + && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { + st->response = 1; + QueuePacket(app); + tribute_master_id = st->tribute_master_id; + DoTributeUpdate(); + } + else { + st->response = 0; + QueuePacket(app); + } + } + return; } + +void Client::Handle_OP_OpenInventory(const EQApplicationPacket *app) +{ + // Does not exist in Ti, UF or RoF clients + // SoF and SoD both send a 4-byte packet with a uint32 value of '8' +} + +void Client::Handle_OP_OpenTributeMaster(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_OpenTributeMaster of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(StartTribute_Struct)) + printf("Error in OP_OpenTributeMaster. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + //Opens the tribute master window + StartTribute_Struct* st = (StartTribute_Struct*)app->pBuffer; + Mob* tribmast = entity_list.GetMob(st->tribute_master_id); + if (tribmast && tribmast->IsNPC() && tribmast->GetClass() == TRIBUTE_MASTER + && DistNoRoot(*tribmast) <= USE_NPC_RANGE2) { + st->response = 1; + QueuePacket(app); + tribute_master_id = st->tribute_master_id; + DoTributeUpdate(); + } + else { + st->response = 0; + QueuePacket(app); + } + } + return; +} + +void Client::Handle_OP_PDeletePetition(const EQApplicationPacket *app) +{ + if (app->size < 2) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PDeletePetition, size=%i, expected %i", app->size, 2); + return; + } + if (petition_list.DeletePetitionByCharName((char*)app->pBuffer)) + Message_StringID(0, PETITION_DELETED); + else + Message_StringID(0, PETITION_NO_DELETE); + return; +} + +void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetCommand_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetCommands, size=%i, expected %i", app->size, sizeof(PetCommand_Struct)); + return; + } + char val1[20] = { 0 }; + PetCommand_Struct* pet = (PetCommand_Struct*)app->pBuffer; + Mob* mypet = this->GetPet(); + + if (!mypet || pet->command == PET_LEADER) + { + if (pet->command == PET_LEADER) + { + if (mypet && (!GetTarget() || GetTarget() == mypet)) + { + mypet->Say_StringID(PET_LEADERIS, GetName()); + } + else if ((mypet = GetTarget())) + { + Mob *Owner = mypet->GetOwner(); + if (Owner) + mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName()); + else + mypet->Say_StringID(I_FOLLOW_NOONE); + } + } + + return; + } + + if (mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy)) + return; + + // just let the command "/pet get lost" work for familiars + if (mypet->GetPetType() == petFamiliar && pet->command != PET_GETLOST) + return; + + uint32 PetCommand = pet->command; + + // Handle Sit/Stand toggle in UF and later. + if (GetClientVersion() >= EQClientUnderfoot) + { + if (PetCommand == PET_SITDOWN) + if (mypet->GetPetOrder() == SPO_Sit) + PetCommand = PET_STANDUP; + } + + switch (PetCommand) + { + case PET_ATTACK: { + if (!GetTarget()) + break; + if (GetTarget()->IsMezzed()) { + Message_StringID(10, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); + break; + } + if (mypet->IsFeared()) + break; //prevent pet from attacking stuff while feared + + if (!mypet->IsAttackAllowed(GetTarget())) { + mypet->Say_StringID(NOT_LEGAL_TARGET); + break; + } + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { + if (GetTarget() != this && mypet->DistNoRootNoZ(*GetTarget()) <= (RuleR(Pets, AttackCommandRange)*RuleR(Pets, AttackCommandRange))) { + if (mypet->IsHeld()) { + if (!mypet->IsFocused()) { + mypet->SetHeld(false); //break the hold and guard if we explicitly tell the pet to attack. + if (mypet->GetPetOrder() != SPO_Guard) + mypet->SetPetOrder(SPO_Follow); + } + else { + mypet->SetTarget(GetTarget()); + } + } + zone->AddAggroMob(); + mypet->AddToHateList(GetTarget(), 1); + Message_StringID(MT_PetResponse, PET_ATTACKING, mypet->GetCleanName(), GetTarget()->GetCleanName()); + } + } + break; + } + case PET_BACKOFF: { + if (mypet->IsFeared()) break; //keeps pet running while feared + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_CALMING); + mypet->WipeHateList(); + mypet->SetTarget(nullptr); + } + break; + } + case PET_HEALTHREPORT: { + Message_StringID(MT_PetResponse, PET_REPORT_HP, ConvertArrayF(mypet->GetHPRatio(), val1)); + mypet->ShowBuffList(this); + //Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(uint8)mypet->GetHPRatio()); + break; + } + case PET_GETLOST: { + if (mypet->Charmed()) + break; + if (mypet->GetPetType() == petCharmed || !mypet->IsNPC()) { + // eqlive ignores this command + // we could just remove the charm + // and continue + mypet->BuffFadeByEffect(SE_Charm); + break; + } + else { + SetPet(nullptr); + } + + mypet->Say_StringID(MT_PetResponse, PET_GETLOST_STRING); + mypet->CastToNPC()->Depop(); + + //Oddly, the client (Titanium) will still allow "/pet get lost" command despite me adding the code below. If someone can figure that out, you can uncomment this code and use it. + /* + if((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 2) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(PET_GETLOST_STRING); + mypet->CastToNPC()->Depop(); + } + */ + + break; + } + case PET_GUARDHERE: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + if (mypet->IsNPC()) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_GUARDINGLIFE); + mypet->SetPetOrder(SPO_Guard); + mypet->CastToNPC()->SaveGuardSpot(); + } + } + break; + } + case PET_FOLLOWME: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_FOLLOWING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_TAUNT: { + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + Message_StringID(MT_PetResponse, PET_DO_TAUNT); + mypet->CastToNPC()->SetTaunting(true); + } + break; + } + case PET_NOTAUNT: { + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + Message_StringID(MT_PetResponse, PET_NO_TAUNT); + mypet->CastToNPC()->SetTaunting(false); + } + break; + } + case PET_GUARDME: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 1) || mypet->GetPetType() != petAnimation) { + mypet->SetHeld(false); + mypet->Say_StringID(MT_PetResponse, PET_GUARDME_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_SITDOWN: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_SIT); + } + break; + } + case PET_STANDUP: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if ((mypet->GetPetType() == petAnimation && GetAA(aaAnimationEmpathy) >= 3) || mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Follow); + mypet->SendAppearancePacket(AT_Anim, ANIM_STAND); + } + break; + } + case PET_SLUMBER: { + if (mypet->IsFeared()) break; //could be exploited like PET_BACKOFF + + if (mypet->GetPetType() != petAnimation) { + mypet->Say_StringID(MT_PetResponse, PET_SIT_STRING); + mypet->SetPetOrder(SPO_Sit); + mypet->SetRunAnimSpeed(0); + if (!mypet->UseBardSpellLogic()) //maybe we can have a bard pet + mypet->InterruptSpell(); //No cast 4 u. //i guess the pet should start casting + mypet->SendAppearancePacket(AT_Anim, ANIM_DEATH); + } + break; + } + case PET_HOLD: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC()){ + if (mypet->IsFeared()) + break; //could be exploited like PET_BACKOFF + + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->WipeHateList(); + mypet->SetHeld(true); + } + break; + } + case PET_HOLD_ON: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC() && !mypet->IsHeld()) { + if (mypet->IsFeared()) + break; //could be exploited like PET_BACKOFF + + mypet->Say_StringID(MT_PetResponse, PET_ON_HOLD); + mypet->WipeHateList(); + mypet->SetHeld(true); + } + break; + } + case PET_HOLD_OFF: { + if (GetAA(aaPetDiscipline) && mypet->IsNPC() && mypet->IsHeld()) + mypet->SetHeld(false); + break; + } + case PET_NOCAST: { + if (GetAA(aaAdvancedPetDiscipline) == 2 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsNoCast()) { + Message_StringID(MT_PetResponse, PET_CASTING); + mypet->CastToNPC()->SetNoCast(false); + } + else { + Message_StringID(MT_PetResponse, PET_NOT_CASTING); + mypet->CastToNPC()->SetNoCast(true); + } + } + break; + } + case PET_FOCUS: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); + mypet->CastToNPC()->SetFocused(false); + } + else { + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_ON: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (!mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOW_FOCUSING); + mypet->CastToNPC()->SetFocused(true); + } + } + break; + } + case PET_FOCUS_OFF: { + if (GetAA(aaAdvancedPetDiscipline) >= 1 && mypet->IsNPC()) { + if (mypet->IsFeared()) + break; + if (mypet->IsFocused()) { + Message_StringID(MT_PetResponse, PET_NOT_FOCUSING); + mypet->CastToNPC()->SetFocused(false); + } + } + break; + } + default: + printf("Client attempted to use a unknown pet command:\n"); + break; + } +} + +void Client::Handle_OP_Petition(const EQApplicationPacket *app) +{ + if (app->size <= 1) + return; + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + /*else if(petition_list.FindPetitionByAccountName(this->AccountName())) + { + Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition."); + return; + }*/ + else + { + if (petition_list.FindPetitionByAccountName(AccountName())) + { + Message(0, "You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it."); + return; + } + Petition* pet = new Petition(CharacterID()); + pet->SetAName(this->AccountName()); + pet->SetClass(this->GetClass()); + pet->SetLevel(this->GetLevel()); + pet->SetCName(this->GetName()); + pet->SetRace(this->GetRace()); + pet->SetLastGM(""); + pet->SetCName(this->GetName()); + pet->SetPetitionText((char*)app->pBuffer); + pet->SetZone(zone->GetZoneID()); + pet->SetUrgency(0); + petition_list.AddPetition(pet); + database.InsertPetitionToDB(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID()); + } + return; +} + +void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetitionBug_Struct)) + printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n", sizeof(PetitionBug_Struct), app->size); + else{ + Message(0, "Petition Bugs are not supported, please use /bug."); + } + return; +} + +void Client::Handle_OP_PetitionCheckIn(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Petition_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionCheckIn, size=%i, expected %i", app->size, sizeof(Petition_Struct)); + return; + } + Petition_Struct* inpet = (Petition_Struct*)app->pBuffer; + + Petition* pet = petition_list.GetPetitionByID(inpet->petnumber); + //if (inpet->urgency != pet->GetUrgency()) + pet->SetUrgency(inpet->urgency); + pet->SetLastGM(this->GetName()); + pet->SetGMText(inpet->gmtext); + + pet->SetCheckedOut(false); + petition_list.UpdatePetition(pet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetitionCheckout(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + uint32 getpetnum = *((uint32*)app->pBuffer); + Petition* getpet = petition_list.GetPetitionByID(getpetnum); + if (getpet != 0) { + getpet->AddCheckout(); + getpet->SetCheckedOut(true); + getpet->SendPetitionToPlayer(this->CastToClient()); + petition_list.UpdatePetition(getpet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + } + } + return; +} + +void Client::Handle_OP_PetitionDelete(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PetitionUpdate_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_PetitionDelete, size=%i, expected %i", app->size, sizeof(PetitionUpdate_Struct)); + return; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionUpdate, sizeof(PetitionUpdate_Struct)); + PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*)outapp->pBuffer; + pet->petnumber = *((int*)app->pBuffer); + pet->color = 0x00; + pet->status = 0xFFFFFFFF; + pet->senttime = 0; + strcpy(pet->accountid, ""); + strcpy(pet->gmsenttoo, ""); + pet->quetotal = petition_list.GetTotalPetitions(); + strcpy(pet->charname, ""); + FastQueuePacket(&outapp); + + if (petition_list.DeletePetition(pet->petnumber) == -1) + std::cout << "Something is borked with: " << pet->petnumber << std::endl; + petition_list.ClearPetitions(); + petition_list.UpdateGMQueue(); + petition_list.ReadDatabase(); + petition_list.UpdateZoneListQueue(); + return; +} + +void Client::Handle_OP_PetitionQue(const EQApplicationPacket *app) +{ +#ifdef _EQDEBUG + printf("%s looking at petitions..\n", this->GetName()); +#endif + return; +} + +void Client::Handle_OP_PetitionRefresh(const EQApplicationPacket *app) +{ + // This is When Client Asks for Petition Again and Again... + // break is here because it floods the zones and causes lag if it + // Were to actually do something:P We update on our own schedule now. + return; +} + +void Client::Handle_OP_PetitionResolve(const EQApplicationPacket *app) +{ + Handle_OP_PetitionDelete(app); +} + +void Client::Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + std::cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(uint32) << std::endl; + return; + } + if (!worldserver.Connected()) + Message(0, "Error: World server disconnected"); + else { + uint32 getpetnum = *((uint32*)app->pBuffer); + Petition* getpet = petition_list.GetPetitionByID(getpetnum); + if (getpet != 0) { + getpet->SetCheckedOut(false); + petition_list.UpdatePetition(getpet); + petition_list.UpdateGMQueue(); + petition_list.UpdateZoneListQueue(); + } + } + return; +} + +void Client::Handle_OP_PickPocket(const EQApplicationPacket *app) +{ + if (app->size != sizeof(PickPocket_Struct)) + { + LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet"); + DumpPacket(app); + } + + if (!HasSkill(SkillPickPockets)) + { + return; + } + + if (!p_timers.Expired(&database, pTimerBeggingPickPocket, false)) + { + Message(13, "Ability recovery time not yet met."); + database.SetMQDetectionFlag(this->AccountName(), this->GetName(), "OP_PickPocket was sent again too quickly.", zone->GetShortName()); + return; + } + PickPocket_Struct* pick_in = (PickPocket_Struct*)app->pBuffer; + + Mob* victim = entity_list.GetMob(pick_in->to); + if (!victim) + return; + + p_timers.Start(pTimerBeggingPickPocket, 8); + if (victim == this){ + Message(0, "You catch yourself red-handed."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } + else if (victim->GetOwnerID()){ + Message(0, "You cannot steal from pets!"); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } + else if (victim->IsNPC()){ + victim->CastToNPC()->PickPocket(this); + } + else{ + Message(0, "Stealing from clients not yet supported."); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_PickPocket, sizeof(sPickPocket_Struct)); + sPickPocket_Struct* pick_out = (sPickPocket_Struct*)outapp->pBuffer; + pick_out->coin = 0; + pick_out->from = victim->GetID(); + pick_out->to = GetID(); + pick_out->myskill = GetSkill(SkillPickPockets); + pick_out->type = 0; + //if we do not send this packet the client will lock up and require the player to relog. + QueuePacket(outapp); + safe_delete(outapp); + } +} + +void Client::Handle_OP_PopupResponse(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(PopupResponse_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PopupResponse expected %i got %i", + sizeof(PopupResponse_Struct), app->size); + DumpPacket(app); + return; + } + PopupResponse_Struct *prs = (PopupResponse_Struct*)app->pBuffer; + + // Handle any EQEmu defined popup Ids first + switch (prs->popupid) + { + case POPUPID_UPDATE_SHOWSTATSWINDOW: + if (GetTarget() && GetTarget()->IsClient()) + GetTarget()->CastToClient()->SendStatsWindow(this, true); + else + SendStatsWindow(this, true); + return; + + default: + break; + } + + char buf[16]; + sprintf(buf, "%d\0", prs->popupid); + + parse->EventPlayer(EVENT_POPUP_RESPONSE, this, buf, 0); + + Mob* Target = GetTarget(); + if (Target && Target->IsNPC()) { + parse->EventNPC(EVENT_POPUP_RESPONSE, Target->CastToNPC(), this, buf, 0); + } +} + +void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app) +{ + if (app->size != sizeof(MovePotionToBelt_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PotionBelt expected %i got %i", + sizeof(MovePotionToBelt_Struct), app->size); + DumpPacket(app); + return; + } + + MovePotionToBelt_Struct *mptbs = (MovePotionToBelt_Struct*)app->pBuffer; + if(!EQEmu::ValueWithin(mptbs->SlotNumber, 0U, 3U)) { + LogFile->write(EQEMuLog::Debug, "Client::Handle_OP_PotionBelt mptbs->SlotNumber out of range."); + return; + } + + if (mptbs->Action == 0) { + const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID); + if (BaseItem) { + m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID; + m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon; + strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, BaseItem->Name, sizeof(BaseItem->Name)); + database.SaveCharacterPotionBelt(this->CharacterID(), mptbs->SlotNumber, m_pp.potionbelt.items[mptbs->SlotNumber].item_id, m_pp.potionbelt.items[mptbs->SlotNumber].icon); + } + } + else { + m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 0; + m_pp.potionbelt.items[mptbs->SlotNumber].icon = 0; + strncpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_name, "\0", 1); + } +} + +void Client::Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app) +{ + if (app->size != sizeof(uint32)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_LeadershipExpToggle expected %i got %i", 1, app->size); + DumpPacket(app); + return; + } + uint32 aaid = *((uint32 *)app->pBuffer); + + if (aaid >= _maxLeaderAA) + return; + + uint32 current_rank = m_pp.leader_abilities.ranks[aaid]; + if (current_rank >= MAX_LEADERSHIP_TIERS) { + Message(13, "This ability can be trained no further."); + return; + } + + uint8 cost = LeadershipAACosts[aaid][current_rank]; + if (cost == 0) { + Message(13, "This ability can be trained no further."); + return; + } + + //TODO: we need to enforce prerequisits + + if (aaid >= raidAAMarkNPC) { + //it is a raid ability. + if (cost > m_pp.raid_leadership_points) { + Message(13, "You do not have enough points to purchase this ability."); + return; + } + + //sell them the ability. + m_pp.raid_leadership_points -= cost; + m_pp.leader_abilities.ranks[aaid]++; + + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); + } else { + //it is a group ability. + if (cost > m_pp.group_leadership_points) { + Message(13, "You do not have enough points to purchase this ability."); + return; + } + + //sell them the ability. + m_pp.group_leadership_points -= cost; + m_pp.leader_abilities.ranks[aaid]++; + + database.SaveCharacterLeadershipAA(this->CharacterID(), &m_pp); + } + + //success, send them an update + EQApplicationPacket *outapp = new EQApplicationPacket(OP_UpdateLeadershipAA, sizeof(UpdateLeadershipAA_Struct)); + UpdateLeadershipAA_Struct *u = (UpdateLeadershipAA_Struct *)outapp->pBuffer; + u->ability_id = aaid; + u->new_rank = m_pp.leader_abilities.ranks[aaid]; + if (aaid >= raidAAMarkNPC) // raid AA + u->pointsleft = m_pp.raid_leadership_points; + else // group AA + u->pointsleft = m_pp.group_leadership_points; + FastQueuePacket(&outapp); + + // Update all group members with the new AA the leader has purchased. + if (IsRaidGrouped()) { + Raid *r = GetRaid(); + if (!r) + return; + if (aaid >= raidAAMarkNPC) { + r->UpdateRaidAAs(); + r->SendAllRaidLeadershipAA(); + } else { + uint32 gid = r->GetGroup(this); + r->UpdateGroupAAs(gid); + r->GroupUpdate(gid, false); + } + } else if (IsGrouped()) { + Group *g = GetGroup(); + if (!g) + return; + g->UpdateGroupAAs(); + g->SendLeadershipAAUpdate(); + } + +} + +void Client::Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app) +{ + // This opcode is sent by the client when the player right clicks a name on the PVP leaderboard and sends + // further details about the selected player, e.g. Race/Class/AAs/Guild etc. + // + if (app->size != sizeof(PVPLeaderBoardDetailsRequest_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardDetailsRequest expected %i got %i", + sizeof(PVPLeaderBoardDetailsRequest_Struct), app->size); + + DumpPacket(app); + + return; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardDetailsReply, sizeof(PVPLeaderBoardDetailsReply_Struct)); + PVPLeaderBoardDetailsReply_Struct *pvplbdrs = (PVPLeaderBoardDetailsReply_Struct *)outapp->pBuffer; + + // TODO: Record and send this data. + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app) +{ + // This Opcode is sent by the client when the Leaderboard button on the PVP Stats window is pressed. + // + // It has a single uint32 payload which is the sort method: + // + // PVPSortByKills = 0, PVPSortByPoints = 1, PVPSortByInfamy = 2 + // + if (app->size != sizeof(PVPLeaderBoardRequest_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_PVPLeaderBoardRequest expected %i got %i", + sizeof(PVPLeaderBoardRequest_Struct), app->size); + + DumpPacket(app); + + return; + } + /*PVPLeaderBoardRequest_Struct *pvplbrs = (PVPLeaderBoardRequest_Struct *)app->pBuffer;*/ //unused + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_PVPLeaderBoardReply, sizeof(PVPLeaderBoard_Struct)); + /*PVPLeaderBoard_Struct *pvplb = (PVPLeaderBoard_Struct *)outapp->pBuffer;*/ //unused + + // TODO: Record and send this data. + + QueuePacket(outapp); + safe_delete(outapp); +} + +void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) +{ + if (app->size < sizeof(RaidGeneral_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RaidCommand, size=%i, expected at least %i", app->size, sizeof(RaidGeneral_Struct)); + DumpPacket(app); + return; + } + + RaidGeneral_Struct *ri = (RaidGeneral_Struct*)app->pBuffer; + switch (ri->action) + { + case RaidCommandInviteIntoExisting: + case RaidCommandInvite: { + Client *i = entity_list.GetClientByName(ri->player_name); + if (i){ + Group *g = i->GetGroup(); + if (g){ + if (g->IsLeader(i) == false) + Message(13, "You can only invite an ungrouped player or group leader to join your raid."); + else{ + //This sends an "invite" to the client in question. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(rg->leader_name, ri->leader_name, 64); + strn0cpy(rg->player_name, ri->player_name, 64); + + rg->parameter = 0; + rg->action = 20; + i->QueuePacket(outapp); + safe_delete(outapp); + } + } + else{ + //This sends an "invite" to the client in question. + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + strn0cpy(rg->leader_name, ri->leader_name, 64); + strn0cpy(rg->player_name, ri->player_name, 64); + + rg->parameter = 0; + rg->action = 20; + i->QueuePacket(outapp); + safe_delete(outapp); + } + } + break; + } + case RaidCommandAcceptInvite: { + Client *i = entity_list.GetClientByName(ri->player_name); + if (i){ + if (IsRaidGrouped()){ + i->Message_StringID(0, ALREADY_IN_RAID, GetName()); //group failed, must invite members not in raid... + return; + } + Raid *r = entity_list.GetRaidByClient(i); + if (r){ + r->VerifyRaid(); + Group *g = GetGroup(); + if (g){ + if (g->GroupCount() + r->RaidCount() > MAX_RAID_MEMBERS) + { + i->Message(13, "Invite failed, group invite would create a raid larger than the maximum number of members allowed."); + return; + } + } + else{ + if (1 + r->RaidCount() > MAX_RAID_MEMBERS) + { + i->Message(13, "Invite failed, member invite would create a raid larger than the maximum number of members allowed."); + return; + } + } + if (g){//add us all + uint32 freeGroup = r->GetFreeGroup(); + Client *addClient = nullptr; + for (int x = 0; x < 6; x++) + { + if (g->members[x]){ + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + + if (!addClient) + { + addClient = c; + r->SetGroupLeader(addClient->GetName()); + } + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + if (g->IsLeader(g->members[x])) + r->AddMember(c, freeGroup, false, true); + else + r->AddMember(c, freeGroup); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + g->DisbandGroup(); + r->GroupUpdate(freeGroup); + } + else{ + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->AddMember(this); + r->SendBulkRaid(this); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + } + else + { + Group *ig = i->GetGroup(); + Group *g = GetGroup(); + if (g) //if our target has a group + { + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + + uint32 groupFree = r->GetFreeGroup(); //get a free group + if (ig){ //if we already have a group then cycle through adding us... + Client *addClientig = nullptr; + for (int x = 0; x < 6; x++) + { + if (ig->members[x]){ + if (!addClientig){ + if (ig->members[x]->IsClient()){ + addClientig = ig->members[x]->CastToClient(); + r->SetGroupLeader(addClientig->GetName()); + } + } + if (ig->IsLeader(ig->members[x])){ + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree, true, true, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else{ + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + ig->DisbandGroup(); + r->GroupUpdate(groupFree); + groupFree = r->GetFreeGroup(); + } + else{ //else just add the inviter + r->SendRaidCreate(i); + r->AddMember(i, 0xFFFFFFFF, true, false, true); + } + + Client *addClient = nullptr; + //now add the existing group + for (int x = 0; x < 6; x++) + { + if (g->members[x]){ + if (!addClient) + { + if (g->members[x]->IsClient()){ + addClient = g->members[x]->CastToClient(); + r->SetGroupLeader(addClient->GetName()); + } + } + if (g->IsLeader(g->members[x])) + { + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree, false, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else + { + Client *c = nullptr; + if (g->members[x]->IsClient()) + c = g->members[x]->CastToClient(); + else + continue; + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, groupFree); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + g->DisbandGroup(); + r->GroupUpdate(groupFree); + } + else + { + if (ig){ + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + Client *addClientig = nullptr; + for (int x = 0; x < 6; x++) + { + if (ig->members[x]) + { + if (!addClientig){ + if (ig->members[x]->IsClient()){ + addClientig = ig->members[x]->CastToClient(); + r->SetGroupLeader(addClientig->GetName()); + } + } + if (ig->IsLeader(ig->members[x])) + { + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, 0, true, true, true); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + else + { + Client *c = nullptr; + if (ig->members[x]->IsClient()) + c = ig->members[x]->CastToClient(); + else + continue; + + r->SendRaidCreate(c); + r->SendMakeLeaderPacketTo(r->leadername, c); + r->AddMember(c, 0); + r->SendBulkRaid(c); + if (r->IsLocked()) { + r->SendRaidLockTo(c); + } + } + } + } + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->SendBulkRaid(this); + r->AddMember(this); + ig->DisbandGroup(); + r->GroupUpdate(0); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + else{ + r = new Raid(i); + entity_list.AddRaid(r); + r->SetRaidDetails(); + r->SendRaidCreate(i); + r->SendRaidCreate(this); + r->SendMakeLeaderPacketTo(r->leadername, this); + r->AddMember(i, 0xFFFFFFFF, true, false, true); + r->SendBulkRaid(this); + r->AddMember(this); + if (r->IsLocked()) { + r->SendRaidLockTo(this); + } + } + } + } + } + break; + } + case RaidCommandDisband: { + Raid *r = entity_list.GetRaidByClient(this); + if (r){ + //if(this == r->GetLeader()){ + uint32 grp = r->GetGroup(ri->leader_name); + + if (grp < 12){ + uint32 i = r->GetPlayerIndex(ri->leader_name); + if (r->members[i].IsGroupLeader){ //assign group leader to someone else + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (strlen(r->members[x].membername) > 0 && i != x){ + if (r->members[x].GroupNumber == grp){ + r->SetGroupLeader(ri->leader_name, false); + r->SetGroupLeader(r->members[x].membername); + r->UpdateGroupAAs(grp); + break; + } + } + } + + } + if (r->members[i].IsRaidLeader){ + for (int x = 0; x < MAX_RAID_MEMBERS; x++){ + if (strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, r->members[i].membername) != 0) + { + r->SetRaidLeader(r->members[i].membername, r->members[x].membername); + r->UpdateRaidAAs(); + r->SendAllRaidLeadershipAA(); + break; + } + } + } + } + + r->RemoveMember(ri->leader_name); + Client *c = entity_list.GetClientByName(ri->leader_name); + if (c) + r->SendGroupDisband(c); + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupRemove(ri->leader_name, grp); + r->GroupUpdate(grp);// break + //} + } + break; + } + case RaidCommandMoveGroup: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (ri->parameter < 12) //moving to a group + { + uint8 grpcount = r->GroupCount(ri->parameter); + + if (grpcount < 6) + { + Client *c = entity_list.GetClientByName(ri->leader_name); + uint32 oldgrp = r->GetGroup(ri->leader_name); + if (ri->parameter == oldgrp) //don't rejoin grp if we order to join same group. + break; + + if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader) + { + r->SetGroupLeader(ri->leader_name, false); + if (oldgrp < 12){ //we were the leader of our old grp + for (int x = 0; x < MAX_RAID_MEMBERS; x++) //assign a new grp leader if we can + { + if (r->members[x].GroupNumber == oldgrp) + { + if (strcmp(ri->leader_name, r->members[x].membername) != 0 && strlen(ri->leader_name) > 0) + { + r->SetGroupLeader(r->members[x].membername); + r->UpdateGroupAAs(oldgrp); + Client *cgl = entity_list.GetClientByName(r->members[x].membername); + if (cgl){ + r->SendRaidRemove(r->members[x].membername, cgl); + r->SendRaidCreate(cgl); + r->SendMakeLeaderPacketTo(r->leadername, cgl); + r->SendRaidAdd(r->members[x].membername, cgl); + r->SendBulkRaid(cgl); + if (r->IsLocked()) { + r->SendRaidLockTo(cgl); + } + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + strn0cpy(rga->playername, r->members[x].membername, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + break; + } + } + } + } + } + if (grpcount == 0) { + r->SetGroupLeader(ri->leader_name); + r->UpdateGroupAAs(ri->parameter); + } + + r->MoveMember(ri->leader_name, ri->parameter); + if (c){ + r->SendGroupDisband(c); + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupAdd(ri->leader_name, ri->parameter); + //r->SendRaidGroupRemove(ri->leader_name, oldgrp); + //r->SendGroupUpdate(c); + //break + r->GroupUpdate(ri->parameter); //send group update to our new group + if (oldgrp < 12) //if our old was a group send update there too + r->GroupUpdate(oldgrp); + + //r->SendMakeGroupLeaderPacketAll(); + } + } + else //moving to ungrouped + { + Client *c = entity_list.GetClientByName(ri->leader_name); + uint32 oldgrp = r->GetGroup(ri->leader_name); + if (r->members[r->GetPlayerIndex(ri->leader_name)].IsGroupLeader){ + r->SetGroupLeader(ri->leader_name, false); + for (int x = 0; x < MAX_RAID_MEMBERS; x++) + { + if (r->members[x].GroupNumber == oldgrp && strlen(r->members[x].membername) > 0 && strcmp(r->members[x].membername, ri->leader_name) != 0) + { + r->SetGroupLeader(r->members[x].membername); + r->UpdateGroupAAs(oldgrp); + Client *cgl = entity_list.GetClientByName(r->members[x].membername); + if (cgl){ + r->SendRaidRemove(r->members[x].membername, cgl); + r->SendRaidCreate(cgl); + r->SendMakeLeaderPacketTo(r->leadername, cgl); + r->SendRaidAdd(r->members[x].membername, cgl); + r->SendBulkRaid(cgl); + if (r->IsLocked()) { + r->SendRaidLockTo(cgl); + } + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + strn0cpy(rga->playername, r->members[x].membername, 64); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + worldserver.SendPacket(pack); + safe_delete(pack); + } + break; + } + } + } + r->MoveMember(ri->leader_name, 0xFFFFFFFF); + if (c){ + r->SendGroupDisband(c); + } + else{ + ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupDisband, sizeof(ServerRaidGeneralAction_Struct)); + ServerRaidGeneralAction_Struct* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; + rga->rid = r->GetID(); + rga->zoneid = zone->GetZoneID(); + rga->instance_id = zone->GetInstanceID(); + strn0cpy(rga->playername, ri->leader_name, 64); + worldserver.SendPacket(pack); + safe_delete(pack); + } + //r->SendRaidGroupRemove(ri->leader_name, oldgrp); + r->GroupUpdate(oldgrp); + //r->SendMakeGroupLeaderPacketAll(); + } + } + break; + } + case RaidCommandRaidLock: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (!r->IsLocked()) + r->LockRaid(true); + else + r->SendRaidLockTo(this); + } + break; + } + case RaidCommandRaidUnlock: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (r->IsLocked()) + r->LockRaid(false); + else + r->SendRaidUnlockTo(this); + } + break; + } + case RaidCommandLootType2: + case RaidCommandLootType: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Loot type changed to: %d.", ri->parameter); + r->ChangeLootType(ri->parameter); + } + break; + } + + case RaidCommandAddLooter2: + case RaidCommandAddLooter: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Adding %s as a raid looter.", ri->leader_name); + r->AddRaidLooter(ri->leader_name); + } + break; + } + + case RaidCommandRemoveLooter2: + case RaidCommandRemoveLooter: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + Message(15, "Removing %s as a raid looter.", ri->leader_name); + r->RemoveRaidLooter(ri->leader_name); + } + break; + } + + case RaidCommandMakeLeader: + { + Raid *r = entity_list.GetRaidByClient(this); + if (r) + { + if (strcmp(r->leadername, GetName()) == 0){ + r->SetRaidLeader(GetName(), ri->leader_name); + r->UpdateRaidAAs(); + r->SendAllRaidLeadershipAA(); + } + } + break; + } + + case RaidCommandSetMotd: + { + Raid *r = entity_list.GetRaidByClient(this); + if (!r) + break; + // we don't use the RaidGeneral here! + RaidMOTD_Struct *motd = (RaidMOTD_Struct *)app->pBuffer; + r->SetRaidMOTD(std::string(motd->motd)); + r->SaveRaidMOTD(); + r->SendRaidMOTDToWorld(); + break; + } + + default: { + Message(13, "Raid command (%d) NYI", ri->action); + break; + } + } +} + +void Client::Handle_OP_RandomReq(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RandomReq_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_RandomReq, size=%i, expected %i", app->size, sizeof(RandomReq_Struct)); + return; + } + const RandomReq_Struct* rndq = (const RandomReq_Struct*)app->pBuffer; + uint32 randLow = rndq->low > rndq->high ? rndq->high : rndq->low; + uint32 randHigh = rndq->low > rndq->high ? rndq->low : rndq->high; + uint32 randResult; + + if (randLow == 0 && randHigh == 0) + { // defaults + randLow = 0; + randHigh = 100; + } + randResult = MakeRandomInt(randLow, randHigh); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RandomReply, sizeof(RandomReply_Struct)); + RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer; + rr->low = randLow; + rr->high = randHigh; + rr->result = randResult; + strcpy(rr->name, GetName()); + entity_list.QueueCloseClients(this, outapp, false, 400); + safe_delete(outapp); + return; +} + +void Client::Handle_OP_ReadBook(const EQApplicationPacket *app) +{ + if (app->size != sizeof(BookRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ReadBook, size=%i, expected %i", app->size, sizeof(BookRequest_Struct)); + return; + } + BookRequest_Struct* book = (BookRequest_Struct*)app->pBuffer; + ReadBook(book); + if (GetClientVersion() >= EQClientSoF) + { + EQApplicationPacket EndOfBook(OP_FinishWindow, 0); + QueuePacket(&EndOfBook); + } + return; +} + +void Client::Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RecipeAutoCombine_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipeAutoCombine_Struct: Expected: %i, Got: %i", + sizeof(RecipeAutoCombine_Struct), app->size); + return; + } + + RecipeAutoCombine_Struct* rac = (RecipeAutoCombine_Struct*)app->pBuffer; + + Object::HandleAutoCombine(this, rac); + return; +} + +void Client::Handle_OP_RecipeDetails(const EQApplicationPacket *app) +{ + if (app->size < sizeof(uint32)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipeDetails Request: Expected: %i, Got: %i", + sizeof(uint32), app->size); + return; + } + uint32 *recipe_id = (uint32*)app->pBuffer; + + SendTradeskillDetails(*recipe_id); + + return; +} + +void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeskillFavorites_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for TradeskillFavorites_Struct: Expected: %i, Got: %i", + sizeof(TradeskillFavorites_Struct), app->size); + return; + } + + TradeskillFavorites_Struct* tsf = (TradeskillFavorites_Struct*)app->pBuffer; + + LogFile->write(EQEMuLog::Debug, "Requested Favorites for: %d - %d\n", tsf->object_type, tsf->some_id); + + // results show that object_type is combiner type + // some_id = 0 if world combiner, item number otherwise + + // make where clause segment for container(s) + std::string containers; + if (tsf->some_id == 0) + containers += StringFormat(" = %u ", tsf->object_type); // world combiner so no item number + else + containers += StringFormat(" in (%u, %u) ", tsf->object_type, tsf->some_id); // container in inventory + + std::string favoriteIDs; //gotta be big enough for 500 IDs + bool first = true; + //Assumes item IDs are <10 characters long + for (uint16 favoriteIndex = 0; favoriteIndex < 500; ++favoriteIndex) { + if (tsf->favorite_recipes[favoriteIndex] == 0) + continue; + + if (first) { + favoriteIDs += StringFormat("%u", tsf->favorite_recipes[favoriteIndex]); + first = false; + } + else + favoriteIDs += StringFormat(",%u", tsf->favorite_recipes[favoriteIndex]); + } + + if (first) //no favorites.... + return; + + const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, " + "SUM(tre.componentcount), crl.madecount,tr.tradeskill " + "FROM tradeskill_recipe AS tr " + "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id=tre.recipe_id " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list " + "WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + "WHERE tr.enabled <> 0 AND tr.id IN (%s) " + "AND tr.must_learn & 0x20 <> 0x20 AND " + "((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) " + "OR (tr.must_learn & 0x3 = 0)) " + "GROUP BY tr.id " + "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + "LIMIT 100 ", CharacterID(), favoriteIDs.c_str(), containers.c_str()); + + TradeskillSearchResults(query, tsf->object_type, tsf->some_id); + return; +} + +void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app) +{ + if (app->size != sizeof(RecipesSearch_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for RecipesSearch_Struct: Expected: %i, Got: %i", + sizeof(RecipesSearch_Struct), app->size); + return; + } + + RecipesSearch_Struct* rss = (RecipesSearch_Struct*)app->pBuffer; + rss->query[55] = '\0'; //just to be sure. + + + LogFile->write(EQEMuLog::Debug, "Requested search recipes for: %d - %d\n", rss->object_type, rss->some_id); + + // make where clause segment for container(s) + char containers[30]; + if (rss->some_id == 0) { + // world combiner so no item number + snprintf(containers, 29, "= %u", rss->object_type); + } + else { + // container in inventory + snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id); + } + + std::string searchClause; + + //omit the rlike clause if query is empty + if (rss->query[0] != 0) { + char buf[120]; //larger than 2X rss->query + database.DoEscapeString(buf, rss->query, strlen(rss->query)); + searchClause = StringFormat("name rlike '%s' AND", buf); + } + + //arbitrary limit of 200 recipes, makes sense to me. + const std::string query = StringFormat("SELECT tr.id, tr.name, tr.trivial, " + "SUM(tre.componentcount), crl.madecount,tr.tradeskill " + "FROM tradeskill_recipe AS tr " + "LEFT JOIN tradeskill_recipe_entries AS tre ON tr.id = tre.recipe_id " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list WHERE char_id = %u) AS crl ON tr.id=crl.recipe_id " + "WHERE %s tr.trivial >= %u AND tr.trivial <= %u AND tr.enabled <> 0 " + "AND tr.must_learn & 0x20 <> 0x20 " + "AND ((tr.must_learn & 0x3 <> 0 " + "AND crl.madecount IS NOT NULL) " + "OR (tr.must_learn & 0x3 = 0)) " + "GROUP BY tr.id " + "HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 " + "LIMIT 200 ", + CharacterID(), searchClause.c_str(), + rss->mintrivial, rss->maxtrivial, containers); + TradeskillSearchResults(query, rss->object_type, rss->some_id); + return; +} + +void Client::Handle_OP_ReloadUI(const EQApplicationPacket *app) +{ + if (IsInAGuild()) + { + SendGuildRanks(); + SendGuildMembers(); + } + return; +} + +void Client::Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app) +{ + if (!RuleB(Spells, EnableBlockedBuffs)) + return; + + if (app->size != sizeof(BlockedBuffs_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RemoveBlockedBuffs expected %i got %i", + sizeof(BlockedBuffs_Struct), app->size); + + DumpPacket(app); + + return; + } + BlockedBuffs_Struct *bbs = (BlockedBuffs_Struct*)app->pBuffer; + + std::set *BlockedBuffs = bbs->Pet ? &PetBlockedBuffs : &PlayerBlockedBuffs; + + std::set RemovedBuffs; + + if (bbs->Count > 0) + { + std::set::iterator Iterator; + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RemoveBlockedBuffs, sizeof(BlockedBuffs_Struct)); + + BlockedBuffs_Struct *obbs = (BlockedBuffs_Struct*)outapp->pBuffer; + + for (unsigned int i = 0; i < BLOCKED_BUFF_COUNT; ++i) + obbs->SpellID[i] = 0; + + obbs->Pet = bbs->Pet; + obbs->Initialise = 0; + obbs->Flags = 0x5a; + + for (unsigned int i = 0; i < bbs->Count; ++i) + { + Iterator = BlockedBuffs->find(bbs->SpellID[i]); + + if (Iterator != BlockedBuffs->end()) + { + RemovedBuffs.insert(bbs->SpellID[i]); + + BlockedBuffs->erase(Iterator); + } + } + obbs->Count = RemovedBuffs.size(); + + Iterator = RemovedBuffs.begin(); + + unsigned int Element = 0; + + while (Iterator != RemovedBuffs.end()) + { + obbs->SpellID[Element++] = (*Iterator); + ++Iterator; + } + + FastQueuePacket(&outapp); + } +} + +void Client::Handle_OP_Report(const EQApplicationPacket *app) +{ + if (!CanUseReport) + { + Message_StringID(MT_System, REPORT_ONCE); + return; + } + + uint32 size = app->size; + uint32 current_point = 0; + std::string reported, reporter; + std::string current_string; + int mode = 0; + + while (current_point < size) + { + if (mode < 2) + { + if (app->pBuffer[current_point] == '|') + { + mode++; + } + else + { + if (mode == 0) + { + reported += app->pBuffer[current_point]; + } + else + { + reporter += app->pBuffer[current_point]; + } + } + current_point++; + } + else + { + if (app->pBuffer[current_point] == 0x0a) + { + current_string += '\n'; + } + else if (app->pBuffer[current_point] == 0x00) + { + CanUseReport = false; + database.AddReport(reporter, reported, current_string); + return; + } + else + { + current_string += app->pBuffer[current_point]; + } + current_point++; + } + } + + CanUseReport = false; + database.AddReport(reporter, reported, current_string); +} + +void Client::Handle_OP_RequestDuel(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Duel_Struct)) + return; + + EQApplicationPacket* outapp = app->Copy(); + Duel_Struct* ds = (Duel_Struct*)outapp->pBuffer; + uint32 duel = ds->duel_initiator; + ds->duel_initiator = ds->duel_target; + ds->duel_target = duel; + Entity* entity = entity_list.GetID(ds->duel_target); + if (GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) { + Message_StringID(10, DUEL_CONSIDERING, entity->GetName()); + return; + } + if (IsDueling()) { + Message_StringID(10, DUEL_INPROGRESS); + return; + } + + if (GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) { + SetDuelTarget(ds->duel_target); + entity->CastToClient()->SetDuelTarget(GetID()); + ds->duel_target = ds->duel_initiator; + entity->CastToClient()->FastQueuePacket(&outapp); + entity->CastToClient()->SetDueling(false); + SetDueling(false); + } + else + safe_delete(outapp); + return; +} + +void Client::Handle_OP_RequestTitles(const EQApplicationPacket *app) +{ + + EQApplicationPacket *outapp = title_manager.MakeTitlesPacket(this); + + if (outapp != nullptr) + FastQueuePacket(&outapp); +} + +void Client::Handle_OP_RespawnWindow(const EQApplicationPacket *app) +{ + // This opcode is sent by the client when the player choses which bind to return to. + // The client sends just a 4 byte packet with the selection number in it + // + if (app->size != 4) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_RespawnWindow expected %i got %i", + 4, app->size); + DumpPacket(app); + return; + } + char *Buffer = (char *)app->pBuffer; + + uint32 Option = VARSTRUCT_DECODE_TYPE(uint32, Buffer); + HandleRespawnFromHover(Option); +} + +void Client::Handle_OP_Rewind(const EQApplicationPacket *app) +{ + if ((rewind_timer.GetRemainingTime() > 1 && rewind_timer.Enabled())) { + Message_StringID(MT_System, REWIND_WAIT); + } + else { + CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), rewind_x, rewind_y, rewind_z, 0, 2, Rewind); + rewind_timer.Start(30000, true); + } +} + +void Client::Handle_OP_RezzAnswer(const EQApplicationPacket *app) +{ + VERIFY_PACKET_LENGTH(OP_RezzAnswer, app, Resurrect_Struct); + + const Resurrect_Struct* ra = (const Resurrect_Struct*)app->pBuffer; + + _log(SPELLS__REZ, "Received OP_RezzAnswer from client. Pendingrezzexp is %i, action is %s", + PendingRezzXP, ra->action ? "ACCEPT" : "DECLINE"); + + _pkt(SPELLS__REZ, app); + + OPRezzAnswer(ra->action, ra->spellid, ra->zone_id, ra->instance_id, ra->x, ra->y, ra->z); + + if (ra->action == 1) + { + EQApplicationPacket* outapp = app->Copy(); + // Send the OP_RezzComplete to the world server. This finds it's way to the zone that + // the rezzed corpse is in to mark the corpse as rezzed. + outapp->SetOpcode(OP_RezzComplete); + worldserver.RezzPlayer(outapp, 0, 0, OP_RezzComplete); + safe_delete(outapp); + } + return; +} + +void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Sacrifice_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Sacrifice expected %i got %i", sizeof(Sacrifice_Struct), app->size); + DumpPacket(app); + return; + } + Sacrifice_Struct *ss = (Sacrifice_Struct*)app->pBuffer; + + if (!PendingSacrifice) { + LogFile->write(EQEMuLog::Error, "Unexpected OP_Sacrifice reply"); + DumpPacket(app); + return; + } + + if (ss->Confirm) { + Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str()); + if (Caster) Sacrifice(Caster); + } + PendingSacrifice = false; + SacrificeCaster.clear(); +} + +void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail) +{ + if (HasSkill(SkillSafeFall)) //this should only get called if the client has safe fall, but just in case... + CheckIncreaseSkill(SkillSafeFall, nullptr); //check for skill up +} + +void Client::Handle_OP_SafePoint(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_Save(const EQApplicationPacket *app) +{ + // The payload is 192 bytes - Not sure what is contained in payload + Save(); + return; +} + +void Client::Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app) +{ + Handle_OP_Save(app); +} + +void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_SelectTribute of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //we should enforce being near a real tribute master to change this + //but im not sure how I wanna do that right now. + if (app->size != sizeof(SelectTributeReq_Struct)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_SelectTribute packet"); + else { + SelectTributeReq_Struct *t = (SelectTributeReq_Struct *)app->pBuffer; + SendTributeDetails(t->client_id, t->tribute_id); + } + return; +} + +void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillSenseTraps)) + return; + + if (!p_timers.Expired(&database, pTimerSenseTraps, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + int reuse = SenseTrapsReuseTime; + switch (GetAA(aaAdvTrapNegotiation)) { + case 1: + reuse -= 1; + break; + case 2: + reuse -= 3; + break; + case 3: + reuse -= 5; + break; + } + p_timers.Start(pTimerSenseTraps, reuse - 1); + + Trap* trap = entity_list.FindNearbyTrap(this, 800); + + CheckIncreaseSkill(SkillSenseTraps, nullptr); + + if (trap && trap->skill > 0) { + int uskill = GetSkill(SkillSenseTraps); + if ((MakeRandomInt(0, 99) + uskill) >= (MakeRandomInt(0, 99) + trap->skill*0.75)) + { + float xdif = trap->x - GetX(); + float ydif = trap->y - GetY(); + if (xdif == 0 && ydif == 0) + Message(MT_Skills, "You sense a trap right under your feet!"); + else if (xdif > 10 && ydif > 10) + Message(MT_Skills, "You sense a trap to the NorthWest."); + else if (xdif < -10 && ydif > 10) + Message(MT_Skills, "You sense a trap to the NorthEast."); + else if (ydif > 10) + Message(MT_Skills, "You sense a trap to the North."); + else if (xdif > 10 && ydif < -10) + Message(MT_Skills, "You sense a trap to the SouthWest."); + else if (xdif < -10 && ydif < -10) + Message(MT_Skills, "You sense a trap to the SouthEast."); + else if (ydif < -10) + Message(MT_Skills, "You sense a trap to the South."); + else if (xdif > 10) + Message(MT_Skills, "You sense a trap to the West."); + else + Message(MT_Skills, "You sense a trap to the East."); + trap->detected = true; + + float angle = CalculateHeadingToTarget(trap->x, trap->y); + + if (angle < 0) + angle = (256 + angle); + + angle *= 2; + MovePC(zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), angle); + return; + } + } + Message(MT_Skills, "You did not find any traps nearby."); + return; +} + +void Client::Handle_OP_SetGuildMOTD(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_SetGuildMOTD"); + mpkt(GUILDS__IN_PACKET_TRACE, app); + + if (app->size != sizeof(GuildMOTD_Struct)) { + // client calls for a motd on login even if they arent in a guild + printf("Error: app size of %i != size of GuildMOTD_Struct of %zu\n", app->size, sizeof(GuildMOTD_Struct)); + return; + } + if (!IsInAGuild()) { + Message(13, "You are not in a guild!"); + return; + } + if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_MOTD)) { + Message(13, "You do not have permissions to edit your guild's MOTD."); + return; + } + + GuildMOTD_Struct* gmotd = (GuildMOTD_Struct*)app->pBuffer; + + mlog(GUILDS__ACTIONS, "Setting MOTD for %s (%d) to: %s - %s", + guild_mgr.GetGuildName(GuildID()), GuildID(), GetName(), gmotd->motd); + + if (!guild_mgr.SetGuildMOTD(GuildID(), gmotd->motd, GetName())) { + Message(0, "Motd update failed."); + } + + return; +} + +void Client::Handle_OP_SetRunMode(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_SetServerFilter(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetServerFilter_Struct)) { + LogFile->write(EQEMuLog::Error, "Received invalid sized " + "OP_SetServerFilter: got %d, expected %d", app->size, + sizeof(SetServerFilter_Struct)); + DumpPacket(app); + return; + } + SetServerFilter_Struct* filter = (SetServerFilter_Struct*)app->pBuffer; + ServerFilter(filter); + return; +} + +void Client::Handle_OP_SetStartCity(const EQApplicationPacket *app) +{ + // if the character has a start city, don't let them use the command + if (m_pp.binds[4].zoneId != 0 && m_pp.binds[4].zoneId != 189) { + Message(15, "Your home city has already been set.", m_pp.binds[4].zoneId, database.GetZoneName(m_pp.binds[4].zoneId)); + return; + } + + if (app->size < 1) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_SetStartCity, size=%i, expected %i", app->size, 1); + DumpPacket(app); + return; + } + + float x(0), y(0), z(0); + uint32 zoneid = 0; + uint32 startCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); + + std::string query = StringFormat("SELECT zone_id, bind_id, x, y, z FROM start_zones " + "WHERE player_class=%i AND player_deity=%i AND player_race=%i", + m_pp.class_, m_pp.deity, m_pp.race); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); + return; + } + + bool validCity = false; + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[1]) != 0) + zoneid = atoi(row[1]); + else + zoneid = atoi(row[0]); + + if (zoneid != startCity) + continue; + + validCity = true; + x = atof(row[2]); + y = atof(row[3]); + z = atof(row[4]); + } + + if (validCity) { + Message(15, "Your home city has been set"); + SetStartZone(startCity, x, y, z); + return; + } + + query = StringFormat("SELECT zone_id, bind_id FROM start_zones " + "WHERE player_class=%i AND player_deity=%i AND player_race=%i", + m_pp.class_, m_pp.deity, m_pp.race); + results = database.QueryDatabase(query); + if (!results.Success()) + return; + + Message(15, "Use \"/startcity #\" to choose a home city from the following list:"); + + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[1]) != 0) + zoneid = atoi(row[1]); + else + zoneid = atoi(row[0]); + + char* name; + database.GetZoneLongName(database.GetZoneName(zoneid), &name); + Message(15, "%d - %s", zoneid, name); + } + +} + +void Client::Handle_OP_SetTitle(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SetTitle_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_SetTitle expected %i got %i", sizeof(SetTitle_Struct), app->size); + DumpPacket(app); + return; + } + + SetTitle_Struct *sts = (SetTitle_Struct *)app->pBuffer; + + std::string Title; + + if (!sts->is_suffix) + { + Title = title_manager.GetPrefix(sts->title_id); + SetAATitle(Title.c_str()); + } + else + { + Title = title_manager.GetSuffix(sts->title_id); + SetTitleSuffix(Title.c_str()); + } +} + +void Client::Handle_OP_Shielding(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Shielding_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_Shielding expected:%i got:%i", sizeof(Shielding_Struct), app->size); + return; + } + if (GetClass() != WARRIOR) + { + return; + } + + if (shield_target) + { + entity_list.MessageClose_StringID(this, false, 100, 0, + END_SHIELDING, GetName(), shield_target->GetName()); + for (int y = 0; y < 2; y++) + { + if (shield_target->shielder[y].shielder_id == GetID()) + { + shield_target->shielder[y].shielder_id = 0; + shield_target->shielder[y].shielder_bonus = 0; + } + } + } + Shielding_Struct* shield = (Shielding_Struct*)app->pBuffer; + shield_target = entity_list.GetMob(shield->target_id); + bool ack = false; + ItemInst* inst = GetInv().GetItem(MainSecondary); + if (!shield_target) + return; + if (inst) + { + const Item_Struct* shield = inst->GetItem(); + if (shield && shield->ItemType == ItemTypeShield) + { + for (int x = 0; x < 2; x++) + { + if (shield_target->shielder[x].shielder_id == 0) + { + entity_list.MessageClose_StringID(this, false, 100, 0, + START_SHIELDING, GetName(), shield_target->GetName()); + shield_target->shielder[x].shielder_id = GetID(); + int shieldbonus = shield->AC * 2; + switch (GetAA(197)) + { + case 1: + shieldbonus = shieldbonus * 115 / 100; + break; + case 2: + shieldbonus = shieldbonus * 125 / 100; + break; + case 3: + shieldbonus = shieldbonus * 150 / 100; + break; + } + shield_target->shielder[x].shielder_bonus = shieldbonus; + shield_timer.Start(); + ack = true; + break; + } + } + } + else + { + Message(0, "You must have a shield equipped to shield a target!"); + shield_target = 0; + return; + } + } + else + { + Message(0, "You must have a shield equipped to shield a target!"); + shield_target = 0; + return; + } + if (!ack) + { + Message_StringID(0, ALREADY_SHIELDED); + shield_target = 0; + return; + } + return; +} + +void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app) +{ + EQApplicationPacket empty(OP_ShopEndConfirm); + QueuePacket(&empty); + //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2); + //outapp->pBuffer[0] = 0x0a; + //outapp->pBuffer[1] = 0x66; + //QueuePacket(outapp); + //safe_delete(outapp); + //Save(); + return; +} + +void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Sell_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerBuy: Expected %i, Got %i", + sizeof(Merchant_Sell_Struct), app->size); + return; + } + RDTSC_Timer t1; + t1.start(); + Merchant_Sell_Struct* mp = (Merchant_Sell_Struct*)app->pBuffer; +#if EQDEBUG >= 5 + LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName()); + DumpPacket(app); +#endif + + int merchantid; + bool tmpmer_used = false; + Mob* tmp = entity_list.GetMob(mp->npcid); + + if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) + return; + + if (mp->quantity < 1) return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + uint32 item_id = 0; + std::list merlist = zone->merchanttable[merchantid]; + std::list::const_iterator itr; + for (itr = merlist.begin(); itr != merlist.end(); ++itr){ + MerchantList ml = *itr; + if (GetLevel() < ml.level_required) { + continue; + } + + int32 fac = tmp->GetPrimaryFaction(); + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) { + continue; + } + + if (mp->itemslot == ml.slot){ + item_id = ml.item; + break; + } + } + const Item_Struct* item = nullptr; + uint32 prevcharges = 0; + if (item_id == 0) { //check to see if its on the temporary table + std::list tmp_merlist = zone->tmpmerchanttable[tmp->GetNPCTypeID()]; + std::list::const_iterator tmp_itr; + TempMerchantList ml; + for (tmp_itr = tmp_merlist.begin(); tmp_itr != tmp_merlist.end(); ++tmp_itr){ + ml = *tmp_itr; + if (mp->itemslot == ml.slot){ + item_id = ml.item; + tmpmer_used = true; + prevcharges = ml.charges; + break; + } + } + } + item = database.GetItem(item_id); + if (!item){ + //error finding item, client didnt get the update packet for whatever reason, roleplay a tad + Message(15, "%s tells you 'Sorry, that item is for display purposes only.' as they take the item off the shelf.", tmp->GetCleanName()); + EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); + Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; + delitem->itemslot = mp->itemslot; + delitem->npcid = mp->npcid; + delitem->playerid = mp->playerid; + delitempacket->priority = 6; + entity_list.QueueCloseClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update + safe_delete(delitempacket); + return; + } + if (CheckLoreConflict(item)) + { + Message(15, "You can only have one of a lore item."); + return; + } + if (tmpmer_used && (mp->quantity > prevcharges || item->MaxCharges > 1)) + { + if (prevcharges > item->MaxCharges && item->MaxCharges > 1) + mp->quantity = item->MaxCharges; + else + mp->quantity = prevcharges; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct)); + Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer; + mpo->quantity = mp->quantity; + mpo->playerid = mp->playerid; + mpo->npcid = mp->npcid; + mpo->itemslot = mp->itemslot; + + int16 freeslotid = 0; + int16 charges = 0; + if (item->Stackable || item->MaxCharges > 1) + charges = mp->quantity; + else + charges = item->MaxCharges; + + ItemInst* inst = database.CreateItem(item, charges); + + int SinglePrice = 0; + if (RuleB(Merchant, UsePriceMod)) + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false)); + else + SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate); + + if (item->MaxCharges > 1) + mpo->price = SinglePrice; + else + mpo->price = SinglePrice * mp->quantity; + if (mpo->price < 0) + { + safe_delete(outapp); + safe_delete(inst); + return; + } + + if (!TakeMoneyFromPP(mpo->price)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "Vendor Cheat: attempted to buy %i of %i: %s that cost %d cp but only has %d pp %d gp %d sp %d cp\n", + mpo->quantity, item->ID, item->Name, + mpo->price, m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + safe_delete(outapp); + safe_delete(inst); + return; + } + + bool stacked = TryStacking(inst); + if (!stacked) + freeslotid = m_inv.FindFreeSlot(false, true, item->Size); + + //make sure we are not completely full... + if (freeslotid == MainCursor) { + if (m_inv.GetItem(MainCursor) != nullptr) { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + } + + if (freeslotid == INVALID_INDEX) + { + Message(13, "You do not have room for any more items."); + safe_delete(outapp); + safe_delete(inst); + return; + } + + std::string packet; + if (!stacked && inst) { + PutItemInInventory(freeslotid, *inst); + SendItemPacket(freeslotid, inst, ItemPacketTrade); + } + else if (!stacked){ + LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->ItemClass Unknown! Type: %i", item->ItemClass); + } + QueuePacket(outapp); + if (inst && tmpmer_used){ + int32 new_charges = prevcharges - mp->quantity; + zone->SaveTempItem(merchantid, tmp->GetNPCTypeID(), item_id, new_charges); + if (new_charges <= 0){ + EQApplicationPacket* delitempacket = new EQApplicationPacket(OP_ShopDelItem, sizeof(Merchant_DelItem_Struct)); + Merchant_DelItem_Struct* delitem = (Merchant_DelItem_Struct*)delitempacket->pBuffer; + delitem->itemslot = mp->itemslot; + delitem->npcid = mp->npcid; + delitem->playerid = mp->playerid; + delitempacket->priority = 6; + entity_list.QueueClients(tmp, delitempacket); //que for anyone that could be using the merchant so they see the update + safe_delete(delitempacket); + } + else { + // Update the charges/quantity in the merchant window + inst->SetCharges(new_charges); + inst->SetPrice(SinglePrice); + inst->SetMerchantSlot(mp->itemslot); + inst->SetMerchantCount(new_charges); + + SendItemPacket(mp->itemslot, inst, ItemPacketMerchant); + } + } + safe_delete(inst); + safe_delete(outapp); + + // start QS code + if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); + QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; + + qsaudit->zone_id = zone->GetZoneID(); + qsaudit->merchant_id = tmp->CastToNPC()->MerchantType; + qsaudit->merchant_money.platinum = 0; + qsaudit->merchant_money.gold = 0; + qsaudit->merchant_money.silver = 0; + qsaudit->merchant_money.copper = 0; + qsaudit->merchant_count = 1; + qsaudit->char_id = character_id; + qsaudit->char_money.platinum = (mpo->price / 1000); + qsaudit->char_money.gold = (mpo->price / 100) % 10; + qsaudit->char_money.silver = (mpo->price / 10) % 10; + qsaudit->char_money.copper = mpo->price % 10; + qsaudit->char_count = 0; + + qsaudit->items[0].char_slot = freeslotid; + qsaudit->items[0].item_id = m_inv[freeslotid]->GetID(); + qsaudit->items[0].charges = mpo->quantity; + qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(1); + qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(2); + qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(3); + qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(4); + qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(5); + + qspack->Deflate(); + if (worldserver.Connected()) { worldserver.SendPacket(qspack); } + safe_delete(qspack); + } + // end QS code + + if (RuleB(EventLog, RecordBuyFromMerchant)) + LogMerchant(this, tmp, mpo->quantity, mpo->price, item, true); + + if ((RuleB(Character, EnableDiscoveredItems))) + { + if (!GetGM() && !IsDiscovered(item_id)) + DiscoverItem(item_id); + } + + t1.stop(); + std::cout << "At 1: " << t1.getDuration() << std::endl; + return; +} + +void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Purchase_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size on OP_ShopPlayerSell: Expected %i, Got %i", + sizeof(Merchant_Purchase_Struct), app->size); + return; + } + RDTSC_Timer t1(true); + Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer; + + Mob* vendor = entity_list.GetMob(mp->npcid); + + if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != MERCHANT) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*vendor) > USE_NPC_RANGE2) + return; + + uint32 price = 0; + uint32 itemid = GetItemIDAt(mp->itemslot); + if (itemid == 0) + return; + const Item_Struct* item = database.GetItem(itemid); + ItemInst* inst = GetInv().GetItem(mp->itemslot); + if (!item || !inst){ + Message(13, "You seemed to have misplaced that item.."); + return; + } + if (mp->quantity > 1) + { + if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges())) + return; + } + + if (!item->NoDrop) { + //Message(13,"%s tells you, 'LOL NOPE'", vendor->GetName()); + return; + } + + int cost_quantity = mp->quantity; + if (inst->IsCharged()) + int cost_quantity = 1; + + if (RuleB(Merchant, UsePriceMod)) + price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price + else + price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod)) + 0.5); + AddMoneyToPP(price, false); + + if (inst->IsStackable() || inst->IsCharged()) + { + unsigned int i_quan = inst->GetCharges(); + if (mp->quantity > i_quan || inst->IsCharged()) + mp->quantity = i_quan; + } + else + mp->quantity = 1; + + if (RuleB(EventLog, RecordSellToMerchant)) + LogMerchant(this, vendor, mp->quantity, price, item, false); + + int charges = mp->quantity; + //Hack workaround so usable items with 0 charges aren't simply deleted + if (charges == 0 && item->ItemType != 11 && item->ItemType != 17 && item->ItemType != 19 && item->ItemType != 21) + charges = 1; + + int freeslot = 0; + if (charges > 0 && (freeslot = zone->SaveTempItem(vendor->CastToNPC()->MerchantType, vendor->GetNPCTypeID(), itemid, charges, true)) > 0){ + ItemInst* inst2 = inst->Clone(); + if (RuleB(Merchant, UsePriceMod)){ + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(vendor, false)); + } + else + inst2->SetPrice(item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate); + inst2->SetMerchantSlot(freeslot); + + uint32 MerchantQuantity = zone->GetTempMerchantQuantity(vendor->GetNPCTypeID(), freeslot); + + if (inst2->IsStackable()) { + inst2->SetCharges(MerchantQuantity); + } + inst2->SetMerchantCount(MerchantQuantity); + + SendItemPacket(freeslot - 1, inst2, ItemPacketMerchant); + safe_delete(inst2); + } + + // start QS code + if (RuleB(QueryServ, PlayerLogMerchantTransactions)) { + ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogMerchantTransactions, sizeof(QSMerchantLogTransaction_Struct)+sizeof(QSTransactionItems_Struct)); + QSMerchantLogTransaction_Struct* qsaudit = (QSMerchantLogTransaction_Struct*)qspack->pBuffer; + + qsaudit->zone_id = zone->GetZoneID(); + qsaudit->merchant_id = vendor->CastToNPC()->MerchantType; + qsaudit->merchant_money.platinum = (price / 1000); + qsaudit->merchant_money.gold = (price / 100) % 10; + qsaudit->merchant_money.silver = (price / 10) % 10; + qsaudit->merchant_money.copper = price % 10; + qsaudit->merchant_count = 0; + qsaudit->char_id = character_id; + qsaudit->char_money.platinum = 0; + qsaudit->char_money.gold = 0; + qsaudit->char_money.silver = 0; + qsaudit->char_money.copper = 0; + qsaudit->char_count = 1; + + qsaudit->items[0].char_slot = mp->itemslot; + qsaudit->items[0].item_id = itemid; + qsaudit->items[0].charges = charges; + qsaudit->items[0].aug_1 = m_inv[mp->itemslot]->GetAugmentItemID(1); + qsaudit->items[0].aug_2 = m_inv[mp->itemslot]->GetAugmentItemID(2); + qsaudit->items[0].aug_3 = m_inv[mp->itemslot]->GetAugmentItemID(3); + qsaudit->items[0].aug_4 = m_inv[mp->itemslot]->GetAugmentItemID(4); + qsaudit->items[0].aug_5 = m_inv[mp->itemslot]->GetAugmentItemID(5); + + qspack->Deflate(); + if (worldserver.Connected()) { worldserver.SendPacket(qspack); } + safe_delete(qspack); + } + // end QS code + + // Now remove the item from the player, this happens regardless of outcome + if (!inst->IsStackable()) + this->DeleteItemInInventory(mp->itemslot, 0, false); + else + this->DeleteItemInInventory(mp->itemslot, mp->quantity, false); + + //This forces the price to show up correctly for charged items. + if (inst->IsCharged()) + mp->quantity = 1; + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct)); + Merchant_Purchase_Struct* mco = (Merchant_Purchase_Struct*)outapp->pBuffer; + mco->npcid = vendor->GetID(); + mco->itemslot = mp->itemslot; + mco->quantity = mp->quantity; + mco->price = price; + QueuePacket(outapp); + safe_delete(outapp); + SendMoneyUpdate(); + t1.start(); + Save(1); + t1.stop(); + std::cout << "Save took: " << t1.getDuration() << std::endl; + return; +} + +void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Merchant_Click_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_ShopRequest, size=%i, expected %i", app->size, sizeof(Merchant_Click_Struct)); + return; + } + + Merchant_Click_Struct* mc = (Merchant_Click_Struct*)app->pBuffer; + + // Send back opcode OP_ShopRequest - tells client to open merchant window. + //EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + //Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer; + int merchantid = 0; + Mob* tmp = entity_list.GetMob(mc->npcid); + + if (tmp == 0 || !tmp->IsNPC() || tmp->GetClass() != MERCHANT) + return; + + //you have to be somewhat close to them to be properly using them + if (DistNoRoot(*tmp) > USE_NPC_RANGE2) + return; + + merchantid = tmp->CastToNPC()->MerchantType; + + int action = 1; + if (merchantid == 0) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; + mco->npcid = mc->npcid; + mco->playerid = 0; + mco->command = 1; //open... + mco->rate = 1.0; + QueuePacket(outapp); + safe_delete(outapp); + return; + } + if (tmp->IsEngaged()){ + this->Message_StringID(0, MERCHANT_BUSY); + action = 0; + } + if (GetFeigned() || IsInvisible()) + { + Message(0, "You cannot use a merchant right now."); + action = 0; + } + int primaryfaction = tmp->CastToNPC()->GetPrimaryFaction(); + int factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), primaryfaction, tmp); + if (factionlvl >= 7) { + MerchantRejectMessage(tmp, primaryfaction); + action = 0; + } + + if (tmp->Charmed()) + action = 0; + + // 1199 I don't have time for that now. etc + if (!tmp->CastToNPC()->IsMerchantOpen()) { + tmp->Say_StringID(MakeRandomInt(1199, 1202)); + action = 0; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopRequest, sizeof(Merchant_Click_Struct)); + Merchant_Click_Struct* mco = (Merchant_Click_Struct*)outapp->pBuffer; + + mco->npcid = mc->npcid; + mco->playerid = 0; + mco->command = action; // Merchant command 0x01 = open + if (RuleB(Merchant, UsePriceMod)){ + mco->rate = 1 / ((RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(tmp, true)); // works + } + else + mco->rate = 1 / (RuleR(Merchant, BuyCostMod)); + + outapp->priority = 6; + QueuePacket(outapp); + safe_delete(outapp); + + if (action == 1) + BulkSendMerchantInventory(merchantid, tmp->GetNPCTypeID()); + + return; +} + +void Client::Handle_OP_Sneak(const EQApplicationPacket *app) +{ + if (!HasSkill(SkillSneak) && GetSkill(SkillSneak) == 0) { + return; //You cannot sneak if you do not have sneak + } + + if (!p_timers.Expired(&database, pTimerSneak, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerSneak, SneakReuseTime - 1); + + bool was = sneaking; + if (sneaking){ + sneaking = false; + hidden = false; + improved_hidden = false; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x03; + sa_out->parameter = 0; + entity_list.QueueClients(this, outapp, true); + safe_delete(outapp); + } + else { + CheckIncreaseSkill(SkillSneak, nullptr, 5); + } + float hidechance = ((GetSkill(SkillSneak) / 300.0f) + .25) * 100; + float random = MakeRandomFloat(0, 99); + if (!was && random < hidechance) { + sneaking = true; + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; + sa_out->spawn_id = GetID(); + sa_out->type = 0x0F; + sa_out->parameter = sneaking; + QueuePacket(outapp); + safe_delete(outapp); + if (GetClass() == ROGUE){ + outapp = new EQApplicationPacket(OP_SimpleMessage, 12); + SimpleMessage_Struct *msg = (SimpleMessage_Struct *)outapp->pBuffer; + msg->color = 0x010E; + if (sneaking){ + msg->string_id = 347; + } + else { + msg->string_id = 348; + } + FastQueuePacket(&outapp); + } + return; +} + +void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) +{ + if (app->size != sizeof(SpawnAppearance_Struct)) { + std::cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << std::endl; + return; + } + SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer; + + if (sa->spawn_id != GetID()) + return; + + if (sa->type == AT_Invis) { + if (sa->parameter != 0) + { + if (!HasSkill(SkillHide) && GetSkill(SkillHide) == 0) + { + if (GetClientVersion() < EQClientSoF) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Invis: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + } + return; + } + invisible = false; + hidden = false; + improved_hidden = false; + entity_list.QueueClients(this, app, true); + return; + } + else if (sa->type == AT_Anim) { + if (IsAIControlled()) + return; + if (sa->parameter == ANIM_STAND) { + SetAppearance(eaStanding); + playeraction = 0; + SetFeigned(false); + BindWound(this, false, true); + camp_timer.Disable(); + } + else if (sa->parameter == ANIM_SIT) { + SetAppearance(eaSitting); + playeraction = 1; + if (!UseBardSpellLogic()) + InterruptSpell(); + SetFeigned(false); + BindWound(this, false, true); + } + else if (sa->parameter == ANIM_CROUCH) { + if (!UseBardSpellLogic()) + InterruptSpell(); + SetAppearance(eaCrouching); + playeraction = 2; + SetFeigned(false); + } + else if (sa->parameter == ANIM_DEATH) { // feign death too + SetAppearance(eaDead); + playeraction = 3; + InterruptSpell(); + } + else if (sa->parameter == ANIM_LOOT) { + SetAppearance(eaLooting); + playeraction = 4; + SetFeigned(false); + } + + // This is from old code + // I have no clue what it's for + /* + else if (sa->parameter == 0x05) { + // Illusion + std::cout << "Illusion packet recv'd:" << std::endl; + DumpPacket(app); + } + */ + else { + std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl; + return; + } + + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Anon) { + // For Anon/Roleplay + if (sa->parameter == 1) { // Anon + m_pp.anon = 1; + } + else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp + m_pp.anon = 2; + } + else if (sa->parameter == 0) { // This is Non-Anon + m_pp.anon = 0; + } + else { + std::cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << std::endl; + return; + } + entity_list.QueueClients(this, app, true); + UpdateWho(); + } + else if ((sa->type == AT_HP) && (dead == 0)) { + return; + } + else if (sa->type == AT_AFK) { + this->AFK = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Split) { + m_pp.autosplit = (sa->parameter == 1); + } + else if (sa->type == AT_Sneak) { + if (sa->parameter != 0) + { + if (!HasSkill(SkillSneak)) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Sneak: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + return; + } + this->sneaking = 0; + entity_list.QueueClients(this, app, true); + } + else if (sa->type == AT_Size) + { + char *hack_str = nullptr; + MakeAnyLenString(&hack_str, "Player sent OP_SpawnAppearance with AT_Size: %i", sa->parameter); + database.SetMQDetectionFlag(this->account_name, this->name, hack_str, zone->GetShortName()); + safe_delete_array(hack_str); + } + else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield) + { + entity_list.QueueClients(this, app, false); + } + else if (sa->type == AT_Levitate) + { + // don't do anything with this, we tell the client when it's + // levitating, not the other way around + } + else if (sa->type == AT_ShowHelm) + { + m_pp.showhelm = (sa->parameter == 1); + entity_list.QueueClients(this, app, true); + } + else { + std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec + << " value: 0x" << std::hex << std::setw(8) << std::setfill('0') << sa->parameter << std::dec << std::endl; + } + return; +} + +void Client::Handle_OP_Split(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Split_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_Split, size=%i, expected %i", app->size, sizeof(Split_Struct)); + return; + } + // The client removes the money on its own, but we have to + // update our state anyway, and make sure they had enough to begin + // with. + Split_Struct *split = (Split_Struct *)app->pBuffer; + //Per the note above, Im not exactly sure what to do on error + //to notify the client of the error... + if (!isgrouped) { + Message(13, "You can not split money if your not in a group."); + return; + } + Group *cgroup = GetGroup(); + if (cgroup == nullptr) { + //invalid group, not sure if we should say more... + Message(13, "You can not split money if your not in a group."); + return; + } + + if (!TakeMoneyFromPP(static_cast(split->copper) + + 10 * static_cast(split->silver) + + 100 * static_cast(split->gold) + + 1000 * static_cast(split->platinum))) { + Message(13, "You do not have enough money to do that split."); + return; + } + cgroup->SplitMoney(split->copper, split->silver, split->gold, split->platinum); + + return; + +} + +void Client::Handle_OP_Surname(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Surname_Struct)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in Surname expected %i got %i", sizeof(Surname_Struct), app->size); + return; + } + + if (!p_timers.Expired(&database, pTimerSurnameChange, false) && !GetGM()) + { + Message(15, "You may only change surnames once every 7 days, your /surname is currently on cooldown."); + return; + } + + if (GetLevel() < 20) + { + Message_StringID(15, SURNAME_LEVEL); + return; + } + + Surname_Struct* surname = (Surname_Struct*)app->pBuffer; + + char *c = nullptr; + bool first = true; + for (c = surname->lastname; *c; c++) + { + if (first) + { + *c = toupper(*c); + first = false; + } + else + { + *c = tolower(*c); + } + } + + if (strlen(surname->lastname) >= 20) { + Message_StringID(15, SURNAME_TOO_LONG); + return; + } + + if (!database.CheckNameFilter(surname->lastname, true)) + { + Message_StringID(15, SURNAME_REJECTED); + return; + } + + ChangeLastName(surname->lastname); + p_timers.Start(pTimerSurnameChange, 604800); + + EQApplicationPacket* outapp = app->Copy(); + outapp = app->Copy(); + surname = (Surname_Struct*)outapp->pBuffer; + surname->unknown0064 = 1; + FastQueuePacket(&outapp); + return; +} + +void Client::Handle_OP_SwapSpell(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(SwapSpell_Struct)) { + std::cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << std::endl; + return; + } + const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*)app->pBuffer; + int swapspelltemp; + + if (swapspell->from_slot < 0 || swapspell->from_slot > MAX_PP_SPELLBOOK || swapspell->to_slot < 0 || swapspell->to_slot > MAX_PP_SPELLBOOK) + return; + + swapspelltemp = m_pp.spell_book[swapspell->from_slot]; + if (swapspelltemp < 0){ + return; + } + m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot]; + m_pp.spell_book[swapspell->to_slot] = swapspelltemp; + + database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); + database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot); + + QueuePacket(app); + return; +} + +void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientTarget_Struct)) { + LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size); + return; + } + + if (GetTarget()) + { + GetTarget()->IsTargeted(-1); + } + + // Locate and cache new target + ClientTarget_Struct* ct = (ClientTarget_Struct*)app->pBuffer; + pClientSideTarget = ct->new_target; + if (!IsAIControlled()) + { + Mob *nt = entity_list.GetMob(ct->new_target); + if (nt) + { + SetTarget(nt); + bool inspect_buffs = false; + // rank 1 gives you ability to see NPC buffs in target window (SoD+) + if (nt->IsNPC()) { + if (IsRaidGrouped()) { + Raid *raid = GetRaid(); + if (raid) { + uint32 gid = raid->GetGroup(this); + if (gid < 12 && raid->GroupCount(gid) > 2) + inspect_buffs = raid->GetLeadershipAA(groupAAInspectBuffs, gid); + } + } else { + Group *group = GetGroup(); + if (group && group->GroupCount() > 2) + inspect_buffs = group->GetLeadershipAA(groupAAInspectBuffs); + } + } + if (nt == this || inspect_buffs || (nt->IsClient() && !nt->CastToClient()->GetPVP()) || + (nt->IsPet() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP()) || + (nt->IsMerc() && nt->GetOwner() && nt->GetOwner()->IsClient() && !nt->GetOwner()->CastToClient()->GetPVP())) + nt->SendBuffsToClient(this); + } + else + { + SetTarget(nullptr); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + + Group *g = GetGroup(); + + if (g && g->HasRole(this, RoleAssist)) + g->SetGroupAssistTarget(0); + + if (g && g->HasRole(this, RoleTank)) + g->SetGroupTankTarget(0); + + if (g && g->HasRole(this, RolePuller)) + g->SetGroupPullerTarget(0); + + return; + } + } + else + { + SetTarget(nullptr); + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + return; + } + + // HoTT + if (GetTarget() && GetTarget()->GetTarget()) + { + SetHoTT(GetTarget()->GetTarget()->GetID()); + UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); + } + else + { + SetHoTT(0); + UpdateXTargetType(TargetsTarget, nullptr); + } + + Group *g = GetGroup(); + + if (g && g->HasRole(this, RoleAssist)) + g->SetGroupAssistTarget(GetTarget()); + + if (g && g->HasRole(this, RoleTank)) + g->SetGroupTankTarget(GetTarget()); + + if (g && g->HasRole(this, RolePuller)) + g->SetGroupPullerTarget(GetTarget()); + + // For /target, send reject or success packet + if (app->GetOpcode() == OP_TargetCommand) { + if (GetTarget() && !GetTarget()->CastToMob()->IsInvisible(this) && (DistNoRoot(*GetTarget()) <= TARGETING_RANGE*TARGETING_RANGE || GetGM())) { + if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special + || GetTarget()->GetBodyType() == BT_NoTarget) + { + //Targeting something we shouldn't with /target + //but the client allows this without MQ so you don't flag it + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); + outapp->pBuffer[0] = 0x2f; + outapp->pBuffer[1] = 0x01; + outapp->pBuffer[4] = 0x0d; + if (GetTarget()) + { + SetTarget(nullptr); + } + QueuePacket(outapp); + safe_delete(outapp); + return; + } + + QueuePacket(app); + EQApplicationPacket hp_app; + GetTarget()->IsTargeted(1); + GetTarget()->CreateHPPacket(&hp_app); + QueuePacket(&hp_app, false); + } + else + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TargetReject, sizeof(TargetReject_Struct)); + outapp->pBuffer[0] = 0x2f; + outapp->pBuffer[1] = 0x01; + outapp->pBuffer[4] = 0x0d; + if (GetTarget()) + { + SetTarget(nullptr); + } + QueuePacket(outapp); + safe_delete(outapp); + } + } + else + { + if (GetTarget()) + { + if (GetGM()) + { + GetTarget()->IsTargeted(1); + return; + } + else if (IsAssistExempted()) + { + GetTarget()->IsTargeted(1); + SetAssistExemption(false); + return; + } + else if (GetTarget()->IsClient()) + { + //make sure this client is in our raid/group + GetTarget()->IsTargeted(1); + return; + } + else if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special + || GetTarget()->GetBodyType() == BT_NoTarget) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something untargetable, %s bodytype: %i\n", + GetName(), GetTarget()->GetName(), (int)GetTarget()->GetBodyType()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget((Mob*)nullptr); + return; + } + else if (IsPortExempted()) + { + GetTarget()->IsTargeted(1); + return; + } + else if (IsSenseExempted()) + { + GetTarget()->IsTargeted(1); + SetSenseExemption(false); + return; + } + else if (IsXTarget(GetTarget())) + { + GetTarget()->IsTargeted(1); + return; + } + else if (GetBindSightTarget()) + { + if (GetBindSightTarget()->DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," + " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), + (zone->newzone_data.maxclip*zone->newzone_data.maxclip), + GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget(nullptr); + return; + } + } + } + else if (DistNoRoot(*GetTarget()) > (zone->newzone_data.maxclip*zone->newzone_data.maxclip)) + { + char *hacker_str = nullptr; + MakeAnyLenString(&hacker_str, "%s attempting to target something beyond the clip plane of %.2f units," + " from (%.2f, %.2f, %.2f) to %s (%.2f, %.2f, %.2f)", GetName(), + (zone->newzone_data.maxclip*zone->newzone_data.maxclip), + GetX(), GetY(), GetZ(), GetTarget()->GetName(), GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ()); + database.SetMQDetectionFlag(AccountName(), GetName(), hacker_str, zone->GetShortName()); + safe_delete_array(hacker_str); + SetTarget(nullptr); + return; + } + + GetTarget()->IsTargeted(1); + } + } + return; +} + +void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) +{ + Handle_OP_TargetCommand(app); +} + +void Client::Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(TaskHistoryRequest_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_TaskHistoryRequest expected %i got %i", + sizeof(TaskHistoryRequest_Struct), app->size); + DumpPacket(app); + return; + } + TaskHistoryRequest_Struct *ths = (TaskHistoryRequest_Struct*)app->pBuffer; + + if (RuleB(TaskSystem, EnableTaskSystem) && taskstate) + taskstate->SendTaskHistory(this, ths->TaskIndex); +} + +void Client::Handle_OP_Taunt(const EQApplicationPacket *app) +{ + if (app->size != sizeof(ClientTarget_Struct)) { + std::cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: " << sizeof(ClientTarget_Struct) << std::endl; + return; + } + + if (!p_timers.Expired(&database, pTimerTaunt, false)) { + Message(13, "Ability recovery time not yet met."); + return; + } + p_timers.Start(pTimerTaunt, TauntReuseTime - 1); + + if (GetTarget() == nullptr || !GetTarget()->IsNPC()) + return; + + Taunt(GetTarget()->CastToNPC(), false); + return; +} + +void Client::Handle_OP_TestBuff(const EQApplicationPacket *app) +{ + return; +} + +void Client::Handle_OP_TGB(const EQApplicationPacket *app) +{ + OPTGB(app); + return; +} + +void Client::Handle_OP_Track(const EQApplicationPacket *app) +{ + if (GetClass() != RANGER && GetClass() != DRUID && GetClass() != BARD) + return; + + if (GetSkill(SkillTracking) == 0) + SetSkill(SkillTracking, 1); + else + CheckIncreaseSkill(SkillTracking, nullptr, 15); + + if (!entity_list.MakeTrackPacket(this)) + LogFile->write(EQEMuLog::Error, "Unable to generate OP_Track packet requested by client."); + + return; +} + +void Client::Handle_OP_TrackTarget(const EQApplicationPacket *app) +{ + int PlayerClass = GetClass(); + + if ((PlayerClass != RANGER) && (PlayerClass != DRUID) && (PlayerClass != BARD)) + return; + + if (app->size != sizeof(TrackTarget_Struct)) + { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_TrackTarget: Expected: %i, Got: %i", + sizeof(TrackTarget_Struct), app->size); + return; + } + + TrackTarget_Struct *tts = (TrackTarget_Struct*)app->pBuffer; + + TrackingID = tts->EntityID; +} + +void Client::Handle_OP_TrackUnknown(const EQApplicationPacket *app) +{ + // size 0 send right after OP_Track + return; +} + +void Client::Handle_OP_TradeAcceptClick(const EQApplicationPacket *app) +{ + Mob* with = trade->With(); + trade->state = TradeAccepted; + + if (with && with->IsClient()) { + //finish trade... + // Have both accepted? + Client* other = with->CastToClient(); + other->QueuePacket(app); + + if (other->trade->state == trade->state) { + other->trade->state = TradeCompleting; + trade->state = TradeCompleting; + + // should we do this for NoDrop items as well? + if (CheckTradeLoreConflict(other) || other->CheckTradeLoreConflict(this)) { + Message_StringID(13, TRADE_CANCEL_LORE); + other->Message_StringID(13, TRADE_CANCEL_LORE); + this->FinishTrade(this); + other->FinishTrade(other); + other->trade->Reset(); + trade->Reset(); + } + else { + // Audit trade to database for both trade streams + other->trade->LogTrade(); + trade->LogTrade(); + + // start QS code + if (RuleB(QueryServ, PlayerLogTrades)) { + QSPlayerLogTrade_Struct event_entry; + std::list event_details; + + memset(&event_entry, 0, sizeof(QSPlayerLogTrade_Struct)); + + // Perform actual trade + this->FinishTrade(other, true, &event_entry, &event_details); + other->FinishTrade(this, false, &event_entry, &event_details); + + event_entry._detail_count = event_details.size(); + + ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct)+(sizeof(QSTradeItems_Struct)* event_entry._detail_count)); + QSPlayerLogTrade_Struct* qs_buf = (QSPlayerLogTrade_Struct*)qs_pack->pBuffer; + + memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogTrade_Struct)); + + int offset = 0; + + for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { + QSTradeItems_Struct* detail = reinterpret_cast(*iter); + qs_buf->items[offset] = *detail; + safe_delete(detail); + } + + event_details.clear(); + + qs_pack->Deflate(); + + if (worldserver.Connected()) + worldserver.SendPacket(qs_pack); + + safe_delete(qs_pack); + // end QS code + } + else { + this->FinishTrade(other); + other->FinishTrade(this); + } + + other->trade->Reset(); + trade->Reset(); + } + // All done + EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); + other->QueuePacket(outapp); + this->FastQueuePacket(&outapp); + } + } + // Trading with a Mob object that is not a Client. + else if (with) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_FinishTrade, 0); + QueuePacket(outapp); + safe_delete(outapp); + if (with->IsNPC()) { + // Audit trade to database for player trade stream + if (RuleB(QueryServ, PlayerLogHandins)) { + QSPlayerLogHandin_Struct event_entry; + std::list event_details; + + memset(&event_entry, 0, sizeof(QSPlayerLogHandin_Struct)); + + FinishTrade(with->CastToNPC(), false, &event_entry, &event_details); + + event_entry._detail_count = event_details.size(); + + ServerPacket* qs_pack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct)+(sizeof(QSHandinItems_Struct)* event_entry._detail_count)); + QSPlayerLogHandin_Struct* qs_buf = (QSPlayerLogHandin_Struct*)qs_pack->pBuffer; + + memcpy(qs_buf, &event_entry, sizeof(QSPlayerLogHandin_Struct)); + + int offset = 0; + + for (std::list::iterator iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { + QSHandinItems_Struct* detail = reinterpret_cast(*iter); + qs_buf->items[offset] = *detail; + safe_delete(detail); + } + + event_details.clear(); + + qs_pack->Deflate(); + + if (worldserver.Connected()) + worldserver.SendPacket(qs_pack); + + safe_delete(qs_pack); + } + else { + FinishTrade(with->CastToNPC()); + } + } +#ifdef BOTS + // TODO: Log Bot trades + else if (with->IsBot()) + with->CastToBot()->FinishTrade(this, Bot::BotTradeClientNormal); +#endif + trade->Reset(); + } + + + return; +} + +void Client::Handle_OP_TradeBusy(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeBusy_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeBusy, size=%i, expected %i", app->size, sizeof(TradeBusy_Struct)); + return; + } + // Trade request recipient is cancelling the trade due to being busy + // Trade requester gets message "I'm busy right now" + // Send busy message on to trade initiator if client + TradeBusy_Struct* msg = (TradeBusy_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + tradee->CastToClient()->QueuePacket(app); + } + return; +} + +void Client::Handle_OP_Trader(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed. + // I don't know what they are for (yet), but it doesn't seem to matter that we ignore them. + + _pkt(TRADING__PACKETS, app); + + uint32 max_items = 80; + + /* + if (GetClientVersion() >= EQClientRoF) + max_items = 200; + */ + + //Show Items + if (app->size == sizeof(Trader_ShowItems_Struct)) + { + Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer; + + switch (sis->Code) + { + case BazaarTrader_EndTraderMode: { + Trader_EndTrader(); + break; + } + case BazaarTrader_EndTransaction: { + + Client* c = entity_list.GetClientByID(sis->TraderID); + if (c) + c->WithCustomer(0); + else + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); + + break; + } + case BazaarTrader_ShowItems: { + Trader_ShowItems(); + break; + } + default: { + _log(TRADING__CLIENT, "Unhandled action code in OP_Trader ShowItems_Struct"); + break; + } + } + } + else if (app->size == sizeof(ClickTrader_Struct)) + { + if (Buyer) { + Trader_EndTrader(); + Message(13, "You cannot be a Trader and Buyer at the same time."); + return; + } + + ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer; + + if (ints->Code == BazaarTrader_StartTraderMode) + { + GetItems_Struct* gis = GetTraderItems(); + + // Verify there are no NODROP or items with a zero price + bool TradeItemsValid = true; + + for (uint32 i = 0; i < max_items; i++) { + + if (gis->Items[i] == 0) break; + + if (ints->ItemCost[i] == 0) { + Message(13, "Item in Trader Satchel with no price. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + const Item_Struct *Item = database.GetItem(gis->Items[i]); + + if (!Item) { + Message(13, "Unexpected error. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + + if (Item->NoDrop == 0) { + Message(13, "NODROP Item in Trader Satchel. Unable to start trader mode"); + TradeItemsValid = false; + break; + } + } + + if (!TradeItemsValid) { + Trader_EndTrader(); + return; + } + + for (uint32 i = 0; i < max_items; i++) { + if (database.GetItem(gis->Items[i])) { + database.SaveTraderItem(this->CharacterID(), gis->Items[i], gis->SerialNumber[i], + gis->Charges[i], ints->ItemCost[i], i); + } + else { + //return; //sony doesnt memset so assume done on first bad item + break; + } + + } + safe_delete(gis); + + this->Trader_StartTrader(); + + if (GetClientVersion() >= EQClientRoF) + { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct)); + TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer; + tss->Code = BazaarTrader_StartTraderMode2; + QueuePacket(outapp); + _pkt(TRADING__PACKETS, outapp); + safe_delete(outapp); + } + } + else if (app->size == sizeof(TraderStatus_Struct)) + { + TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer; + + if (tss->Code == BazaarTrader_ShowItems) + { + Trader_ShowItems(); + } + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n", + ints->Code); + + LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->Code); + } + } + + else if (app->size == sizeof(TraderPriceUpdate_Struct)) + { + HandleTraderPriceUpdate(app); + } + else { + _log(TRADING__CLIENT, "Unknown size for OP_Trader: %i\n", app->size); + LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size); + DumpPacket(app); + return; + } + + return; +} + +void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // Client has elected to buy an item from a Trader + // + _pkt(TRADING__PACKETS, app); + + if (app->size == sizeof(TraderBuy_Struct)){ + + TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer; + + if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){ + + BuyTraderItem(tbs, Trader, app); + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Null Client Pointer"); + } + } + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderBuy: Struct size mismatch"); + + } + return; +} + +void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequest, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + return; + } + // Client requesting a trade session from an npc/client + // Trade session not started until OP_TradeRequestAck is sent + + BreakInvis(); + + // Pass trade request on to recipient + TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + tradee->CastToClient()->QueuePacket(app); + } +#ifndef BOTS + else if (tradee && tradee->IsNPC()) { +#else + else if (tradee && (tradee->IsNPC() || tradee->IsBot())) { +#endif + //npcs always accept + trade->Start(msg->to_mob_id); + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeRequestAck, sizeof(TradeRequest_Struct)); + TradeRequest_Struct* acc = (TradeRequest_Struct*)outapp->pBuffer; + acc->from_mob_id = msg->to_mob_id; + acc->to_mob_id = msg->from_mob_id; + FastQueuePacket(&outapp); + safe_delete(outapp); + } + return; +} + +void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app) +{ + if (app->size != sizeof(TradeRequest_Struct)) { + LogFile->write(EQEMuLog::Error, "Wrong size: OP_TradeRequestAck, size=%i, expected %i", app->size, sizeof(TradeRequest_Struct)); + return; + } + // Trade request recipient is acknowledging they are able to trade + // After this, the trade session has officially started + // Send ack on to trade initiator if client + TradeRequest_Struct* msg = (TradeRequest_Struct*)app->pBuffer; + Mob* tradee = entity_list.GetMob(msg->to_mob_id); + + if (tradee && tradee->IsClient()) { + trade->Start(msg->to_mob_id); + tradee->CastToClient()->QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TraderShop(const EQApplicationPacket *app) +{ + // Bazaar Trader: + // + // This is when a potential purchaser right clicks on this client who is in Trader mode to + // browse their goods. + // + _pkt(TRADING__PACKETS, app); + + TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer; + + if (app->size != sizeof(TraderClick_Struct)) { + + _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch"); + + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct)); + + TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer; + + Client* Customer = entity_list.GetClientByID(tcs->TraderID); + + if (Customer) + outtcs->Approval = Customer->WithCustomer(GetID()); + else { + _log(TRADING__CLIENT, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)" + " returned a nullptr pointer"); + return; + } + + outtcs->TraderID = tcs->TraderID; + + outtcs->Unknown008 = 0x3f800000; + + QueuePacket(outapp); + + _pkt(TRADING__PACKETS, outapp); + + if (outtcs->Approval) { + this->BulkSendTraderInventory(Customer->CharacterID()); + Customer->Trader_CustomerBrowsing(this); + } + else + Message_StringID(clientMessageYellow, TRADER_BUSY); + + safe_delete(outapp); + + return; +} + +void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app) +{ + if (app->size != sizeof(NewCombine_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i", + sizeof(NewCombine_Struct), app->size); + return; + } + /*if (m_tradeskill_object == nullptr) { + Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use"); + return; + }*/ + + //fixed this to work for non-world objects + + // Delegate to tradeskill object to perform combine + NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer; + Object::HandleCombine(this, in_combine, m_tradeskill_object); + return; +} + +void Client::Handle_OP_Translocate(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(Translocate_Struct)) { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Translocate expected %i got %i", sizeof(Translocate_Struct), app->size); + DumpPacket(app); + return; + } + Translocate_Struct *its = (Translocate_Struct*)app->pBuffer; + + if (!PendingTranslocate) return; + + if ((RuleI(Spells, TranslocateTimeLimit) > 0) && (time(nullptr) > (TranslocateTime + RuleI(Spells, TranslocateTimeLimit)))) { + Message(13, "You did not accept the Translocate within the required time limit."); + PendingTranslocate = false; + return; + } + + if (its->Complete == 1) { + + int SpellID = PendingTranslocateData.SpellID; + int i = parse->EventSpell(EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE, nullptr, this, SpellID, 0); + + if (i == 0) + { + // If the spell has a translocate to bind effect, AND we are already in the zone the client + // is bound in, use the GoToBind method. If we send OP_Translocate in this case, the client moves itself + // to the bind coords it has from the PlayerProfile, but with the X and Y reversed. I suspect they are + // reversed in the pp, and since spells like Gate are handled serverside, this has not mattered before. + if (((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) && + zone->GetZoneID() == PendingTranslocateData.ZoneID) + { + PendingTranslocate = false; + GoToBind(); + return; + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Translocate, sizeof(Translocate_Struct)); + Translocate_Struct *ots = (Translocate_Struct*)outapp->pBuffer; + memcpy(ots, &PendingTranslocateData, sizeof(Translocate_Struct)); + + //Was sending the packet back to initiate client zone... + //but that could be abusable, so lets go through proper channels + MovePC(ots->ZoneID, 0, ots->x, ots->y, ots->z, GetHeading(), 0, ZoneSolicited); + } + } + + PendingTranslocate = false; +} + +void Client::Handle_OP_TributeItem(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeItem of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //player donates an item... + if (app->size != sizeof(TributeItem_Struct)) + printf("Error in OP_TributeItem. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + TributeItem_Struct* t = (TributeItem_Struct*)app->pBuffer; + + tribute_master_id = t->tribute_master_id; + //make sure they are dealing with a valid tribute master + Mob* tribmast = entity_list.GetMob(t->tribute_master_id); + if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) + return; + if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) + return; + + t->tribute_points = TributeItem(t->slot, t->quantity); + + _log(TRIBUTE__OUT, "Sending tribute item reply with %d points", t->tribute_points); + _pkt(TRIBUTE__OUT, app); + + QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TributeMoney(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeMoney of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //player donates money + if (app->size != sizeof(TributeMoney_Struct)) + printf("Error in OP_TributeMoney. Expected size of: %zu, but got: %i\n", sizeof(StartTribute_Struct), app->size); + else { + TributeMoney_Struct* t = (TributeMoney_Struct*)app->pBuffer; + + tribute_master_id = t->tribute_master_id; + //make sure they are dealing with a valid tribute master + Mob* tribmast = entity_list.GetMob(t->tribute_master_id); + if (!tribmast || !tribmast->IsNPC() || tribmast->GetClass() != TRIBUTE_MASTER) + return; + if (DistNoRoot(*tribmast) > USE_NPC_RANGE2) + return; + + t->tribute_points = TributeMoney(t->platinum); + + _log(TRIBUTE__OUT, "Sending tribute money reply with %d points", t->tribute_points); + _pkt(TRIBUTE__OUT, app); + + QueuePacket(app); + } + return; +} + +void Client::Handle_OP_TributeNPC(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeNPC of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + return; +} + +void Client::Handle_OP_TributeToggle(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeToggle of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + if (app->size != sizeof(uint32)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeToggle packet"); + else { + uint32 *val = (uint32 *)app->pBuffer; + ToggleTribute(*val ? true : false); + } + return; +} + +void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app) +{ + _log(TRIBUTE__IN, "Received OP_TributeUpdate of length %d", app->size); + _pkt(TRIBUTE__IN, app); + + //sent when the client changes their tribute settings... + if (app->size != sizeof(TributeInfo_Struct)) + LogFile->write(EQEMuLog::Error, "Invalid size on OP_TributeUpdate packet"); + else { + TributeInfo_Struct *t = (TributeInfo_Struct *)app->pBuffer; + ChangeTributeSettings(t); + } + return; +} + +void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app) +{ + if (app->size < sizeof(VeteranClaimRequest)) + { + LogFile->write(EQEMuLog::Debug, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", + app->size, sizeof(VeteranClaimRequest)); + DumpPacket(app); + return; + } + + VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer; + + if (vcr->claim_id == 0xFFFFFFFF) //request update packet + { + SendRewards(); + } + else //try to claim something! + { + if (!TryReward(vcr->claim_id)) + { + Message(13, "Your claim has been rejected."); + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); + VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + cr->reject_field = -1; + FastQueuePacket(&vetapp); + } + else + { + EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply)); + VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer; + strcpy(cr->name, GetName()); + cr->claim_id = vcr->claim_id; + cr->reject_field = 0; + FastQueuePacket(&vetapp); + } + } +} + +void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app) +{ + + if (app->size != sizeof(VoiceMacroIn_Struct)) { + + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_VoiceMacroIn expected %i got %i", + sizeof(VoiceMacroIn_Struct), app->size); + + DumpPacket(app); + + return; + } + + if (!RuleB(Chat, EnableVoiceMacros)) return; + + VoiceMacroIn_Struct* vmi = (VoiceMacroIn_Struct*)app->pBuffer; + + VoiceMacroReceived(vmi->Type, vmi->Target, vmi->MacroNumber); + +} + +void Client::Handle_OP_WearChange(const EQApplicationPacket *app) +{ + if (app->size != sizeof(WearChange_Struct)) { + std::cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << std::endl; + return; + } + + WearChange_Struct* wc = (WearChange_Struct*)app->pBuffer; + if (wc->spawn_id != GetID()) + return; + + // we could maybe ignore this and just send our own from moveitem + entity_list.QueueClients(this, app, true); + return; +} + +void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app) +{ + if (app->size != sizeof(Who_All_Struct)) { + std::cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << std::endl; + return; + } + Who_All_Struct* whoall = (Who_All_Struct*)app->pBuffer; + + if (whoall->type == 0) // SoF only, for regular /who + entity_list.ZoneWho(this, whoall); + else + WhoAll(whoall); + return; +} + +void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) +{ + if (app->size != 1) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetAutoAddHaters, expected 1, got %i", app->size); + DumpPacket(app); + return; + } + + XTargetAutoAddHaters = app->ReadUInt8(0); +} + +void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) +{ + if (app->size < 12) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_XTargetRequest, expected at least 12, got %i", app->size); + DumpPacket(app); + return; + } + + uint32 Unknown000 = app->ReadUInt32(0); + + if (Unknown000 != 1) + return; + + uint32 Slot = app->ReadUInt32(4); + + if (Slot >= XTARGET_HARDCAP) + return; + + XTargetType Type = (XTargetType)app->ReadUInt32(8); + + XTargets[Slot].Type = Type; + XTargets[Slot].ID = 0; + XTargets[Slot].Name[0] = 0; + + switch (Type) + { + case Empty: + case Auto: + { + break; + } + + case CurrentTargetPC: + { + char Name[65]; + + app->ReadString(Name, 12, 64); + Client *c = entity_list.GetClientByName(Name); + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, Name, 64); + } + SendXTargetPacket(Slot, c); + + break; + } + + case CurrentTargetNPC: + { + char Name[65]; + app->ReadString(Name, 12, 64); + Mob *m = entity_list.GetMob(Name); + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + break; + } + } + + case TargetsTarget: + { + if (GetTarget()) + UpdateXTargetType(TargetsTarget, GetTarget()->GetTarget()); + else + UpdateXTargetType(TargetsTarget, nullptr); + + break; + } + + case GroupTank: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetMainTankName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetMainTankName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + case GroupTankTarget: + { + Group *g = GetGroup(); + + if (g) + g->NotifyTankTarget(this); + + break; + } + + case GroupAssist: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetMainAssistName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetMainAssistName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + + case GroupAssistTarget: + { + + Group *g = GetGroup(); + + if (g) + g->NotifyAssistTarget(this); + + break; + } + + case Puller: + { + Group *g = GetGroup(); + + if (g) + { + Client *c = entity_list.GetClientByName(g->GetPullerName()); + + if (c) + { + XTargets[Slot].ID = c->GetID(); + strncpy(XTargets[Slot].Name, c->GetName(), 64); + } + else + { + strncpy(XTargets[Slot].Name, g->GetPullerName(), 64); + } + SendXTargetPacket(Slot, c); + } + break; + } + + case PullerTarget: + { + + Group *g = GetGroup(); + + if (g) + g->NotifyPullerTarget(this); + + break; + } + + case GroupMarkTarget1: + case GroupMarkTarget2: + case GroupMarkTarget3: + { + Group *g = GetGroup(); + + if (g) + g->SendMarkedNPCsToMember(this); + + break; + } + + case RaidAssist1: + case RaidAssist2: + case RaidAssist3: + case RaidAssist1Target: + case RaidAssist2Target: + case RaidAssist3Target: + case RaidMarkTarget1: + case RaidMarkTarget2: + case RaidMarkTarget3: + { + // Not implemented yet. + break; + } + + case MyPet: + { + Mob *m = GetPet(); + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + + } + break; + } + case MyPetTarget: + { + Mob *m = GetPet(); + + if (m) + m = m->GetTarget(); + + if (m) + { + XTargets[Slot].ID = m->GetID(); + SendXTargetPacket(Slot, m); + + } + break; + } + + default: + LogFile->write(EQEMuLog::Debug, "Unhandled XTarget Type %i", Type); + break; + } + +} + +void Client::Handle_OP_YellForHelp(const EQApplicationPacket *app) +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_YellForHelp, 4); + *(uint32 *)outapp->pBuffer = GetID(); + entity_list.QueueCloseClients(this, outapp, true, 100.0); + safe_delete(outapp); + return; +} + +/* +void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) +{ + return; +} +*/ diff --git a/zone/client_packet.h b/zone/client_packet.h index 424a8f78f..0868b93ba 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -1,292 +1,295 @@ - void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app); - void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app); - void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app); - void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app); - void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app); - void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app); - void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app); + // connecting opcode handlers void Handle_Connect_0x3e33(const EQApplicationPacket *app); + void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientError(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); + void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); void Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app); + void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app); + void Handle_Connect_OP_SendAAStats(const EQApplicationPacket *app); + void Handle_Connect_OP_SendAATable(const EQApplicationPacket *app); void Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app); + void Handle_Connect_OP_SendGuildTributes(const EQApplicationPacket *app); + void Handle_Connect_OP_SendTributes(const EQApplicationPacket *app); + void Handle_Connect_OP_SetDataRate(const EQApplicationPacket *app); + void Handle_Connect_OP_SetServerFilter(const EQApplicationPacket *app); + void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app); + void Handle_Connect_OP_TGB(const EQApplicationPacket *app); + void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app); + void Handle_Connect_OP_WearChange(const EQApplicationPacket *app); void Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app); void Handle_Connect_OP_ZoneComplete(const EQApplicationPacket *app); - void Handle_Connect_OP_ReqNewZone(const EQApplicationPacket *app); - void Handle_Connect_OP_SpawnAppearance(const EQApplicationPacket *app); - void Handle_Connect_OP_WearChange(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientUpdate(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientError(const EQApplicationPacket *app); - void Handle_Connect_OP_ApproveZone(const EQApplicationPacket *app); - void Handle_Connect_OP_ClientReady(const EQApplicationPacket *app); - void Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app); - void Handle_Connect_OP_TGB(const EQApplicationPacket *app); - void Handle_OP_ClientUpdate(const EQApplicationPacket *app); - void Handle_OP_AutoAttack(const EQApplicationPacket *app); - void Handle_OP_AutoAttack2(const EQApplicationPacket *app); - void Handle_OP_Consent(const EQApplicationPacket *app); - void Handle_OP_ConsentDeny(const EQApplicationPacket *app); - void Handle_OP_TargetMouse(const EQApplicationPacket *app); - void Handle_OP_TargetCommand(const EQApplicationPacket *app); - void Handle_OP_Shielding(const EQApplicationPacket *app); - void Handle_OP_Jump(const EQApplicationPacket *app); - void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureRequest(const EQApplicationPacket *app); - void Handle_OP_LDoNButton(const EQApplicationPacket *app); - void Handle_OP_LeaveAdventure(const EQApplicationPacket *app); - void Handle_OP_Consume(const EQApplicationPacket *app); - void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app); - void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app); - void Handle_OP_Consider(const EQApplicationPacket *app); - void Handle_OP_Begging(const EQApplicationPacket *app); - void Handle_OP_TestBuff(const EQApplicationPacket *app); - void Handle_OP_Surname(const EQApplicationPacket *app); - void Handle_OP_ClearSurname(const EQApplicationPacket *app); - void Handle_OP_YellForHelp(const EQApplicationPacket *app); - void Handle_OP_Assist(const EQApplicationPacket *app); - void Handle_OP_AssistGroup(const EQApplicationPacket *app); - void Handle_OP_GMTraining(const EQApplicationPacket *app); - void Handle_OP_GMEndTraining(const EQApplicationPacket *app); - void Handle_OP_GMTrainSkill(const EQApplicationPacket *app); - void Handle_OP_DuelResponse(const EQApplicationPacket *app); - void Handle_OP_DuelResponse2(const EQApplicationPacket *app); - void Handle_OP_RequestDuel(const EQApplicationPacket *app); - void Handle_OP_SpawnAppearance(const EQApplicationPacket *app); - void Handle_OP_BazaarInspect(const EQApplicationPacket *app); - void Handle_OP_Death(const EQApplicationPacket *app); - void Handle_OP_MoveCoin(const EQApplicationPacket *app); - void Handle_OP_ItemLinkClick(const EQApplicationPacket *app); - void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); - void Handle_OP_MoveItem(const EQApplicationPacket *app); - void Handle_OP_Camp(const EQApplicationPacket *app); - void Handle_OP_Logout(const EQApplicationPacket *app); - void Handle_OP_SenseHeading(const EQApplicationPacket *app); - void Handle_OP_LDoNOpen(const EQApplicationPacket *app); - void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app); - void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app); - void Handle_OP_LDoNInspect(const EQApplicationPacket *app); - void Handle_OP_LDoNPickLock(const EQApplicationPacket *app); - void Handle_OP_FeignDeath(const EQApplicationPacket *app); - void Handle_OP_Sneak(const EQApplicationPacket *app); - void Handle_OP_Hide(const EQApplicationPacket *app); - void Handle_OP_ChannelMessage(const EQApplicationPacket *app); - void Handle_OP_WearChange(const EQApplicationPacket *app); - void Handle_OP_ZoneChange(const EQApplicationPacket *app); - void Handle_OP_DeleteSpawn(const EQApplicationPacket *app); - void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app); - void Handle_OP_Save(const EQApplicationPacket *app); - void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); - void Handle_OP_GMZoneRequest(const EQApplicationPacket *app); - void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app); - void Handle_OP_EndLootRequest(const EQApplicationPacket *app); - void Handle_OP_LootRequest(const EQApplicationPacket *app); - void Handle_OP_Dye(const EQApplicationPacket *app); - void Handle_OP_LootItem(const EQApplicationPacket *app); - void Handle_OP_GuildDelete(const EQApplicationPacket *app); - void Handle_OP_GuildPublicNote(const EQApplicationPacket *app); - void Handle_OP_GetGuildsList(const EQApplicationPacket *app); - void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app); - void Handle_OP_GuildPeace(const EQApplicationPacket *app); - void Handle_OP_GuildWar(const EQApplicationPacket *app); - void Handle_OP_GuildLeader(const EQApplicationPacket *app); - void Handle_OP_GuildDemote(const EQApplicationPacket *app); - void Handle_OP_GuildPromote(const EQApplicationPacket *app); - void Handle_OP_GuildInvite(const EQApplicationPacket *app); - void Handle_OP_GuildRemove(const EQApplicationPacket *app); - void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app); - void Handle_OP_GuildManageBanker(const EQApplicationPacket *app); - void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app); - void Handle_OP_ManaChange(const EQApplicationPacket *app); - void Handle_OP_MemorizeSpell(const EQApplicationPacket *app); - void Handle_OP_SwapSpell(const EQApplicationPacket *app); - void Handle_OP_CastSpell(const EQApplicationPacket *app); - void Handle_OP_DeleteItem(const EQApplicationPacket *app); - void Handle_OP_CombatAbility(const EQApplicationPacket *app); - void Handle_OP_Taunt(const EQApplicationPacket *app); - void Handle_OP_InstillDoubt(const EQApplicationPacket *app); - void Handle_OP_RezzAnswer(const EQApplicationPacket *app); - void Handle_OP_GMSummon(const EQApplicationPacket *app); - void Handle_OP_TradeRequest(const EQApplicationPacket *app); - void Handle_OP_TradeRequestAck(const EQApplicationPacket *app); - void Handle_OP_CancelTrade(const EQApplicationPacket *app); - void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app); - void Handle_OP_BoardBoat(const EQApplicationPacket *app); - void Handle_OP_LeaveBoat(const EQApplicationPacket *app); - void Handle_OP_RandomReq(const EQApplicationPacket *app); - void Handle_OP_Buff(const EQApplicationPacket *app); - void Handle_OP_GMHideMe(const EQApplicationPacket *app); - void Handle_OP_GMNameChange(const EQApplicationPacket *app); - void Handle_OP_GMKill(const EQApplicationPacket *app); - void Handle_OP_GMLastName(const EQApplicationPacket *app); - void Handle_OP_GMToggle(const EQApplicationPacket *app); - void Handle_OP_LFGCommand(const EQApplicationPacket *app); - void Handle_OP_GMGoto(const EQApplicationPacket *app); - void Handle_OP_TraderShop(const EQApplicationPacket *app); - void Handle_OP_ShopRequest(const EQApplicationPacket *app); - void Handle_OP_BazaarSearch(const EQApplicationPacket *app); - void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app); - void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app); - void Handle_OP_ShopEnd(const EQApplicationPacket *app); -// void Handle_OP_CloseContainer(const EQApplicationPacket *app); - void Handle_OP_ClickObjectAction(const EQApplicationPacket *app); - void Handle_OP_ClickObject(const EQApplicationPacket *app); - void Handle_OP_RecipesFavorite(const EQApplicationPacket *app); - void Handle_OP_RecipesSearch(const EQApplicationPacket *app); - void Handle_OP_RecipeDetails(const EQApplicationPacket *app); - void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app); - void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app); - void Handle_OP_ItemName(const EQApplicationPacket *app); - void Handle_OP_AugmentItem(const EQApplicationPacket *app); - void Handle_OP_ClickDoor(const EQApplicationPacket *app); - void Handle_OP_CreateObject(const EQApplicationPacket *app); - void Handle_OP_FaceChange(const EQApplicationPacket *app); - void Handle_OP_GroupInvite(const EQApplicationPacket *app); - void Handle_OP_GroupInvite2(const EQApplicationPacket *app); - void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app); - void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app); - void Handle_OP_GroupFollow(const EQApplicationPacket *app); - void Handle_OP_GroupFollow2(const EQApplicationPacket *app); - void Handle_OP_GroupDisband(const EQApplicationPacket *app); - void Handle_OP_GroupDelete(const EQApplicationPacket *app); - void Handle_OP_GMEmoteZone(const EQApplicationPacket *app); - void Handle_OP_InspectRequest(const EQApplicationPacket *app); - void Handle_OP_InspectAnswer(const EQApplicationPacket *app); - void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app); - void Handle_OP_Medding(const EQApplicationPacket *app); - void Handle_OP_DeleteSpell(const EQApplicationPacket *app); - void Handle_OP_PetitionBug(const EQApplicationPacket *app); - void Handle_OP_Bug(const EQApplicationPacket *app); - void Handle_OP_Petition(const EQApplicationPacket *app); - void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app); - void Handle_OP_PetitionResolve(const EQApplicationPacket *app); - void Handle_OP_PetitionDelete(const EQApplicationPacket *app); - void Handle_OP_PetCommands(const EQApplicationPacket *app); - void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app); - void Handle_OP_PetitionQue(const EQApplicationPacket *app); - void Handle_OP_PDeletePetition(const EQApplicationPacket *app); - void Handle_OP_PetitionCheckout(const EQApplicationPacket *app); - void Handle_OP_PetitionRefresh(const EQApplicationPacket *app); - void Handle_OP_ReadBook(const EQApplicationPacket *app); - void Handle_OP_Emote(const EQApplicationPacket *app); - void Handle_OP_Animation(const EQApplicationPacket *app); - void Handle_OP_SetServerFilter(const EQApplicationPacket *app); - void Handle_OP_GMDelCorpse(const EQApplicationPacket *app); - void Handle_OP_GMKick(const EQApplicationPacket *app); - void Handle_OP_GMServers(const EQApplicationPacket *app); - void Handle_OP_Illusion(const EQApplicationPacket *app); - void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app); - void Handle_OP_Fishing(const EQApplicationPacket *app); - void Handle_OP_Forage(const EQApplicationPacket *app); - void Handle_OP_Mend(const EQApplicationPacket *app); - void Handle_OP_EnvDamage(const EQApplicationPacket *app); - void Handle_OP_Damage(const EQApplicationPacket *app); - void Handle_OP_AAAction(const EQApplicationPacket *app); - void Handle_OP_TraderBuy(const EQApplicationPacket *app); - void Handle_OP_Trader(const EQApplicationPacket *app); - void Handle_OP_GMFind(const EQApplicationPacket *app); - void Handle_OP_PickPocket(const EQApplicationPacket *app); - void Handle_OP_Bind_Wound(const EQApplicationPacket *app); - void Handle_OP_TrackTarget(const EQApplicationPacket *app); - void Handle_OP_Track(const EQApplicationPacket *app); - void Handle_OP_TrackUnknown(const EQApplicationPacket *app); + void Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app); + // connected opcode handlers void Handle_0x0193(const EQApplicationPacket *app); void Handle_0x01e7(const EQApplicationPacket *app); - void Handle_OP_ClientError(const EQApplicationPacket *app); - void Handle_OP_ReloadUI(const EQApplicationPacket *app); - void Handle_OP_TGB(const EQApplicationPacket *app); - void Handle_OP_Split(const EQApplicationPacket *app); - void Handle_OP_SenseTraps(const EQApplicationPacket *app); - void Handle_OP_DisarmTraps(const EQApplicationPacket *app); - void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app); - void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app); - void Handle_OP_TributeItem(const EQApplicationPacket *app); - void Handle_OP_TributeMoney(const EQApplicationPacket *app); - void Handle_OP_SelectTribute(const EQApplicationPacket *app); - void Handle_OP_TributeUpdate(const EQApplicationPacket *app); - void Handle_OP_TributeToggle(const EQApplicationPacket *app); - void Handle_OP_TributeNPC(const EQApplicationPacket *app); - void Handle_OP_ConfirmDelete(const EQApplicationPacket *app); - void Handle_OP_CrashDump(const EQApplicationPacket *app); - void Handle_OP_ControlBoat(const EQApplicationPacket *app); - void Handle_OP_DumpName(const EQApplicationPacket *app); - void Handle_OP_SetRunMode(const EQApplicationPacket *app); - void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app); - void Handle_OP_Heartbeat(const EQApplicationPacket *app); - void Handle_OP_SafePoint(const EQApplicationPacket *app); - void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); - void Handle_OP_BankerChange(const EQApplicationPacket *app); - void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app); - void Handle_OP_SetTitle(const EQApplicationPacket *app); - void Handle_OP_RequestTitles(const EQApplicationPacket *app); - void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); - void Handle_OP_Ignore(const EQApplicationPacket *app); - void Handle_OP_LoadSpellSet(const EQApplicationPacket *app); - void Handle_OP_AutoFire(const EQApplicationPacket *app); - void Handle_OP_Rewind(const EQApplicationPacket *app); - void Handle_OP_RaidCommand(const EQApplicationPacket *app); - void Handle_OP_Translocate(const EQApplicationPacket *app); - void Handle_OP_Sacrifice(const EQApplicationPacket *app); + void Handle_OP_AAAction(const EQApplicationPacket *app); void Handle_OP_AcceptNewTask(const EQApplicationPacket *app); - void Handle_OP_CancelTask(const EQApplicationPacket *app); - void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app); - void Handle_OP_KeyRing(const EQApplicationPacket *app); - void Handle_OP_FriendsWho(const EQApplicationPacket *app); - void Handle_OP_Bandolier(const EQApplicationPacket *app); - void Handle_OP_PopupResponse(const EQApplicationPacket *app); - void Handle_OP_PotionBelt(const EQApplicationPacket *app); - void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app); - void Handle_OP_LFPCommand(const EQApplicationPacket *app); - void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app); - void Handle_OP_Barter(const EQApplicationPacket *app); - void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); - void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app); - void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app); - void Handle_OP_DelegateAbility(const EQApplicationPacket *app); - void Handle_OP_ApplyPoison(const EQApplicationPacket *app); - void Handle_OP_AugmentInfo(const EQApplicationPacket *app); - void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); - void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); - void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app); - void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureInfoRequest(const EQApplicationPacket *app); void Handle_OP_AdventureLeaderboardRequest(const EQApplicationPacket *app); - void Handle_OP_RespawnWindow(const EQApplicationPacket *app); - void Handle_OP_GroupUpdate(const EQApplicationPacket *app); - void Handle_OP_SetStartCity(const EQApplicationPacket *app); - void Handle_OP_Report(const EQApplicationPacket *app); - void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); - void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app); - void Handle_OP_GuildBank(const EQApplicationPacket *app); - void Handle_OP_GroupRoles(const EQApplicationPacket *app); - void Handle_OP_HideCorpse(const EQApplicationPacket *app); - void Handle_OP_TradeBusy(const EQApplicationPacket *app); - void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app); - void Handle_OP_GuildStatus(const EQApplicationPacket *app); - void Handle_OP_BlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app); - void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app); - void Handle_OP_CorpseDrag(const EQApplicationPacket *app); - void Handle_OP_CorpseDrop(const EQApplicationPacket *app); - void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app); - void Handle_OP_GuildCreate(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantPurchase(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app); + void Handle_OP_AdventureRequest(const EQApplicationPacket *app); + void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app); void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app); - void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app); void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app); void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app); void Handle_OP_AltCurrencySell(const EQApplicationPacket *app); - void Handle_OP_CrystalReclaim(const EQApplicationPacket *app); - void Handle_OP_CrystalCreate(const EQApplicationPacket *app); - void Handle_OP_LFGuild(const EQApplicationPacket *app); - void Handle_OP_XTargetRequest(const EQApplicationPacket *app); - void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); - void Handle_OP_ItemPreview(const EQApplicationPacket *app); - void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app); - void Handle_OP_MercenaryHire(const EQApplicationPacket *app); - void Handle_OP_MercenaryCommand(const EQApplicationPacket *app); - void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app); - void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app); - void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app); - void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app); - void Handle_OP_OpenInventory(const EQApplicationPacket *app); - void Handle_OP_OpenContainer(const EQApplicationPacket *app); + void Handle_OP_AltCurrencySellSelection(const EQApplicationPacket *app); + void Handle_OP_Animation(const EQApplicationPacket *app); + void Handle_OP_ApplyPoison(const EQApplicationPacket *app); + void Handle_OP_Assist(const EQApplicationPacket *app); + void Handle_OP_AssistGroup(const EQApplicationPacket *app); + void Handle_OP_AugmentInfo(const EQApplicationPacket *app); + void Handle_OP_AugmentItem(const EQApplicationPacket *app); + void Handle_OP_AutoAttack(const EQApplicationPacket *app); + void Handle_OP_AutoAttack2(const EQApplicationPacket *app); + void Handle_OP_AutoFire(const EQApplicationPacket *app); + void Handle_OP_Bandolier(const EQApplicationPacket *app); + void Handle_OP_BankerChange(const EQApplicationPacket *app); + void Handle_OP_Barter(const EQApplicationPacket *app); + void Handle_OP_BazaarInspect(const EQApplicationPacket *app); + void Handle_OP_BazaarSearch(const EQApplicationPacket *app); + void Handle_OP_Begging(const EQApplicationPacket *app); + void Handle_OP_Bind_Wound(const EQApplicationPacket *app); + void Handle_OP_BlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_BoardBoat(const EQApplicationPacket *app); + void Handle_OP_Buff(const EQApplicationPacket *app); + void Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app); + void Handle_OP_Bug(const EQApplicationPacket *app); + void Handle_OP_Camp(const EQApplicationPacket *app); + void Handle_OP_CancelTask(const EQApplicationPacket *app); + void Handle_OP_CancelTrade(const EQApplicationPacket *app); + void Handle_OP_CastSpell(const EQApplicationPacket *app); + void Handle_OP_ChannelMessage(const EQApplicationPacket *app); + void Handle_OP_ClearBlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_ClearNPCMarks(const EQApplicationPacket *app); + void Handle_OP_ClearSurname(const EQApplicationPacket *app); + void Handle_OP_ClickDoor(const EQApplicationPacket *app); + void Handle_OP_ClickObject(const EQApplicationPacket *app); + void Handle_OP_ClickObjectAction(const EQApplicationPacket *app); + void Handle_OP_ClientError(const EQApplicationPacket *app); void Handle_OP_ClientTimeStamp(const EQApplicationPacket *app); + void Handle_OP_ClientUpdate(const EQApplicationPacket *app); +// void Handle_OP_CloseContainer(const EQApplicationPacket *app); + void Handle_OP_CombatAbility(const EQApplicationPacket *app); + void Handle_OP_ConfirmDelete(const EQApplicationPacket *app); + void Handle_OP_Consent(const EQApplicationPacket *app); + void Handle_OP_ConsentDeny(const EQApplicationPacket *app); + void Handle_OP_Consider(const EQApplicationPacket *app); + void Handle_OP_ConsiderCorpse(const EQApplicationPacket *app); + void Handle_OP_Consume(const EQApplicationPacket *app); + void Handle_OP_ControlBoat(const EQApplicationPacket *app); + void Handle_OP_CorpseDrag(const EQApplicationPacket *app); + void Handle_OP_CorpseDrop(const EQApplicationPacket *app); + void Handle_OP_CrashDump(const EQApplicationPacket *app); + void Handle_OP_CreateObject(const EQApplicationPacket *app); + void Handle_OP_CrystalCreate(const EQApplicationPacket *app); + void Handle_OP_CrystalReclaim(const EQApplicationPacket *app); + void Handle_OP_Damage(const EQApplicationPacket *app); + void Handle_OP_Death(const EQApplicationPacket *app); + void Handle_OP_DelegateAbility(const EQApplicationPacket *app); + void Handle_OP_DeleteItem(const EQApplicationPacket *app); + void Handle_OP_DeleteSpawn(const EQApplicationPacket *app); + void Handle_OP_DeleteSpell(const EQApplicationPacket *app); + void Handle_OP_DisarmTraps(const EQApplicationPacket *app); + void Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app); + void Handle_OP_DuelResponse(const EQApplicationPacket *app); + void Handle_OP_DuelResponse2(const EQApplicationPacket *app); + void Handle_OP_DumpName(const EQApplicationPacket *app); + void Handle_OP_Dye(const EQApplicationPacket *app); + void Handle_OP_Emote(const EQApplicationPacket *app); + void Handle_OP_EndLootRequest(const EQApplicationPacket *app); + void Handle_OP_EnvDamage(const EQApplicationPacket *app); + void Handle_OP_FaceChange(const EQApplicationPacket *app); + void Handle_OP_FeignDeath(const EQApplicationPacket *app); + void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); + void Handle_OP_Fishing(const EQApplicationPacket *app); + void Handle_OP_Forage(const EQApplicationPacket *app); + void Handle_OP_FriendsWho(const EQApplicationPacket *app); + void Handle_OP_GetGuildMOTD(const EQApplicationPacket *app); + void Handle_OP_GetGuildsList(const EQApplicationPacket *app); + void Handle_OP_GMBecomeNPC(const EQApplicationPacket *app); + void Handle_OP_GMDelCorpse(const EQApplicationPacket *app); + void Handle_OP_GMEmoteZone(const EQApplicationPacket *app); + void Handle_OP_GMEndTraining(const EQApplicationPacket *app); + void Handle_OP_GMFind(const EQApplicationPacket *app); + void Handle_OP_GMGoto(const EQApplicationPacket *app); + void Handle_OP_GMHideMe(const EQApplicationPacket *app); + void Handle_OP_GMKick(const EQApplicationPacket *app); + void Handle_OP_GMKill(const EQApplicationPacket *app); + void Handle_OP_GMLastName(const EQApplicationPacket *app); + void Handle_OP_GMNameChange(const EQApplicationPacket *app); + void Handle_OP_GMSearchCorpse(const EQApplicationPacket *app); + void Handle_OP_GMServers(const EQApplicationPacket *app); + void Handle_OP_GMSummon(const EQApplicationPacket *app); + void Handle_OP_GMToggle(const EQApplicationPacket *app); + void Handle_OP_GMTraining(const EQApplicationPacket *app); + void Handle_OP_GMTrainSkill(const EQApplicationPacket *app); + void Handle_OP_GMZoneRequest(const EQApplicationPacket *app); + void Handle_OP_GMZoneRequest2(const EQApplicationPacket *app); + void Handle_OP_GroupAcknowledge(const EQApplicationPacket *app); + void Handle_OP_GroupCancelInvite(const EQApplicationPacket *app); + void Handle_OP_GroupDelete(const EQApplicationPacket *app); + void Handle_OP_GroupDisband(const EQApplicationPacket *app); + void Handle_OP_GroupFollow(const EQApplicationPacket *app); + void Handle_OP_GroupFollow2(const EQApplicationPacket *app); + void Handle_OP_GroupInvite(const EQApplicationPacket *app); + void Handle_OP_GroupInvite2(const EQApplicationPacket *app); + void Handle_OP_GroupMakeLeader(const EQApplicationPacket *app); + void Handle_OP_GroupMentor(const EQApplicationPacket *app); + void Handle_OP_GroupRoles(const EQApplicationPacket *app); + void Handle_OP_GroupUpdate(const EQApplicationPacket *app); + void Handle_OP_GuildBank(const EQApplicationPacket *app); + void Handle_OP_GuildCreate(const EQApplicationPacket *app); + void Handle_OP_GuildDelete(const EQApplicationPacket *app); + void Handle_OP_GuildDemote(const EQApplicationPacket *app); + void Handle_OP_GuildInvite(const EQApplicationPacket *app); + void Handle_OP_GuildInviteAccept(const EQApplicationPacket *app); + void Handle_OP_GuildLeader(const EQApplicationPacket *app); + void Handle_OP_GuildManageBanker(const EQApplicationPacket *app); + void Handle_OP_GuildPeace(const EQApplicationPacket *app); + void Handle_OP_GuildPromote(const EQApplicationPacket *app); + void Handle_OP_GuildPublicNote(const EQApplicationPacket *app); + void Handle_OP_GuildRemove(const EQApplicationPacket *app); + void Handle_OP_GuildStatus(const EQApplicationPacket *app); + void Handle_OP_GuildUpdateURLAndChannel(const EQApplicationPacket *app); + void Handle_OP_GuildWar(const EQApplicationPacket *app); + void Handle_OP_Heartbeat(const EQApplicationPacket *app); + void Handle_OP_Hide(const EQApplicationPacket *app); + void Handle_OP_HideCorpse(const EQApplicationPacket *app); + void Handle_OP_Ignore(const EQApplicationPacket *app); + void Handle_OP_Illusion(const EQApplicationPacket *app); + void Handle_OP_InspectAnswer(const EQApplicationPacket *app); + void Handle_OP_InspectMessageUpdate(const EQApplicationPacket *app); + void Handle_OP_InspectRequest(const EQApplicationPacket *app); + void Handle_OP_InstillDoubt(const EQApplicationPacket *app); + void Handle_OP_ItemLinkClick(const EQApplicationPacket *app); + void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); + void Handle_OP_ItemName(const EQApplicationPacket *app); + void Handle_OP_ItemPreview(const EQApplicationPacket *app); + void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); + void Handle_OP_Jump(const EQApplicationPacket *app); + void Handle_OP_KeyRing(const EQApplicationPacket *app); + void Handle_OP_LDoNButton(const EQApplicationPacket *app); + void Handle_OP_LDoNDisarmTraps(const EQApplicationPacket *app); + void Handle_OP_LDoNInspect(const EQApplicationPacket *app); + void Handle_OP_LDoNOpen(const EQApplicationPacket *app); + void Handle_OP_LDoNPickLock(const EQApplicationPacket *app); + void Handle_OP_LDoNSenseTraps(const EQApplicationPacket *app); + void Handle_OP_LeadershipExpToggle(const EQApplicationPacket *app); + void Handle_OP_LeaveAdventure(const EQApplicationPacket *app); + void Handle_OP_LeaveBoat(const EQApplicationPacket *app); + void Handle_OP_LFGCommand(const EQApplicationPacket *app); + void Handle_OP_LFGGetMatchesRequest(const EQApplicationPacket *app); + void Handle_OP_LFGuild(const EQApplicationPacket *app); + void Handle_OP_LFPCommand(const EQApplicationPacket *app); + void Handle_OP_LFPGetMatchesRequest(const EQApplicationPacket *app); + void Handle_OP_LoadSpellSet(const EQApplicationPacket *app); + void Handle_OP_Logout(const EQApplicationPacket *app); + void Handle_OP_LootItem(const EQApplicationPacket *app); + void Handle_OP_LootRequest(const EQApplicationPacket *app); + void Handle_OP_ManaChange(const EQApplicationPacket *app); + void Handle_OP_Medding(const EQApplicationPacket *app); + void Handle_OP_MemorizeSpell(const EQApplicationPacket *app); + void Handle_OP_Mend(const EQApplicationPacket *app); + void Handle_OP_MercenaryCommand(const EQApplicationPacket *app); + void Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryDismiss(const EQApplicationPacket *app); + void Handle_OP_MercenaryHire(const EQApplicationPacket *app); + void Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app); + void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app); + void Handle_OP_MoveCoin(const EQApplicationPacket *app); + void Handle_OP_MoveItem(const EQApplicationPacket *app); + void Handle_OP_OpenContainer(const EQApplicationPacket *app); + void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app); + void Handle_OP_OpenInventory(const EQApplicationPacket *app); + void Handle_OP_OpenTributeMaster(const EQApplicationPacket *app); + void Handle_OP_PDeletePetition(const EQApplicationPacket *app); + void Handle_OP_PetCommands(const EQApplicationPacket *app); + void Handle_OP_Petition(const EQApplicationPacket *app); + void Handle_OP_PetitionBug(const EQApplicationPacket *app); + void Handle_OP_PetitionCheckIn(const EQApplicationPacket *app); + void Handle_OP_PetitionCheckout(const EQApplicationPacket *app); + void Handle_OP_PetitionDelete(const EQApplicationPacket *app); + void Handle_OP_PetitionQue(const EQApplicationPacket *app); + void Handle_OP_PetitionRefresh(const EQApplicationPacket *app); + void Handle_OP_PetitionResolve(const EQApplicationPacket *app); + void Handle_OP_PetitionUnCheckout(const EQApplicationPacket *app); + void Handle_OP_PickPocket(const EQApplicationPacket *app); + void Handle_OP_PopupResponse(const EQApplicationPacket *app); + void Handle_OP_PotionBelt(const EQApplicationPacket *app); + void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app); + void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app); + void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); + void Handle_OP_RaidCommand(const EQApplicationPacket *app); + void Handle_OP_RandomReq(const EQApplicationPacket *app); + void Handle_OP_ReadBook(const EQApplicationPacket *app); + void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app); + void Handle_OP_RecipeDetails(const EQApplicationPacket *app); + void Handle_OP_RecipesFavorite(const EQApplicationPacket *app); + void Handle_OP_RecipesSearch(const EQApplicationPacket *app); + void Handle_OP_ReloadUI(const EQApplicationPacket *app); + void Handle_OP_RemoveBlockedBuffs(const EQApplicationPacket *app); + void Handle_OP_Report(const EQApplicationPacket *app); + void Handle_OP_RequestDuel(const EQApplicationPacket *app); + void Handle_OP_RequestTitles(const EQApplicationPacket *app); + void Handle_OP_RespawnWindow(const EQApplicationPacket *app); + void Handle_OP_Rewind(const EQApplicationPacket *app); + void Handle_OP_RezzAnswer(const EQApplicationPacket *app); + void Handle_OP_Sacrifice(const EQApplicationPacket *app); + void Handle_OP_SafeFallSuccess(const EQApplicationPacket *app); + void Handle_OP_SafePoint(const EQApplicationPacket *app); + void Handle_OP_Save(const EQApplicationPacket *app); + void Handle_OP_SaveOnZoneReq(const EQApplicationPacket *app); + void Handle_OP_SelectTribute(const EQApplicationPacket *app); + void Handle_OP_SenseHeading(const EQApplicationPacket *app); + void Handle_OP_SenseTraps(const EQApplicationPacket *app); + void Handle_OP_SetGuildMOTD(const EQApplicationPacket *app); + void Handle_OP_SetRunMode(const EQApplicationPacket *app); + void Handle_OP_SetServerFilter(const EQApplicationPacket *app); + void Handle_OP_SetStartCity(const EQApplicationPacket *app); + void Handle_OP_SetTitle(const EQApplicationPacket *app); + void Handle_OP_Shielding(const EQApplicationPacket *app); + void Handle_OP_ShopEnd(const EQApplicationPacket *app); + void Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app); + void Handle_OP_ShopPlayerSell(const EQApplicationPacket *app); + void Handle_OP_ShopRequest(const EQApplicationPacket *app); + void Handle_OP_Sneak(const EQApplicationPacket *app); + void Handle_OP_SpawnAppearance(const EQApplicationPacket *app); + void Handle_OP_Split(const EQApplicationPacket *app); + void Handle_OP_Surname(const EQApplicationPacket *app); + void Handle_OP_SwapSpell(const EQApplicationPacket *app); + void Handle_OP_TargetCommand(const EQApplicationPacket *app); + void Handle_OP_TargetMouse(const EQApplicationPacket *app); + void Handle_OP_TaskHistoryRequest(const EQApplicationPacket *app); + void Handle_OP_Taunt(const EQApplicationPacket *app); + void Handle_OP_TestBuff(const EQApplicationPacket *app); + void Handle_OP_TGB(const EQApplicationPacket *app); + void Handle_OP_Track(const EQApplicationPacket *app); + void Handle_OP_TrackTarget(const EQApplicationPacket *app); + void Handle_OP_TrackUnknown(const EQApplicationPacket *app); + void Handle_OP_TradeAcceptClick(const EQApplicationPacket *app); + void Handle_OP_TradeBusy(const EQApplicationPacket *app); + void Handle_OP_Trader(const EQApplicationPacket *app); + void Handle_OP_TraderBuy(const EQApplicationPacket *app); + void Handle_OP_TradeRequest(const EQApplicationPacket *app); + void Handle_OP_TradeRequestAck(const EQApplicationPacket *app); + void Handle_OP_TraderShop(const EQApplicationPacket *app); + void Handle_OP_TradeSkillCombine(const EQApplicationPacket *app); + void Handle_OP_Translocate(const EQApplicationPacket *app); + void Handle_OP_TributeItem(const EQApplicationPacket *app); + void Handle_OP_TributeMoney(const EQApplicationPacket *app); + void Handle_OP_TributeNPC(const EQApplicationPacket *app); + void Handle_OP_TributeToggle(const EQApplicationPacket *app); + void Handle_OP_TributeUpdate(const EQApplicationPacket *app); + void Handle_OP_VetClaimRequest(const EQApplicationPacket *app); + void Handle_OP_VoiceMacroIn(const EQApplicationPacket *app); + void Handle_OP_WearChange(const EQApplicationPacket *app); + void Handle_OP_WhoAllRequest(const EQApplicationPacket *app); + void Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app); + void Handle_OP_XTargetRequest(const EQApplicationPacket *app); + void Handle_OP_YellForHelp(const EQApplicationPacket *app); + void Handle_OP_ZoneChange(const EQApplicationPacket *app); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 61f4936c8..d9b2d9030 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -967,93 +967,96 @@ void Client::BulkSendInventoryItems() void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { const Item_Struct* handyitem = nullptr; - uint32 numItemSlots=80; //The max number of items passed in the transaction. + uint32 numItemSlots = 80; //The max number of items passed in the transaction. const Item_Struct *item; std::list merlist = zone->merchanttable[merchant_id]; std::list::const_iterator itr; Mob* merch = entity_list.GetMobByNpcTypeID(npcid); - if(merlist.size()==0){ //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded + if (merlist.size() == 0) { //Attempt to load the data, it might have been missed if someone spawned the merchant after the zone was loaded zone->LoadNewMerchantData(merchant_id); merlist = zone->merchanttable[merchant_id]; - if(merlist.size()==0) + if (merlist.size() == 0) return; } std::list tmp_merlist = zone->tmpmerchanttable[npcid]; std::list::iterator tmp_itr; - uint32 i=1; + uint32 i = 1; uint8 handychance = 0; - for (itr = merlist.begin(); itr != merlist.end() && i < numItemSlots; ++itr) { + for (itr = merlist.begin(); itr != merlist.end() && i <= numItemSlots; ++itr) { MerchantList ml = *itr; if (merch->CastToNPC()->GetMerchantProbability() > ml.probability) continue; - - if(GetLevel() < ml.level_required) + + if (GetLevel() < ml.level_required) continue; if (!(ml.classes_required & (1 << (GetClass() - 1)))) continue; int32 fac = merch ? merch->GetPrimaryFaction() : 0; - if(fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) + if (fac != 0 && GetModCharacterFactionLevel(fac) < ml.faction_required) continue; - handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1 ); + handychance = MakeRandomInt(0, merlist.size() + tmp_merlist.size() - 1); item = database.GetItem(ml.item); - if(item) { - if(handychance==0) - handyitem=item; + if (item) { + if (handychance == 0) + handyitem = item; else handychance--; - int charges=1; - if(item->ItemClass==ItemClassCommon) - charges=item->MaxCharges; + int charges = 1; + if (item->ItemClass == ItemClassCommon) + charges = item->MaxCharges; ItemInst* inst = database.CreateItem(item, charges); if (inst) { - if (RuleB(Merchant, UsePriceMod)){ - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(merch,false))); + if (RuleB(Merchant, UsePriceMod)) { + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false))); } else - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate)); + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate)); inst->SetMerchantSlot(ml.slot); inst->SetMerchantCount(-1); //unlimited - if(charges > 0) + if (charges > 0) inst->SetCharges(charges); else inst->SetCharges(1); - SendItemPacket(ml.slot-1, inst, ItemPacketMerchant); + SendItemPacket(ml.slot - 1, inst, ItemPacketMerchant); safe_delete(inst); } } // Account for merchant lists with gaps. - if(ml.slot >= i) + if (ml.slot >= i) { + if (ml.slot > i) + LogFile->write(EQEMuLog::Debug, "(WARNING) Merchantlist contains gap at slot %d. Merchant: %d, NPC: %d", i, merchant_id, npcid); i = ml.slot + 1; + } } std::list origtmp_merlist = zone->tmpmerchanttable[npcid]; tmp_merlist.clear(); - for(tmp_itr = origtmp_merlist.begin();tmp_itr != origtmp_merlist.end() && iItemClass==ItemClassCommon && (int16)ml.charges <= item->MaxCharges) // charges=ml.charges; //else charges = item->MaxCharges; ItemInst* inst = database.CreateItem(item, charges); if (inst) { - if (RuleB(Merchant, UsePriceMod)){ - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate*Client::CalcPriceMod(merch,false))); + if (RuleB(Merchant, UsePriceMod)) { + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(merch, false))); } else - inst->SetPrice((item->Price*(RuleR(Merchant, SellCostMod))*item->SellRate)); + inst->SetPrice((item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate)); inst->SetMerchantSlot(ml.slot); inst->SetMerchantCount(ml.charges); if(charges > 0) @@ -1069,32 +1072,32 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) { } //this resets the slot zone->tmpmerchanttable[npcid] = tmp_merlist; - if(merch != nullptr && handyitem){ - char handy_id[8]={0}; - int greeting=MakeRandomInt(0, 4); - int greet_id=0; - switch(greeting){ + if (merch != nullptr && handyitem) { + char handy_id[8] = { 0 }; + int greeting = MakeRandomInt(0, 4); + int greet_id = 0; + switch (greeting) { case 1: - greet_id=MERCHANT_GREETING; + greet_id = MERCHANT_GREETING; break; case 2: - greet_id=MERCHANT_HANDY_ITEM1; + greet_id = MERCHANT_HANDY_ITEM1; break; case 3: - greet_id=MERCHANT_HANDY_ITEM2; + greet_id = MERCHANT_HANDY_ITEM2; break; case 4: - greet_id=MERCHANT_HANDY_ITEM3; + greet_id = MERCHANT_HANDY_ITEM3; break; default: - greet_id=MERCHANT_HANDY_ITEM4; + greet_id = MERCHANT_HANDY_ITEM4; } - sprintf(handy_id,"%i",greet_id); + sprintf(handy_id, "%i", greet_id); - if(greet_id!=MERCHANT_GREETING) - Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName(),handyitem->Name); + if (greet_id != MERCHANT_GREETING) + Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName(), handyitem->Name); else - Message_StringID(10,GENERIC_STRINGID_SAY,merch->GetCleanName(),handy_id,this->GetName()); + Message_StringID(10, GENERIC_STRINGID_SAY, merch->GetCleanName(), handy_id, this->GetName()); merch->CastToNPC()->FaceTarget(this->CastToMob()); } @@ -1547,7 +1550,12 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) if (from_bucket == &m_pp.platinum_shared) amount_to_add = 0 - amount_to_take; - database.SetSharedPlatinum(AccountID(),amount_to_add); + database.SetSharedPlatinum(AccountID(),amount_to_add); + } + } + else{ + if (to_bucket == &m_pp.platinum_shared || from_bucket == &m_pp.platinum_shared){ + this->Message(13, "::: WARNING! ::: SHARED BANK IS DISABLED AND YOUR PLATINUM WILL BE DESTROYED IF YOU PUT IT HERE"); } } } @@ -1580,7 +1588,7 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) safe_delete(outapp); } - Save(); + SaveCurrency(); } void Client::OPGMTraining(const EQApplicationPacket *app) @@ -1715,6 +1723,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app) } uint16 skilllevel = GetRawSkill(skill); + if(skilllevel == 0) { //this is a new skill.. uint16 t_level = SkillTrainLevel(skill, GetClass()); @@ -1724,7 +1733,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app) } SetSkill(skill, t_level); - } else { + } else { switch(skill) { case SkillBrewing: case SkillMakePoison: diff --git a/zone/command.cpp b/zone/command.cpp index cf180f2bd..33f7c2068 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -149,7 +149,7 @@ Access Levels: int command_init(void) { if ( - command_add("resetaa","- Resets a Player's AA in their profile.",200,command_resetaa) || + command_add("resetaa","- Resets a Player's AA in their profile and refunds spent AA's to unspent, disconnects player.",200,command_resetaa) || command_add("qtest","- QueryServ testing command.",255,command_qtest) || command_add("bind","- Sets your targets bind spot to their current location",200,command_bind) || command_add("sendop","[opcode] - LE's Private test command, leave it alone",200,command_sendop) || @@ -225,7 +225,6 @@ int command_init(void) { command_add("worldshutdown","- Shut down world and all zones",200,command_worldshutdown) || command_add("sendzonespawns","- Refresh spawn list for all clients in zone",150,command_sendzonespawns) || command_add("dbspawn2","[spawngroup] [respawn] [variance] - Spawn an NPC from a predefined row in the spawn2 table",100,command_dbspawn2) || - command_add("copychar","[character name] [new character] [new account id] - Create a copy of a character",100,command_copychar) || command_add("shutdown","- Shut this zone process down",150,command_shutdown) || command_add("delacct","[accountname] - Delete an account",150,command_delacct) || command_add("setpass","[accountname] [password] - Set local password for accountname",150,command_setpass) || @@ -258,12 +257,11 @@ int command_init(void) { command_add("dbspawn",nullptr,0,command_npctypespawn) || command_add("heal","- Completely heal your target",10,command_heal) || command_add("appearance","[type] [value] - Send an appearance packet for you or your target",150,command_appearance) || - command_add("charbackup","[list/restore] - Query or restore character backups",150,command_charbackup) || command_add("nukeitem","[itemid] - Remove itemid from your player target's inventory",150,command_nukeitem) || command_add("peekinv","[worn/cursor/inv/bank/trade/trib/all] - Print out contents of your player target's inventory",100,command_peekinv) || command_add("findnpctype","[search criteria] - Search database NPC types",100,command_findnpctype) || command_add("findzone","[search criteria] - Search database zones",100,command_findzone) || - command_add("fz",nullptr,100,command_findzone) || + command_add("fz",nullptr,100, command_findzone) || command_add("viewnpctype","[npctype id] - Show info about an npctype",100,command_viewnpctype) || command_add("reloadstatic","- Reload Static Zone Data",150,command_reloadstatic) || command_add("reloadquest"," - Clear quest cache (any argument causes it to also stop all timers)",150,command_reloadqst) || @@ -333,8 +331,6 @@ int command_init(void) { command_add("guilds",nullptr,0,command_guild) || command_add("zonestatus","- Show connected zoneservers, synonymous with /servers",150,command_zonestatus) || command_add("manaburn","- Use AA Wizard class skill manaburn on target",10,command_manaburn) || - command_add("viewmessage","[id] - View messages in your tell queue",100,command_viewmessage) || - command_add("viewmessages",nullptr,0,command_viewmessage) || command_add("doanim","[animnum] [type] - Send an EmoteAnim for you or your target",50,command_doanim) || command_add("randomfeatures","- Temporarily randomizes the Facial Features of your target",80,command_randomfeatures) || command_add("rf",nullptr,80,command_randomfeatures) || @@ -374,8 +370,8 @@ int command_init(void) { command_add("opcode","- opcode management",250,command_opcode) || command_add("logs","[status|normal|error|debug|quest|all] - Subscribe to a log type",250,command_logs) || command_add("nologs","[status|normal|error|debug|quest|all] - Unsubscribe to a log type",250,command_nologs) || - command_add("ban","[name] - Ban by character name",150,command_ban) || - command_add("suspend","[name][days] - Suspend by character name and for specificed number of days",150,command_suspend) || + command_add("ban","[name] [reason]- Ban by character name",150,command_ban) || + command_add("suspend","[name] [days] [reason] - Suspend by character name and for specificed number of days",150,command_suspend) || command_add("ipban","[IP address] - Ban IP by character name",200,command_ipban) || command_add("oocmute","[1/0] - Mutes OOC chat",200,command_oocmute) || command_add("revoke","[charname] [1/0] - Makes charname unable to talk on OOC",200,command_revoke) || @@ -386,7 +382,7 @@ int command_init(void) { command_add("npcshout","[message] - Make your NPC target shout a message.",150,command_npcshout) || command_add("timers","- Display persistent timers for target",200,command_timers) || command_add("hp","- Refresh your HP bar from the server.",0,command_hp) || - command_add("pf","- ",0,command_pf) || + command_add("pf","- Display additional mob coordinate and wandering data",0,command_pf) || command_add("logsql","- enable SQL logging",200,command_logsql) || command_add("bestz","- Ask map for a good Z coord for your x,y coords.",0,command_bestz) || command_add("ginfo","- get group info on target.",20,command_ginfo) || @@ -407,7 +403,6 @@ int command_init(void) { command_add("guildapprove","[guildapproveid] - Approve a guild with specified ID (guild creator receives the id)",0,command_guildapprove) || command_add("guildlist","[guildapproveid] - Lists character names who have approved the guild specified by the approve id",0,command_guildlist) || command_add("altactivate", "[argument] - activates alternate advancement abilities, use altactivate help for more information", 0, command_altactivate) || - command_add("refundaa", "- Refunds your target's AA points, will disconnect them in the process as well.", 100, command_refundaa) || #ifdef BOTS command_add("bot","- Type \"#bot help\" to the see the list of available commands for bots.", 0, command_bot) || @@ -593,7 +588,7 @@ int command_realdispatch(Client *c, const char *message) /* QS: Player_Log_Issued_Commands */ if (RuleB(QueryServ, PlayerLogIssuedCommandes)){ std::string event_desc = StringFormat("Issued command :: '%s' in zoneid:%i instid:%i", message, c->GetZoneID(), c->GetInstanceID()); - QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); + QServ->PlayerLogEvent(Player_Log_Issued_Commands, c->CharacterID(), event_desc); } #ifdef COMMANDS_LOGGING @@ -820,17 +815,18 @@ void command_version(Client *c, const Seperator *sep) void command_setfaction(Client *c, const Seperator *sep) { - if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) + if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) { c->Message(0, "Usage: #setfaction [faction number]"); - else - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"Setting NPC %u to faction %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[1])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_faction_id=%i where id=%i",atoi(sep->argplus[1]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } + return; + } + + auto npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + c->Message(15,"Setting NPC %u to faction %i", npcTypeID, atoi(sep->argplus[1])); + + std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", + atoi(sep->argplus[1]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); } void command_serversidename(Client *c, const Seperator *sep) @@ -1514,77 +1510,71 @@ void command_movechar(Client *c, const Seperator *sep) void command_viewpetition(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) + if (sep->arg[1][0] == 0) { c->Message(0, "Usage: #viewpetition (petition number) Type #listpetition for a list"); - else - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int queryfound = 0; - MYSQL_RES *result; - MYSQL_ROW row; - c->Message(13," ID : Character Name , Petition Text"); - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, petitiontext from petitions order by petid"), errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) - { - if (strcasecmp(row[0],sep->argplus[1])== 0) - { - queryfound=1; - c->Message(15, " %s: %s , %s ",row[0],row[1],row[2]); - } - } - LogFile->write(EQEMuLog::Normal,"View petition request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); - if (queryfound==0) - c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); - mysql_free_result(result); - } - safe_delete_array(query); - } + return; + } + + c->Message(13," ID : Character Name , Petition Text"); + + std::string query = "SELECT petid, charname, petitiontext FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"View petition request from %s, petition number: %i", c->GetName(), atoi(sep->argplus[1]) ); + + if (results.RowCount() == 0) { + c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + if (strcasecmp(row[0], sep->argplus[1]) == 0) + c->Message(15, " %s: %s , %s ", row[0], row[1], row[2]); + } void command_petitioninfo(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0) + if (sep->arg[1][0] == 0) { c->Message(0, "Usage: #petitioninfo (petition number) Type #listpetition for a list"); - else - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int queryfound = 0; - MYSQL_RES *result; - MYSQL_ROW row; + return; + } + + std::string query = "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"Petition information request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); + + if (results.RowCount() == 0) { + c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + if (strcasecmp(row[0],sep->argplus[1])== 0) + c->Message(13," ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s",row[0],row[1],row[2],row[3],row[4],row[5],row[6]); - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, accountname, zone, charclass, charrace, charlevel from petitions order by petid"), errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) - if (strcasecmp(row[0],sep->argplus[1])== 0) - { - queryfound=1; - c->Message(13," ID : %s Character Name: %s Account Name: %s Zone: %s Character Class: %s Character Race: %s Character Level: %s",row[0],row[1],row[2],row[3],row[4],row[5],row[6]); - } - LogFile->write(EQEMuLog::Normal,"Petition information request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); - if (queryfound==0) - c->Message(13,"There was an error in your request: ID not found! Please check the Id and try again."); - mysql_free_result(result); - } - safe_delete_array(query); - } } void command_delpetition(Client *c, const Seperator *sep) { - if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) + if (sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*") == 0) { c->Message(0, "Usage: #delpetition (petition number) Type #listpetition for a list"); - else { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(13,"Attempting to delete petition number: %i",atoi(sep->argplus[1])); - if (database.RunQuery(query, MakeAnyLenString(&query, "DELETE from petitions where petid=%i",atoi(sep->argplus[1])), errbuf)) { - LogFile->write(EQEMuLog::Normal,"Delete petition request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); - } - safe_delete_array(query); - } + return; + } + + c->Message(13,"Attempting to delete petition number: %i",atoi(sep->argplus[1])); + std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", atoi(sep->argplus[1])); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"Delete petition request from %s, petition number:", c->GetName(), atoi(sep->argplus[1]) ); + } void command_listnpcs(Client *c, const Seperator *sep) @@ -2158,8 +2148,8 @@ void command_worldshutdown(Client *c, const Seperator *sep) uint32 interval=0; if (worldserver.Connected()) { if(sep->IsNumber(1) && sep->IsNumber(2) && ((time=atoi(sep->arg[1]))>0) && ((interval=atoi(sep->arg[2]))>0)) { - worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down in %i seconds, everyone log out before this time.",time); - c->Message(0, "Sending shutdown packet now, World will shutdown in: %i Seconds with an interval of: %i",time,interval); + worldserver.SendEmoteMessage(0,0,15,":SYSTEM MSG:World coming down in %i minutes, everyone log out before this time.", (time / 60 )); + c->Message(0, "Sending shutdown packet now, World will shutdown in: %i minutes with an interval of: %i seconds", (time / 60), interval); ServerPacket* pack = new ServerPacket(ServerOP_ShutdownAll,sizeof(WorldShutDown_Struct)); WorldShutDown_Struct* wsd = (WorldShutDown_Struct*)pack->pBuffer; wsd->time=time*1000; @@ -2228,28 +2218,6 @@ void command_dbspawn2(Client *c, const Seperator *sep) } } -void command_copychar(Client *c, const Seperator *sep) -{ - if(sep->arg[1][0]==0 || sep->arg[2][0] == 0 || sep->arg[3][0] == 0) - c->Message(0, "Usage: #copychar [character name] [new character] [new account id]"); - //CheckUsedName.... TRUE=No Char, FALSE=Char/Error - //If there is no source... - else if (database.CheckUsedName((char*)sep->arg[1])) { - c->Message(0, "Source character not found!"); - } - else { - //If there is a name is not used.... - if (database.CheckUsedName((char*) sep->arg[2])) { - if (!database.CopyCharacter((char*) sep->arg[1], (char*) sep->arg[2], atoi(sep->arg[3]))) - c->Message(0, "Character copy operation failed!"); - else - c->Message(0, "Character copy complete."); - } - else - c->Message(0, "Target character already exists!"); - } -} - void command_shutdown(Client *c, const Seperator *sep) { CatchSignal(2); @@ -2424,7 +2392,7 @@ void command_showskills(Client *c, const Seperator *sep) c->Message(0, "Skills for %s", t->GetName()); for (SkillUseTypes i=Skill1HBlunt; i <= HIGHEST_SKILL; i=(SkillUseTypes)(i+1)) - c->Message(0, "Skill [%d] is at [%d]", i, t->GetSkill(i)); + c->Message(0, "Skill [%d] is at [%d] - %u", i, t->GetSkill(i), t->GetRawSkill(i)); } void command_findspell(Client *c, const Seperator *sep) @@ -2492,14 +2460,14 @@ void command_castspell(Client *c, const Seperator *sep) else if (c->GetTarget() == 0) if(c->Admin() >= commandInstacast) - c->SpellFinished(spellid, 0, 10, 0, -1, spells[spellid].ResistDiff); + c->SpellFinished(spellid, 0, USE_ITEM_SPELL_SLOT, 0, -1, spells[spellid].ResistDiff); else - c->CastSpell(spellid, 0, 10, 0); + c->CastSpell(spellid, 0, USE_ITEM_SPELL_SLOT, 0); else if(c->Admin() >= commandInstacast) c->SpellFinished(spellid, c->GetTarget(), 10, 0, -1, spells[spellid].ResistDiff); else - c->CastSpell(spellid, c->GetTarget()->GetID(), 10, 0); + c->CastSpell(spellid, c->GetTarget()->GetID(), USE_ITEM_SPELL_SLOT, 0); } } @@ -2791,85 +2759,6 @@ void command_appearance(Client *c, const Seperator *sep) } } -void command_charbackup(Client *c, const Seperator *sep) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - if (strcasecmp(sep->arg[1], "list") == 0) { - uint32 charid = 0; - if (sep->IsNumber(2)) - charid = atoi(sep->arg[2]); - else - database.GetAccountIDByChar(sep->arg[2], &charid); - if (charid) { - if (database.RunQuery(query, MakeAnyLenString(&query, - "Select id, backupreason, charid, account_id, zoneid, DATE_FORMAT(ts, '%%m/%%d/%%Y %%H:%%i:%%s') " - " from character_backup where charid=%u", charid), errbuf, &result)) { - safe_delete(query); - uint32 x = 0; - while ((row = mysql_fetch_row(result))) { - c->Message(0, " %u: %s, %s (%u), reason=%u", atoi(row[0]), row[5], database.GetZoneName(atoi(row[4])), atoi(row[4]), atoi(row[1])); - x++; - } - c->Message(0, " %u backups found.", x); - mysql_free_result(result); - } - else { - c->Message(13, "Query error: '%s' %s", query, errbuf); - safe_delete(query); - } - } - else - c->Message(0, "Usage: #charbackup list [char name/id]"); - } - else if (strcasecmp(sep->arg[1], "restore") == 0) { - uint32 charid = 0; - if (sep->IsNumber(2)) - charid = atoi(sep->arg[2]); - else - database.GetAccountIDByChar(sep->arg[2], &charid); - - if (charid && sep->IsNumber(3)) { - uint32 cbid = atoi(sep->arg[3]); - if (database.RunQuery(query, MakeAnyLenString(&query, - "Insert into character_backup (backupreason, charid, account_id, name, profile, level, class, x, y, z, zoneid, alt_adv) " - " select 1, id, account_id, name, profile, level, class, x, y, z, zoneid, alt_adv from character_ where id=%u", charid), errbuf)) { - if (database.RunQuery(query, MakeAnyLenString(&query, - "update character_ inner join character_backup on character_.id = character_backup.charid " - " set character_.name = character_backup.name, " - " character_.profile = character_backup.profile, " - " character_.level = character_backup.level, " - " character_.class = character_backup.class, " - " character_.x = character_backup.x, " - " character_.y = character_backup.y, " - " character_.z = character_backup.z, " - " character_.zoneid = character_backup.zoneid " - " where character_backup.charid=%u and character_backup.id=%u", charid, cbid), errbuf)) { - safe_delete(query); - c->Message(0, "Character restored."); - } - else { - c->Message(13, "Query error: '%s' %s", query, errbuf); - safe_delete(query); - } - } - else { - c->Message(13, "Query error: '%s' %s", query, errbuf); - safe_delete(query); - } - } - else - c->Message(0, "Usage: #charbackup list [char name/id]"); - } - else { - c->Message(0, "#charbackup sub-commands:"); - c->Message(0, " list [char name/id]"); - c->Message(0, " restore [char name/id] [backup#]"); - } -} - void command_nukeitem(Client *c, const Seperator *sep) { int numitems, itemid; @@ -2891,6 +2780,21 @@ void command_peekinv(Client *c, const Seperator *sep) return; } + // linkcore_# indicates the number of consecutive 0's..not the actual length + const std::string linkcore_49 = "%c%06X0000000000000000000000000000000000000000000000000%s%c"; // RoF+ + const std::string linkcore_44 = "%c%06X00000000000000000000000000000000000000000000%s%c"; // SoF->UF + const std::string linkcore_39 = "%c%06X000000000000000000000000000000000000000%s%c"; // 6.2->Ti + const char* linkcore = nullptr; + std::string linkbase = ""; + + // consider pushing 'linkcores' to EQLimits + if (c->GetClientVersion() >= EQClientRoF) + linkcore = linkcore_49.c_str(); + else if (c->GetClientVersion() >= EQClientSoF) + linkcore = linkcore_44.c_str(); + else + linkcore = linkcore_39.c_str(); + bool bAll = (strcasecmp(sep->arg[1], "all") == 0); bool bFound = false; Client* client = c->GetTarget()->CastToClient(); @@ -2903,76 +2807,45 @@ void command_peekinv(Client *c, const Seperator *sep) for (int16 i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - // this kind of stuff needs to be pushed to the client translators - c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "WornSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } + if (c->GetClientVersion() >= EQClientSoF) + { + const ItemInst* inst = client->GetInv().GetItem(MainPowerSource); + item = (inst) ? inst->GetItem() : nullptr; + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", + MainPowerSource, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); + } + } + if (bAll || (strcasecmp(sep->arg[1], "inv")==0)) { // Personal inventory items bFound = true; for (int16 i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "InvSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } - else - { - c->Message((item==0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } - if(c->GetClientVersion() >= EQClientSoF) - { - const ItemInst* inst = client->GetInv().GetItem(MainPowerSource); - item = (inst) ? inst->GetItem() : nullptr; - c->Message((item==0), "InvSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", MainPowerSource, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } } // Changed to show 'empty' cursors and not to show bag slots on 'queued' cursor slots (cursor bag slots 331 to 340 are not arrayed...) @@ -2981,59 +2854,30 @@ void command_peekinv(Client *c, const Seperator *sep) // Personal inventory items bFound = true; iter_queue it; - int i=0; + int i = 0; if(client->GetInv().CursorEmpty()) { // Display 'front' cursor slot even if 'empty' (item(30[0]) == null) - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - 0, 0x12, 0, "null", 0x12, 0); - } - else - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - 0, 0x12, 0, "null", 0x12, 0); - } + linkbase = StringFormat(linkcore, 0x12, 0, "null", 0x12); + c->Message((item == 0), "CursorSlot: %i, Item: %i (%s), Charges: %i", + MainCursor, 0, linkbase.c_str(), 0); } else { for(it=client->GetInv().cursor_begin();it!=client->GetInv().cursor_end();++it,i++) { const ItemInst* inst = *it; item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", MainCursor, i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", + MainCursor, i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer) && i==0) { // 'CSD 1' - only display contents of slot 30[0] container..higher ones don't exist for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(MainCursor, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(MainCursor, j), - MainCursor, j, ((item == 0) ? 0 : item->ID), 0x12, ((item == 0) ? 0 : item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } - else - { - c->Message((item==0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(MainCursor, j), - MainCursor, j, ((item == 0) ? 0 : item->ID), 0x12, ((item == 0) ? 0 : item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:instbag->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(MainCursor, j), MainCursor, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } @@ -3046,20 +2890,10 @@ void command_peekinv(Client *c, const Seperator *sep) for (int16 i = EmuConstants::TRIBUTE_BEGIN; i <= EmuConstants::TRIBUTE_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "TributeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "TributeSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } @@ -3070,128 +2904,62 @@ void command_peekinv(Client *c, const Seperator *sep) for (i = EmuConstants::BANK_BEGIN; i <= EmuConstants::BANK_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "BankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "BankSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } for (i = EmuConstants::SHARED_BANK_BEGIN; i <= EmuConstants::SHARED_BANK_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "ShBankSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "SharedBankSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), " ShBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } } + if (bAll || (strcasecmp(sep->arg[1], "trade")==0)) { // Items in trade window (current trader only, not the other trader) bFound = true; for (int16 i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_END; i++) { const ItemInst* inst = client->GetInv().GetItem(i); item = (inst) ? inst->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), "TradeSlot: %i, Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", i, - ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), "TradeSlot: %i, Item: %i (%s), Charges: %i", + i, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); if (inst && inst->IsType(ItemClassContainer)) { for (uint8 j = SUB_BEGIN; j < EmuConstants::ITEM_CONTAINER_SIZE; j++) { const ItemInst* instbag = client->GetInv().GetItem(i, j); item = (instbag) ? instbag->GetItem() : nullptr; - if (c->GetClientVersion() >= EQClientSoF) - { - c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X00000000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } - else - { - c->Message((item==0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%c%06X000000000000000000000000000000000000000%s%c), Charges: %i", - Inventory::CalcSlotId(i, j), - i, j, ((item==0)?0:item->ID),0x12, ((item==0)?0:item->ID), - ((item==0)?"null":item->Name), 0x12, - ((item==0)?0:inst->GetCharges())); - } + linkbase = StringFormat(linkcore, 0x12, ((item == 0) ? 0 : item->ID), ((item == 0) ? "null" : item->Name), 0x12); + c->Message((item == 0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(i, j), i, j, ((item == 0) ? 0 : item->ID), linkbase.c_str(), ((item == 0) ? 0 : inst->GetCharges())); } } } @@ -3207,128 +2975,93 @@ void command_peekinv(Client *c, const Seperator *sep) void command_findnpctype(Client *c, const Seperator *sep) { - if(sep->arg[1][0] == 0) + if(sep->arg[1][0] == 0) { c->Message(0, "Usage: #findnpctype [search criteria]"); - else - { - int id; - int count; - const int maxrows = 20; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query; - MYSQL_RES *result; - MYSQL_ROW row; + return; + } - query = new char[256]; + std::string query; - // If id evaluates to 0, then search as if user entered a string. - if ((id = atoi((const char *)sep->arg[1])) == 0) - MakeAnyLenString(&query, - "SELECT id,name" - " FROM npc_types WHERE name LIKE '%%%s%%'", - sep->arg[1]); - // Otherwise, look for just that npc id. - else - MakeAnyLenString(&query, - "SELECT id,name FROM npc_types WHERE id=%i", id); + int id = atoi((const char *)sep->arg[1]); + if (id == 0) // If id evaluates to 0, then search as if user entered a string. + query = StringFormat("SELECT id, name FROM npc_types WHERE name LIKE '%%%s%%'", sep->arg[1]); + else // Otherwise, look for just that npc id. + query = StringFormat("SELECT id, name FROM npc_types WHERE id = %i", id); - // If query runs successfully. - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - count = 0; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message (0, "Error querying database."); + c->Message (0, query.c_str()); + } - // Process each row returned. - while((row = mysql_fetch_row(result))) - { - // Limit to returning maxrows rows. - if (++count > maxrows) - { - c->Message (0, - "%i npc types shown. Too many results.", maxrows); - break; - } - c->Message (0, " %s: %s", row[0], row[1]); - } + if (results.RowCount() == 0) // No matches found. + c->Message (0, "No matches found for %s.", sep->arg[1]); - // If we did not hit the maxrows limit. - if (count <= maxrows) - c->Message (0, "Query complete. %i rows shown.", count); - // No matches found. - else if (count == 0) - c->Message (0, "No matches found for %s.", sep->arg[1]); + // If query runs successfully. + int count = 0; + const int maxrows = 20; - mysql_free_result(result); - } - // If query failed. - else - { - c->Message (0, "Error querying database."); - c->Message (0, query); - } + // Process each row returned. + for (auto row = results.begin(); row != results.end(); ++row) { + // Limit to returning maxrows rows. + if (++count > maxrows) { + c->Message (0, "%i npc types shown. Too many results.", maxrows); + break; + } + + c->Message (0, " %s: %s", row[0], row[1]); + } + + // If we did not hit the maxrows limit. + if (count <= maxrows) + c->Message (0, "Query complete. %i rows shown.", count); - safe_delete_array(query); - } } void command_findzone(Client *c, const Seperator *sep) { - if(sep->arg[1][0] == 0) + if(sep->arg[1][0] == 0) { c->Message(0, "Usage: #findzone [search criteria]"); - else - { - int id; - int count; - const int maxrows = 20; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query; - MYSQL_RES *result; - MYSQL_ROW row; + return; + } - query = new char[256]; + std::string query; + int id = atoi((const char *)sep->arg[1]); + if (id == 0) { // If id evaluates to 0, then search as if user entered a string. + char *escName = new char[strlen(sep->arg[1]) * 2 + 1]; + database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); - // If id evaluates to 0, then search as if user entered a string. - if ((id = atoi((const char *)sep->arg[1])) == 0) - { - char *EscName = new char[strlen(sep->arg[1]) * 2 + 1]; - database.DoEscapeString(EscName, sep->arg[1], strlen(sep->arg[1])); + query = StringFormat("SELECT zoneidnumber, short_name, long_name FROM zone " + "WHERE long_name RLIKE '%s' AND version = 0", escName); + safe_delete_array(escName); + } + else // Otherwise, look for just that zoneidnumber. + query = StringFormat("SELECT zoneidnumber, short_name, long_name FROM zone " + "WHERE zoneidnumber = %i AND version = 0", id); - MakeAnyLenString(&query, "SELECT zoneidnumber,short_name,long_name FROM zone WHERE long_name rLIKE '%s' AND version=0", - EscName); - safe_delete_array(EscName); - } - // Otherwise, look for just that zoneidnumber. - else - MakeAnyLenString(&query, "SELECT zoneidnumber,short_name,long_name FROM zone WHERE zoneidnumber=%i AND version=0", id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message (0, "Error querying database."); + c->Message (0, query.c_str()); + return; + } - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - count = 0; + int count = 0; + const int maxrows = 20; - while((row = mysql_fetch_row(result))) - { - if (++count > maxrows) - { - c->Message (0, "%i zones shown. Too many results.", maxrows); - break; - } - c->Message (0, " %s: %s, %s", row[0], row[1], row[2]); - } + for(auto row = results.begin(); row != results.end(); ++row) { + if (++count > maxrows) { + c->Message (0, "%i zones shown. Too many results.", maxrows); + break; + } - if (count <= maxrows) - c->Message (0, "Query complete. %i rows shown.", count); - else if (count == 0) - c->Message (0, "No matches found for %s.", sep->arg[1]); + c->Message (0, " %s: %s, %s", row[0], row[1], row[2]); + } - mysql_free_result(result); - } - else - { - c->Message (0, "Error querying database."); - c->Message (0, query); - } - - safe_delete_array(query); - } + if (count <= maxrows) + c->Message (0, "Query complete. %i rows shown.", count); + else if (count == 0) + c->Message (0, "No matches found for %s.", sep->arg[1]); } void command_viewnpctype(Client *c, const Seperator *sep) @@ -3519,23 +3252,20 @@ void command_motd(Client *c, const Seperator *sep) void command_listpetition(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool header = false; - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, accountname from petitions order by petid"), errbuf, &result)) { - LogFile->write(EQEMuLog::Normal,"Petition list requested by %s", c->GetName()); - while ((row = mysql_fetch_row(result))) { - if(!header) { - header = true; - c->Message(13," ID : Character Name , Account Name"); - } - c->Message(15, " %s: %s , %s ",row[0],row[1],row[2]); - } - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = "SELECT petid, charname, accountname FROM petitions ORDER BY petid"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + LogFile->write(EQEMuLog::Normal,"Petition list requested by %s", c->GetName()); + + if (results.RowCount() == 0) + return; + + c->Message(13," ID : Character Name , Account Name"); + + for (auto row = results.begin(); row != results.end(); ++row) + c->Message(15, " %s: %s , %s ",row[0],row[1],row[2]); } void command_equipitem(Client *c, const Seperator *sep) @@ -4297,25 +4027,24 @@ void command_repop(Client *c, const Seperator *sep) LinkedListIterator iterator(zone->spawn2_list); iterator.Reset(); - while (iterator.MoreElements()) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE id=%lu" - " AND instance_id=%lu",(unsigned long)iterator.GetData()->GetID(), (unsigned long)zone->GetInstanceID()), errbuf); - safe_delete_array(query); + while (iterator.MoreElements()) { + std::string query = StringFormat("DELETE FROM respawn_times WHERE id = %lu AND instance_id = %lu", + (unsigned long)iterator.GetData()->GetID(), + (unsigned long)zone->GetInstanceID()); + auto results = database.QueryDatabase(query); iterator.Advance(); } c->Message(0, "Zone depop: Force resetting spawn timers."); } - if (sep->IsNumber(timearg)) { - c->Message(0, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); - zone->Repop(atoi(sep->arg[timearg])*1000); - } - else { - c->Message(0, "Zone depoped. Repoping now."); + + if (!sep->IsNumber(timearg)) { + c->Message(0, "Zone depoped. Repoping now."); zone->Repop(); + return; } + + c->Message(0, "Zone depoped. Repop in %i seconds", atoi(sep->arg[timearg])); + zone->Repop(atoi(sep->arg[timearg])*1000); } void command_spawnstatus(Client *c, const Seperator *sep) @@ -4660,33 +4389,31 @@ void command_npcspawn(Client *c, const Seperator *sep) } void command_spawnfix(Client *c, const Seperator *sep) { - Mob *t = c->GetTarget(); - if (!t || !t->IsNPC()) + Mob *targetMob = c->GetTarget(); + if (!targetMob || !targetMob->IsNPC()) { c->Message(0, "Error: #spawnfix: Need an NPC target."); - else { - Spawn2* s2 = t->CastToNPC()->respawn2; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + return; + } - if(!s2) { - c->Message(0, "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET x='%f', y='%f', z='%f', heading='%f' WHERE id='%i'",c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Updating coordinates successful."); - t->Depop(false); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } + Spawn2* s2 = targetMob->CastToNPC()->respawn2; + + if(!s2) { + c->Message(0, "#spawnfix FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = '%i'", + c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Updating coordinates successful."); + targetMob->Depop(false); } void command_loc(Client *c, const Seperator *sep) @@ -5294,53 +5021,6 @@ void command_manaburn(Client *c, const Seperator *sep) } } -void command_viewmessage(Client *c, const Seperator *sep) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(sep->arg[1][0]==0) - { - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where receiver='%s'",c->GetName()), errbuf, &result)) - { - if (mysql_num_rows(result)>0) - { - c->Message(0,"You have messages waiting for you to view."); - c->Message(0,"Type #Viewmessage to view the message."); - c->Message(0," ID , Message Sent Date, Message Sender"); - while ((row = mysql_fetch_row(result))) - c->Message(0,"ID: %s Sent Date: %s Sender: %s ",row[0],row[1],row[3]); - } - else - c->Message(0,"You have no new messages"); - mysql_free_result(result); - } - safe_delete_array(query); - } - else - { - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT id,date,receiver,sender,message from tellque where id=%s",sep->argplus[1]), errbuf, &result)) - { - if (mysql_num_rows(result)==1) - { - row = mysql_fetch_row(result); - mysql_free_result(result); - if (strcasecmp((const char *) c->GetName(), (const char *) row[2]) == 0) - { - c->Message(15,"ID: %s,Sent Date: %s,Sender: %s,Message: %s",row[0],row[1],row[3],row[4]); - database.RunQuery(query, MakeAnyLenString(&query, "Delete from tellque where id=%s",row[0]), errbuf); - } - else - c->Message(13,"Invalid Message Number, check the number and try again."); - } - else - c->Message(13,"Invalid Message Number, check the number and try again."); - } - safe_delete_array(query); - } -} - void command_doanim(Client *c, const Seperator *sep) { if (!sep->IsNumber(1)) @@ -6274,13 +5954,13 @@ void command_setcrystals(Client *c, const Seperator *sep) { t->SetRadiantCrystals(atoi(sep->arg[2])); t->SendCrystalCounts(); - t->Save(); + t->SaveCurrency(); } else if(!strcasecmp(sep->arg[1], "ebon")) { t->SetEbonCrystals(atoi(sep->arg[2])); t->SendCrystalCounts(); - t->Save(); + t->SaveCurrency(); } else { @@ -6307,123 +5987,139 @@ void command_stun(Client *c, const Seperator *sep) c->Message(0, "Usage: #stun [duration]"); } + void command_ban(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(sep->arg[1][0] == 0) - { - c->Message(0, "Usage: #ban [charname]"); +if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { + c->Message(0, "Usage: #ban "); + return; } - else - { - database.RunQuery(query, MakeAnyLenString(&query, "SELECT account_id from character_ where name = '%s'", sep->arg[1]), errbuf, &result); - if(query) - { - safe_delete_array(query); - } - if(mysql_num_rows(result)) - { - row = mysql_fetch_row(result); - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set status = -2 where id = %i", atoi(row[0])), errbuf, 0); - c->Message(13,"Account number %i with the character %s has been banned.", atoi(row[0]), sep->arg[1]); + auto account_id = database.GetAccountIDByChar(sep->arg[1]); - ServerPacket* pack = new ServerPacket(ServerOP_FlagUpdate, 6); - *((uint32*) pack->pBuffer) = atoi(row[0]); - *((int16*) &pack->pBuffer[4]) = -2; - worldserver.SendPacket(pack); - safe_delete(pack); + std::string message; + int i = 2; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } - Client *client = nullptr; - client = entity_list.GetClientByName(sep->arg[1]); - if(client) - { - client->WorldKick(); - } - else - { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer; - strcpy(skp->adminname, c->GetName()); - strcpy(skp->name, sep->arg[1]); - skp->adminrank = c->Admin(); - worldserver.SendPacket(pack); - safe_delete(pack); - } + if(message.length() > 0) { + message.push_back(' '); + } - mysql_free_result(result); - } - else - { - c->Message(13,"Character does not exist."); - } - if(query) - { - safe_delete_array(query); - } - } + message += sep->arg[i]; + ++i; + } + + if(message.length() == 0) { + c->Message(0, "Usage: #ban "); + return; + } + + if(account_id == 0) { + c->Message(13, "Character does not exist."); + return; + } + + std::string query = StringFormat("UPDATE account SET status = -2, ban_reason = '%s' " + "WHERE id = %i", EscapeString(message).c_str(), account_id); + auto results = database.QueryDatabase(query); + + c->Message(13, "Account number %i with the character %s has been banned with message: \"%s\"", account_id, sep->arg[1], message.c_str()); + + ServerPacket flagUpdatePack(ServerOP_FlagUpdate, 6); + *((uint32*)&flagUpdatePack.pBuffer[0]) = account_id; + *((int16*)&flagUpdatePack.pBuffer[4]) = -2; + worldserver.SendPacket(&flagUpdatePack); + + Client *client = nullptr; + client = entity_list.GetClientByName(sep->arg[1]); + if(client) { + client->WorldKick(); + return; + } + + ServerPacket kickPlayerPack(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*)kickPlayerPack.pBuffer; + strcpy(skp->adminname, c->GetName()); + strcpy(skp->name, sep->arg[1]); + skp->adminrank = c->Admin(); + worldserver.SendPacket(&kickPlayerPack); } void command_suspend(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; + if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) { + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); + return; + } - if((sep->arg[1][0] == 0) || (sep->arg[2][0] == 0)) - c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately)"); - else - { - int Duration = atoi(sep->arg[2]); + int duration = atoi(sep->arg[2]); - if(Duration < 0) - Duration = 0; + if(duration < 0) + duration = 0; - char *EscName = new char[strlen(sep->arg[1]) * 2 + 1]; + std::string message; - database.DoEscapeString(EscName, sep->arg[1], strlen(sep->arg[1])); + if(duration > 0) { + int i = 3; + while(1) { + if(sep->arg[i][0] == 0) { + break; + } - int AccountID; + if(message.length() > 0) { + message.push_back(' '); + } - if((AccountID = database.GetAccountIDByChar(EscName)) > 0) - { - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY)" - " WHERE `id` = %i", Duration, AccountID), errbuf, 0); + message += sep->arg[i]; + ++i; + } - if(Duration) - c->Message(13,"Account number %i with the character %s has been temporarily suspended for %i day(s).", AccountID, sep->arg[1], - Duration); - else - c->Message(13,"Account number %i with the character %s is no longer suspended.", AccountID, sep->arg[1]); + if(message.length() == 0) { + c->Message(0, "Usage: #suspend (Specify 0 days to lift the suspension immediately) "); + return; + } + } - safe_delete_array(query); + char *escName = new char[strlen(sep->arg[1]) * 2 + 1]; + database.DoEscapeString(escName, sep->arg[1], strlen(sep->arg[1])); + int accountID = database.GetAccountIDByChar(escName); + safe_delete_array(escName); - Client *BannedClient = entity_list.GetClientByName(sep->arg[1]); + if (accountID <= 0) { + c->Message(13,"Character does not exist."); + return; + } - if(BannedClient) - BannedClient->WorldKick(); - else - { - ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); - ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*) pack->pBuffer; + std::string query = StringFormat("UPDATE `account` SET `suspendeduntil` = DATE_ADD(NOW(), INTERVAL %i DAY), " + "suspend_reason = '%s' WHERE `id` = %i", + duration, EscapeString(message).c_str(), accountID); + auto results = database.QueryDatabase(query); - strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); - strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); - sks->adminrank = c->Admin(); + if(duration) + c->Message(13,"Account number %i with the character %s has been temporarily suspended for %i day(s).", accountID, sep->arg[1], duration); + else + c->Message(13,"Account number %i with the character %s is no longer suspended.", accountID, sep->arg[1]); - worldserver.SendPacket(pack); + Client *bannedClient = entity_list.GetClientByName(sep->arg[1]); - safe_delete(pack); - } + if(bannedClient) { + bannedClient->WorldKick(); + return; + } - } else - c->Message(13,"Character does not exist."); + ServerPacket* pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct)); + ServerKickPlayer_Struct* sks = (ServerKickPlayer_Struct*) pack->pBuffer; - safe_delete_array(EscName); - } + strn0cpy(sks->adminname, c->GetName(), sizeof(sks->adminname)); + strn0cpy(sks->name, sep->arg[1], sizeof(sks->name)); + sks->adminrank = c->Admin(); + + worldserver.SendPacket(pack); + + safe_delete(pack); } void command_ipban(Client *c, const Seperator *sep) @@ -6442,50 +6138,41 @@ void command_ipban(Client *c, const Seperator *sep) void command_revoke(Client *c, const Seperator *sep) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) - { + if(sep->arg[1][0] == 0 || sep->arg[2][0] == 0) { c->Message(0, "Usage: #revoke [charname] [1/0]"); + return; } - else - { - uint32 tmp = database.GetAccountIDByChar(sep->arg[1]); - if(tmp) - { - int flag = sep->arg[2][0] == '1' ? true : false; - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE account set revoked=%d where id = %i", flag, tmp), errbuf, 0); - c->Message(13,"%s account number %i with the character %s.", flag?"Revoking":"Unrevoking", tmp, sep->arg[1]); - Client* revokee = entity_list.GetClientByAccID(tmp); - if(revokee) - { - c->Message(0, "Found %s in this zone.", revokee->GetName()); - revokee->SetRevoked(flag); - } - else - { + + uint32 characterID = database.GetAccountIDByChar(sep->arg[1]); + if(characterID == 0) { + c->Message(13,"Character does not exist."); + return; + } + + int flag = sep->arg[2][0] == '1' ? true : false; + std::string query = StringFormat("UPDATE account SET revoked = %d WHERE id = %i", flag, characterID); + auto results = database.QueryDatabase(query); + + c->Message(13,"%s account number %i with the character %s.", flag? "Revoking": "Unrevoking", characterID, sep->arg[1]); + + Client* revokee = entity_list.GetClientByAccID(characterID); + if(revokee) { + c->Message(0, "Found %s in this zone.", revokee->GetName()); + revokee->SetRevoked(flag); + return; + } + #if EQDEBUG >= 6 - c->Message(0, "Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); + c->Message(0, "Couldn't find %s in this zone, passing request to worldserver.", sep->arg[1]); #endif - ServerPacket * outapp = new ServerPacket (ServerOP_Revoke,sizeof(RevokeStruct)); - RevokeStruct* revoke = (RevokeStruct*)outapp->pBuffer; - strn0cpy(revoke->adminname, c->GetName(), 64); - strn0cpy(revoke->name, sep->arg[1], 64); - revoke->toggle = flag; - worldserver.SendPacket(outapp); - safe_delete(outapp); - } - } - else { - c->Message(13,"Character does not exist."); - } - if(query) - { - safe_delete_array(query); - query=nullptr; - } - } + + ServerPacket * outapp = new ServerPacket (ServerOP_Revoke,sizeof(RevokeStruct)); + RevokeStruct* revoke = (RevokeStruct*)outapp->pBuffer; + strn0cpy(revoke->adminname, c->GetName(), 64); + strn0cpy(revoke->name, sep->arg[1], 64); + revoke->toggle = flag; + worldserver.SendPacket(outapp); + safe_delete(outapp); } void command_oocmute(Client *c, const Seperator *sep) @@ -6595,12 +6282,11 @@ void command_npcemote(Client *c, const Seperator *sep) } void command_npcedit(Client *c, const Seperator *sep) -{ - if (!c->GetTarget() || !c->GetTarget()->IsNPC()) - { +{ if (!c->GetTarget() || !c->GetTarget()->IsNPC()) { c->Message(0, "Error: Must have NPC targeted"); return; } + if ( strcasecmp( sep->arg[1], "help" ) == 0 ) { c->Message(0, "Help File for #npcedit. Syntax for commands are:"); @@ -6673,663 +6359,731 @@ void command_npcedit(Client *c, const Seperator *sep) c->Message(0, "#npcedit version - Set an NPC's version"); } - else if ( strcasecmp( sep->arg[1], "name" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has the name %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set name='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + uint32 npcTypeID = c->GetTarget()->CastToNPC()->GetNPCTypeID(); + + if ( strcasecmp( sep->arg[1], "name" ) == 0 ) { + c->Message(15,"NPCID %u now has the name %s.",npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET name = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "lastname" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has the lastname %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set lastname='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "race" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has the race %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set race=%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], "class" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now class %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set class=%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], "bodytype" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has type %i bodytype.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set bodytype=%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], "hp" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Hitpoints.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set hp=%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], "gender" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now gender %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set gender=%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], "texture" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now uses texture %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set texture=%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], "helmtexture" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now uses helmtexture %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set helmtexture=%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], "size" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now size %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set size=%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], "hpregen" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now regens %i hitpoints per tick.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set hp_regen_rate=%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], "manaregen" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now regens %i mana per tick.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set mana_regen_rate=%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], "loottable" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now on loottable_id %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set loottable_id=%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], "merchantid" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now merchant_id %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set merchant_id=%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], "alt_currency_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'alt_currency_id' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set alt_currency_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "npc_spells_effects_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'npc_spells_effects_id' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_spells_effects_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "adventure_template_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'adventure_template_id' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set adventure_template_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "trap_template" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'trap_template' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set trap_template='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "special_abilities" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'special_abilities' set to %s.",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set special_abilities='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "spell" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now uses spell list %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_spells_id=%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], "faction" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now faction %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_faction_id=%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], "mindmg" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now hits for a min of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set mindmg=%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], "maxdmg" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now hits for a max of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set maxdmg=%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], "aggroradius" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has an aggro radius of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set aggroradius=%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], "assistradius" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has an assist radius of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set assistradius=%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], "social" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u social status is now %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set social=%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], "runspeed" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now runs at %f",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atof(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set runspeed=%f where id=%i",atof(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "AGI" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Agility.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set AGI=%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], "CHA" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Charisma.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set CHA=%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], "DEX" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Dexterity.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set DEX=%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], "INT" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Intelligence.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set _INT=%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], "STA" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Stamina.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set STA=%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], "STR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Strength.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set STR=%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], "WIS" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Magic Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set WIS=%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], "MR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Magic Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set MR=%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], "DR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Disease Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set DR=%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], "CR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Cold Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set CR=%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], "FR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Fire Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set FR=%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], "PR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Poison Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set PR=%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], "Corrup" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a Corruption Resistance of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set corrup=%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], "PhR" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a Physical Resistance of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set PhR=%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], "seeinvis" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seeinvis set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_invis=%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], "seeinvisundead" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seeinvisundead set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_invis_undead=%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], "seehide" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seehide set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_hide=%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], "seeimprovedhide" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has seeimprovedhide set to %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set see_improved_hide=%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], "AC" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Armor Class.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set ac=%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], "ATK" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Attack.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set atk=%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], "Accuracy" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i Accuracy.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set accuracy=%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], "level" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u is now level %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set level=%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], "maxlevel" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a maximum level of %i.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set maxlevel=%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], "qglobal" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "Quest globals have been %s for NPCID %u", atoi(sep->arg[2]) == 0 ? "disabled" : "enabled", c->GetTarget()->CastToNPC()->GetNPCTypeID()); - database.RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET qglobal=%i WHERE id=%i", atoi(sep->argplus[2]) == 0 ? 0 : 1, c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "npcaggro" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u will now %s other NPCs with negative faction npc_value",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])==0?"not aggro":"aggro"); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set npc_aggro=%i where id=%i",atoi(sep->argplus[2])==0?0:1,c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "limit" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has a spawn limit of %i",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set limit=%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], "Attackspeed" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has attack_speed set to %f",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atof(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set attack_speed=%f where id=%i",atof(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - 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]; - char *query = 0; - c->Message(15,"NPCID %u is now %s",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])==0?"not findable":"findable"); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set findable=%i where id=%i",atoi(sep->argplus[2])==0?0:1,c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "wep1" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u will have item graphic %i set to his primary on repop.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set d_meele_texture1=%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], "wep2" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u will have item graphic %i set to his secondary on repop.",c->GetTarget()->CastToNPC()->GetNPCTypeID(),atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set d_meele_texture2=%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], "featuresave" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u saved with all current facial feature settings",c->GetTarget()->CastToNPC()->GetNPCTypeID()); + if ( strcasecmp( sep->arg[1], "lastname" ) == 0 ) { + c->Message(15,"NPCID %u now has the lastname %s.",npcTypeID, sep->argplus[2]); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_haircolor=%i where id=%i",c->GetTarget()->GetHairColor(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_beardcolor=%i where id=%i",c->GetTarget()->GetBeardColor(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_hairstyle=%i where id=%i",c->GetTarget()->GetHairStyle(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set luclin_beard=%i where id=%i",c->GetTarget()->GetBeard(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set face=%i where id=%i",c->GetTarget()->GetLuclinFace(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set drakkin_heritage=%i where id=%i",c->GetTarget()->GetDrakkinHeritage(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set drakkin_tattoo=%i where id=%i",c->GetTarget()->GetDrakkinTattoo(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set drakkin_details=%i where id=%i",c->GetTarget()->GetDrakkinDetails(),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + std::string query = StringFormat("UPDATE npc_types SET lastname = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + if ( strcasecmp( sep->arg[1], "race" ) == 0 ) { + c->Message(15,"NPCID %u now has the race %i.",npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET race = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "color" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has %i red, %i green, and %i blue tinting on their armor.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set armortint_red=%i, armortint_green=%i, armortint_blue=%i where id=%i", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + if ( strcasecmp( sep->arg[1], "class" ) == 0 ) { + c->Message(15,"NPCID %u is now class %i.",npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET class = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "armortint_id" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15,"NPCID %u now has field 'armortint_id' set to %s",c->GetTarget()->CastToNPC()->GetNPCTypeID(), (sep->argplus[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set armortint_id='%s' where id=%i",(sep->argplus[2]),c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + if ( strcasecmp( sep->arg[1], "bodytype" ) == 0 ) { + c->Message(15,"NPCID %u now has type %i bodytype.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET bodytype = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } - else if ( strcasecmp( sep->arg[1], "setanimation" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - int Animation = 0; - if(sep->arg[2] && atoi(sep->arg[2]) <= 4){ - if((strcasecmp( sep->arg[2], "stand" ) == 0) || atoi(sep->arg[2]) == 0){ - Animation = 0; //Stand - } - if((strcasecmp( sep->arg[2], "sit" ) == 0) || atoi(sep->arg[2]) == 1){ - Animation = 1; //Sit - } - if((strcasecmp( sep->arg[2], "crouch" ) == 0) || atoi(sep->arg[2]) == 2){ - Animation = 2; //Crouch - } - if((strcasecmp( sep->arg[2], "dead" ) == 0) || atoi(sep->arg[2]) == 3){ - Animation = 3; //Dead - } - if((strcasecmp( sep->arg[2], "loot" ) == 0) || atoi(sep->arg[2]) == 4){ - Animation = 4; //Looting Animation - } + + if ( strcasecmp( sep->arg[1], "hp" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Hitpoints.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET hp = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "gender" ) == 0 ) { + c->Message(15,"NPCID %u is now gender %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET gender = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "texture" ) == 0 ) { + c->Message(15,"NPCID %u now uses texture %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET texture = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "helmtexture" ) == 0 ) { + c->Message(15,"NPCID %u now uses helmtexture %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET helmtexture = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "size" ) == 0 ) { + c->Message(15,"NPCID %u is now size %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET size = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "hpregen" ) == 0 ) { + c->Message(15,"NPCID %u now regens %i hitpoints per tick.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET hp_regen_rate = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "manaregen" ) == 0 ) { + c->Message(15,"NPCID %u now regens %i mana per tick.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET mana_regen_rate = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "loottable" ) == 0 ) { + c->Message(15,"NPCID %u is now on loottable_id %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET loottable_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "merchantid" ) == 0 ) { + c->Message(15,"NPCID %u is now merchant_id %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET merchant_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "alt_currency_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'alt_currency_id' set to %s.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET alt_currency_id = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "npc_spells_effects_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'npc_spells_effects_id' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET npc_spells_effects_id = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "adventure_template_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'adventure_template_id' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET adventure_template_id = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "trap_template" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'trap_template' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET trap_template = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "special_abilities" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'special_abilities' set to %s.", npcTypeID, sep->argplus[2]); + + std::string query = StringFormat("UPDATE npc_types SET special_abilities = '%s' WHERE id = %i", + sep->argplus[2],npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "spell" ) == 0 ) { + c->Message(15,"NPCID %u now uses spell list %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET npc_spells_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "faction" ) == 0 ) { + c->Message(15,"NPCID %u is now faction %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET npc_faction_id = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "mindmg" ) == 0 ) { + c->Message(15,"NPCID %u now hits for a min of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET mindmg = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "maxdmg" ) == 0 ) { + c->Message(15,"NPCID %u now hits for a max of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET maxdmg = %i WHERE id = %i", + atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "aggroradius" ) == 0 ) { + c->Message(15,"NPCID %u now has an aggro radius of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET aggroradius = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "assistradius" ) == 0 ) { + c->Message(15,"NPCID %u now has an assist radius of %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET assistradius = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "social" ) == 0 ) { + c->Message(15,"NPCID %u social status is now %i", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET social = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "runspeed" ) == 0 ) { + c->Message(15,"NPCID %u now runs at %f", npcTypeID, atof(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET runspeed = %f WHERE id = %i", + atof(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "AGI" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Agility.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET AGI = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "CHA" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Charisma.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET CHA = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "DEX" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Dexterity.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET DEX = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "INT" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Intelligence.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET _INT = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "STA" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Stamina.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET STA = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "STR" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Strength.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET STR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "WIS" ) == 0 ) { + c->Message(15,"NPCID %u now has a Magic Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET WIS = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "MR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Magic Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET MR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "DR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Disease Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET DR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "CR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Cold Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET CR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "FR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Fire Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET FR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "PR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Poison Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET PR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Corrup" ) == 0 ) { + c->Message(15,"NPCID %u now has a Corruption Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET corrup = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "PhR" ) == 0 ) { + c->Message(15,"NPCID %u now has a Physical Resistance of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET PhR = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seeinvis" ) == 0 ) { + c->Message(15,"NPCID %u now has seeinvis set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_invis = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seeinvisundead" ) == 0 ) { + c->Message(15,"NPCID %u now has seeinvisundead set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_invis_undead = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seehide" ) == 0 ) { + c->Message(15,"NPCID %u now has seehide set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_hide = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "seeimprovedhide" ) == 0 ) { + c->Message(15,"NPCID %u now has seeimprovedhide set to %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET see_improved_hide = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "AC" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Armor Class.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET ac = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "ATK" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Attack.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET atk = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Accuracy" ) == 0 ) { + c->Message(15,"NPCID %u now has %i Accuracy.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET accuracy = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "level" ) == 0 ) { + c->Message(15,"NPCID %u is now level %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET level = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "maxlevel" ) == 0 ) { + c->Message(15,"NPCID %u now has a maximum level of %i.", npcTypeID, atoi(sep->argplus[2])); + + std::string query = StringFormat("UPDATE npc_types SET maxlevel = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "qglobal" ) == 0 ) { + c->Message(15,"Quest globals have been %s for NPCID %u", + atoi(sep->arg[2]) == 0 ? "disabled" : "enabled", npcTypeID); + + std::string query = StringFormat("UPDATE npc_types SET qglobal = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "npcaggro" ) == 0 ) { + c->Message(15,"NPCID %u will now %s other NPCs with negative faction npc_value", + npcTypeID, atoi(sep->arg[2]) == 0? "not aggro": "aggro"); + + std::string query = StringFormat("UPDATE npc_types SET npc_aggro = %i WHERE id = %i", + atoi(sep->argplus[2]) == 0? 0: 1, npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "limit" ) == 0 ) { + c->Message(15,"NPCID %u now has a spawn limit of %i", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET limit = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Attackspeed" ) == 0 ) { + c->Message(15,"NPCID %u now has attack_speed set to %f", + npcTypeID, atof(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET attack_speed = %f WHERE id = %i", + atof(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "Attackdelay" ) == 0 ) { + c->Message(15,"NPCID %u now has attack_delay set to %i",npcTypeID,atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET attack_delay = %i WHERE id = %i",atoi(sep->argplus[2]),npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "findable" ) == 0 ) { + c->Message(15,"NPCID %u is now %s", npcTypeID, atoi(sep->arg[2]) == 0? "not findable": "findable"); + + std::string query = StringFormat("UPDATE npc_types SET findable = %i WHERE id = %i", + atoi(sep->argplus[2]) == 0? 0: 1, npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "wep1" ) == 0 ) { + c->Message(15,"NPCID %u will have item graphic %i set to his primary on repop.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET d_meele_texture1 = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "wep2" ) == 0 ) { + c->Message(15,"NPCID %u will have item graphic %i set to his secondary on repop.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET d_meele_texture2 = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "featuresave" ) == 0 ) { + c->Message(15,"NPCID %u saved with all current facial feature settings", + npcTypeID); + + Mob* target = c->GetTarget(); + + std::string query = StringFormat("UPDATE npc_types " + "SET luclin_haircolor = %i, luclin_beardcolor = %i, " + "luclin_hairstyle = %i, luclin_beard = %i, " + "face = %i, drakkin_heritage = %i, " + "drakkin_tattoo = %i, drakkin_details = %i, " + "WHERE id = %i", + target->GetHairColor(), target->GetBeardColor(), + target->GetHairStyle(), target->GetBeard(), + target->GetLuclinFace(), target->GetDrakkinHeritage(), + target->GetDrakkinTattoo(), target->GetDrakkinDetails(), + npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "color" ) == 0 ) { + c->Message(15,"NPCID %u now has %i red, %i green, and %i blue tinting on their armor.", + npcTypeID, atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); + + std::string query = StringFormat("UPDATE npc_types " + "SET armortint_red = %i, armortint_green = %i, armortint_blue = %i " + "WHERE id = %i", + atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "armortint_id" ) == 0 ) { + c->Message(15,"NPCID %u now has field 'armortint_id' set to %s", + npcTypeID, sep->arg[2]); + + std::string query = StringFormat("UPDATE npc_types SET armortint_id = '%s' WHERE id = %i", + sep->argplus[2], npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "setanimation" ) == 0 ) { + int animation = 0; + if(sep->arg[2] && atoi(sep->arg[2]) <= 4) { + if((strcasecmp( sep->arg[2], "stand" ) == 0) || atoi(sep->arg[2]) == 0) + animation = 0; //Stand + if((strcasecmp( sep->arg[2], "sit" ) == 0) || atoi(sep->arg[2]) == 1) + animation = 1; //Sit + if((strcasecmp( sep->arg[2], "crouch" ) == 0) || atoi(sep->arg[2]) == 2) + animation = 2; //Crouch + if((strcasecmp( sep->arg[2], "dead" ) == 0) || atoi(sep->arg[2]) == 3) + animation = 3; //Dead + if((strcasecmp( sep->arg[2], "loot" ) == 0) || atoi(sep->arg[2]) == 4) + animation = 4; //Looting Animation } - else{ + else { c->Message(0, "You must specifiy an animation stand, sit, crouch, dead, loot (0-4)"); c->Message(0, "Example: #npcedit setanimation sit"); c->Message(0, "Example: #npcedit setanimation 0"); return; } - c->Message(15,"NPCID %u now has the animation set to %i on spawn with spawngroup %i", c->GetTarget()->CastToNPC()->GetNPCTypeID(), Animation, c->GetTarget()->CastToNPC()->GetSp2() ); - database.RunQuery(query, MakeAnyLenString(&query, "update spawn2 set animation = %i where spawngroupID=%i", Animation, c->GetTarget()->CastToNPC()->GetSp2()), errbuf); - c->GetTarget()->SetAppearance(EmuAppearance(Animation)); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "scalerate" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a scaling rate of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set scalerate=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "healscale" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a heal scaling rate of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set healscale=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "spellscale" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u now has a spell scaling rate of %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set spellscale=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "no_target" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u is now %s.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2]) == 0 ? "targetable" : "untargetable"); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set no_target_hotkey=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); - } - else if ( strcasecmp( sep->arg[1], "version" ) == 0 ) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - c->Message(15, "NPCID %u is now version %i.", c->GetTarget()->CastToNPC()->GetNPCTypeID(), atoi(sep->arg[2])); - database.RunQuery(query, MakeAnyLenString(&query, "update npc_types set version=%i where id=%i", atoi(sep->arg[2]), c->GetTarget()->CastToNPC()->GetNPCTypeID()), errbuf); - c->LogSQL(query); - safe_delete_array(query); + + c->Message(15,"NPCID %u now has the animation set to %i on spawn with spawngroup %i", npcTypeID, animation, c->GetTarget()->CastToNPC()->GetSp2() ); + + std::string query = StringFormat("UPDATE spawn2 SET animation = %i " + "WHERE spawngroupID = %i", + animation, c->GetTarget()->CastToNPC()->GetSp2()); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + + c->GetTarget()->SetAppearance(EmuAppearance(animation)); + return; } - else if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) - { - c->Message(0, "Type #npcedit help for more info"); + if ( strcasecmp( sep->arg[1], "scalerate" ) == 0 ) { + c->Message(15,"NPCID %u now has a scaling rate of %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET scalerate = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; } + + if ( strcasecmp( sep->arg[1], "healscale" ) == 0 ) { + c->Message(15, "NPCID %u now has a heal scaling rate of %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET healscale = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "spellscale" ) == 0 ) { + c->Message(15, "NPCID %u now has a spell scaling rate of %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET spellscale = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "no_target" ) == 0 ) { + c->Message(15, "NPCID %u is now %s.", + npcTypeID, atoi(sep->arg[2]) == 0? "targetable": "untargetable"); + + std::string query = StringFormat("UPDATE npc_types SET no_target_hotkey = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if ( strcasecmp( sep->arg[1], "version" ) == 0 ) { + c->Message(15, "NPCID %u is now version %i.", + npcTypeID, atoi(sep->arg[2])); + + std::string query = StringFormat("UPDATE npc_types SET version = %i WHERE id = %i", + atoi(sep->argplus[2]), npcTypeID); + database.QueryDatabase(query); + c->LogSQL(query.c_str()); + return; + } + + if((sep->arg[1][0] == 0 || strcasecmp(sep->arg[1],"*")==0) || ((c->GetTarget()==0) || (c->GetTarget()->IsClient()))) + c->Message(0, "Type #npcedit help for more info"); + } #ifdef PACKET_PROFILER @@ -7434,56 +7188,58 @@ void command_nologs(Client *c, const Seperator *sep) void command_qglobal(Client *c, const Seperator *sep) { //In-game switch for qglobal column - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; if(sep->arg[1][0] == 0) { c->Message(0, "Syntax: #qglobal [on/off/view]. Requires NPC target."); return; } - Mob *t = c->GetTarget(); - if(!t || !t->IsNPC()) { + + Mob *target = c->GetTarget(); + + if(!target || !target->IsNPC()) { c->Message(13, "NPC Target Required!"); return; } - if(!strcasecmp(sep->arg[1], "on")) - { - if(!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET qglobal=1 WHERE id='%i'", t->GetNPCTypeID()), errbuf, 0)) - { + + if(!strcasecmp(sep->arg[1], "on")) { + std::string query = StringFormat("UPDATE npc_types SET qglobal = 1 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { c->Message(15, "Could not update database."); + return; } - else - { - c->LogSQL(query); - c->Message(15, "Success! Changes take effect on zone reboot."); - } - safe_delete(query); + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Changes take effect on zone reboot."); + return; } - else if(!strcasecmp(sep->arg[1], "off")) - { - if(!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET qglobal=0 WHERE id='%i'", t->GetNPCTypeID()), errbuf, 0)) - { + + if(!strcasecmp(sep->arg[1], "off")) { + std::string query = StringFormat("UPDATE npc_types SET qglobal = 0 WHERE id = '%i'", + target->GetNPCTypeID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { c->Message(15, "Could not update database."); + return; } - else - { - c->LogSQL(query); - c->Message(15, "Success! Changes take effect on zone reboot."); - } - safe_delete(query); + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Changes take effect on zone reboot."); + return; } - else if(!strcasecmp(sep->arg[1], "view")) - { - const NPCType *type = database.GetNPCType(t->GetNPCTypeID()); - if(!type) { + + if(!strcasecmp(sep->arg[1], "view")) { + const NPCType *type = database.GetNPCType(target->GetNPCTypeID()); + if(!type) c->Message(15, "Invalid NPC type."); - } else if(type->qglobal) { + else if(type->qglobal) c->Message(15, "This NPC has quest globals active."); - } else { + else c->Message(15, "This NPC has quest globals disabled."); - } - } else { - c->Message(15, "Invalid action specified."); + return; } + + c->Message(15, "Invalid action specified."); } void command_path(Client *c, const Seperator *sep) @@ -7810,7 +7566,7 @@ void Client::Undye() { SendWearChange(cur_slot); } - Save(0); + database.DeleteCharacterDye(this->CharacterID()); } void command_undye(Client *c, const Seperator *sep) @@ -7990,8 +7746,6 @@ void command_flags(Client *c, const Seperator *sep) { void command_flagedit(Client *c, const Seperator *sep) { //super-command for editing zone flags - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; if(sep->arg[1][0] == '\0' || !strcasecmp(sep->arg[1], "help")) { c->Message(0, "Syntax: #flagedit [lockzone|unlockzone|listzones|give|take]."); c->Message(0, "...lockzone [zone id/short] [flag name] - Set the specified flag name on the zone, locking the zone"); @@ -8024,17 +7778,21 @@ void command_flagedit(Client *c, const Seperator *sep) { database.DoEscapeString(flag_name, sep->argplus[3], 64); flag_name[127] = '\0'; - if(!database.RunQuery(query, MakeAnyLenString(&query, - "UPDATE zone SET flag_needed='%s' WHERE zoneidnumber=%d AND version=%d", - flag_name, zoneid, zone->GetInstanceVersion()), errbuf)) - { - c->Message(13, "Error updating zone: %s", errbuf); - } else { - c->LogSQL(query); - c->Message(15, "Success! Zone %s now requires a flag, named %s", database.GetZoneName(zoneid), flag_name); + std::string query = StringFormat("UPDATE zone SET flag_needed = '%s' " + "WHERE zoneidnumber = %d AND version = %d", + flag_name, zoneid, zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + c->Message(13, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; } - safe_delete(query); - } else if(!strcasecmp(sep->arg[1], "unlockzone")) { + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Zone %s now requires a flag, named %s", database.GetZoneName(zoneid), flag_name); + return; + } + + if(!strcasecmp(sep->arg[1], "unlockzone")) { uint32 zoneid = 0; if(sep->arg[2][0] != '\0') { zoneid = atoi(sep->arg[2]); @@ -8042,39 +7800,43 @@ void command_flagedit(Client *c, const Seperator *sep) { zoneid = database.GetZoneID(sep->arg[2]); } } + if(zoneid < 1) { c->Message(13, "zone required. see help."); return; } - if(!database.RunQuery(query, MakeAnyLenString(&query, - "UPDATE zone SET flag_needed='' WHERE zoneidnumber=%d AND version=%d", - zoneid, zone->GetInstanceVersion()), errbuf)) - { - c->Message(15, "Error updating zone: %s", errbuf); - } else { - c->LogSQL(query); - c->Message(15, "Success! Zone %s no longer requires a flag.", database.GetZoneName(zoneid)); + std::string query = StringFormat("UPDATE zone SET flag_needed = '' " + "WHERE zoneidnumber = %d AND version = %d", + zoneid, zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + c->Message(15, "Error updating zone: %s", results.ErrorMessage().c_str()); + return; } - safe_delete(query); - } else if(!strcasecmp(sep->arg[1], "listzones")) { - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT zoneidnumber,short_name,long_name,version,flag_needed FROM zone WHERE flag_needed != ''" - ), errbuf, &result)) - { - c->Message(0, "Zones which require flags:"); - while ((row = mysql_fetch_row(result))) - { - c->Message(0, "Zone %s (%s,%s) version %s requires key %s", row[2], row[0], row[1], row[3], row[4]); - } - mysql_free_result(result); - } else { - c->Message(13, "Unable to query zone flags: %s", errbuf); - } - safe_delete_array(query); - } else if(!strcasecmp(sep->arg[1], "give")) { + + c->LogSQL(query.c_str()); + c->Message(15, "Success! Zone %s no longer requires a flag.", database.GetZoneName(zoneid)); + return; + } + + if(!strcasecmp(sep->arg[1], "listzones")) { + std::string query = "SELECT zoneidnumber, short_name, long_name, version, flag_needed " + "FROM zone WHERE flag_needed != ''"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Unable to query zone flags: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(0, "Zones which require flags:"); + for (auto row = results.begin(); row != results.end(); ++row) + c->Message(0, "Zone %s (%s,%s) version %s requires key %s", row[2], row[0], row[1], row[3], row[4]); + + return; + } + + if(!strcasecmp(sep->arg[1], "give")) { uint32 zoneid = 0; if(sep->arg[2][0] != '\0') { zoneid = atoi(sep->arg[2]); @@ -8094,7 +7856,10 @@ void command_flagedit(Client *c, const Seperator *sep) { } t->CastToClient()->SetZoneFlag(zoneid); - } else if(!strcasecmp(sep->arg[1], "give")) { + return; + } + + if(!strcasecmp(sep->arg[1], "give")) { uint32 zoneid = 0; if(sep->arg[2][0] != '\0') { zoneid = atoi(sep->arg[2]); @@ -8114,9 +7879,10 @@ void command_flagedit(Client *c, const Seperator *sep) { } t->CastToClient()->ClearZoneFlag(zoneid); - } else { - c->Message(15, "Invalid action specified. use '#flagedit help' for help"); + return; } + + c->Message(15, "Invalid action specified. use '#flagedit help' for help"); } void command_mlog(Client *c, const Seperator *sep) { @@ -8662,25 +8428,6 @@ void command_altactivate(Client *c, const Seperator *sep){ } } -void command_refundaa(Client *c, const Seperator *sep){ - Client* refundee = nullptr; - if(c) { - if(c->GetTarget()){ - if(c->GetTarget()->IsClient()) - refundee = c->GetTarget()->CastToClient(); - else - c->Message(0, "Your target must be a client."); - } - else{ - c->Message(0, "You must have a target selected."); - } - - if(refundee) { - refundee->RefundAA(); - } - } -} - void command_traindisc(Client *c, const Seperator *sep) { uint8 max_level, min_level; @@ -8736,6 +8483,7 @@ void command_traindisc(Client *c, const Seperator *sep) break; //continue the 1st loop } else if(t->GetPP().disciplines.values[r] == 0) { t->GetPP().disciplines.values[r] = curspell; + database.SaveCharacterDisc(c->CharacterID(), r, curspell); t->SendDisciplineUpdate(); t->Message(0, "You have learned a new discipline!"); count++; //success counter @@ -8883,233 +8631,261 @@ void command_refreshgroup(Client *c, const Seperator *sep) void command_advnpcspawn(Client *c, const Seperator *sep) { Mob *target=c->GetTarget(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 last_insert_id = 0; - if (strcasecmp(sep->arg[1], "maketype") == 0){ - if(target && target->IsNPC()) - { - database.NPCSpawnDB(6, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); - } - else - c->Message(0, "Target Required!"); - } - else if (strcasecmp(sep->arg[1], "makegroup") == 0) { - if(sep->arg[2]) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name,spawn_limit,dist,max_x,min_x,max_y,min_y,delay) VALUES (\"%s\",%i,%f,%f,%f,%f,%f,%i)", sep->arg[2], (sep->arg[3]?atoi(sep->arg[3]):0), (sep->arg[4]?atof(sep->arg[4]):0), (sep->arg[5]?atof(sep->arg[5]):0), (sep->arg[6]?atof(sep->arg[6]):0), (sep->arg[7]?atof(sep->arg[7]):0), (sep->arg[8]?atof(sep->arg[8]):0), (sep->arg[9]?atoi(sep->arg[9]):0)), errbuf, 0, 0, &last_insert_id)) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "Group ID %i created successfully!", last_insert_id); - } - safe_delete_array(query); - } - else - { - c->Message(0, "Format: #advnpdspawn makegroup [spawn limit] [dist] [max x] [min x] [max y] [min y] [delay]"); - } - } - else if (strcasecmp(sep->arg[1], "addgroupentry") == 0) { - if(atoi(sep->arg[2]) && atoi(sep->arg[3]) && atoi(sep->arg[4])) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID,npcID,chance) VALUES (%i,%i,%i)", atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4]), errbuf, 0, 0, &last_insert_id))) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "NPC %i added to group %i with %i chance!", atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[4]) ); - } - safe_delete(query); - } - else - { - c->Message(0, "Format: #advnpdspawn addgroupentry "); - } - } - else if (strcasecmp(sep->arg[1], "editgroupbox") == 0) { - if(atof(sep->arg[2]) && atof(sep->arg[3]) && atof(sep->arg[4]) && atof(sep->arg[5]) && atof(sep->arg[6]) && atof(sep->arg[7]) && atof(sep->arg[8])) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawngroup SET dist='%f',max_x='%f',min_x='%f',max_y='%f',min_y='%f',delay='%i' WHERE id='%i'", atof(sep->arg[3]),atof(sep->arg[4]),atof(sep->arg[5]),atof(sep->arg[6]),atof(sep->arg[7]),atoi(sep->arg[8]),atoi(sep->arg[2]), errbuf, 0, 0, &last_insert_id))) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "Group ID %i created successfully!", last_insert_id); - } - safe_delete_array(query); - } - else - { - c->Message(0, "Format: #advnpdspawn editgroupbox "); - } - } - else if (strcasecmp(sep->arg[1], "cleargroupbox") == 0) { - if(atoi(sep->arg[2])) - { - if (!database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawngroup SET dist='0',max_x='0',min_x='0',max_y='0',min_y='0',delay='0' WHERE id='%i'",atoi(sep->arg[2])), errbuf, 0, 0, &last_insert_id)) - { - c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); - c->Message(13, errbuf); - } - else - { - c->LogSQL(query); - c->Message(0, "Group ID %i created successfully!", last_insert_id); - } - safe_delete_array(query); - } - else - { - c->Message(0, "Format: #advnpdspawn cleargroupbox "); - } - } - else if (strcasecmp(sep->arg[1], "addgroupspawn") == 0 && atoi(sep->arg[2])!=0) { - database.NPCSpawnDB(5, zone->GetShortName(), zone->GetInstanceVersion(), c, 0, atoi(sep->arg[2])); - c->Message(0, "Mob of group %i added successfully!", atoi(sep->arg[2])); - } - else if (strcasecmp(sep->arg[1], "removegroupspawn") == 0) { - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - Spawn2* s2 = target->CastToNPC()->respawn2; + if (strcasecmp(sep->arg[1], "maketype") == 0) { + if(!target || !target->IsNPC()) { + c->Message(0, "Target Required!"); + return; + } - if(!s2) { - c->Message(0, "removegroupspawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'",s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Spawnpoint Removed successfully."); - target->Depop(false); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } - } - else if (strcasecmp(sep->arg[1], "movespawn") == 0) { - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - Spawn2* s2 = target->CastToNPC()->respawn2; + database.NPCSpawnDB(6, zone->GetShortName(), zone->GetInstanceVersion(), c, target->CastToNPC()); + return; + } - if(!s2) { - c->Message(0, "movespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET x='%f', y='%f', z='%f', heading='%f' WHERE id='%i'",c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Updating coordinates successful."); - target->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); - target->CastToNPC()->SaveGuardSpot(true); - target->SendPosition(); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } - } - else if (strcasecmp(sep->arg[1], "editrespawn") == 0) { - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - Spawn2* s2 = target->CastToNPC()->respawn2; + if (strcasecmp(sep->arg[1], "makegroup") == 0) { + if(!sep->arg[2]) { + c->Message(0, "Format: #advnpdspawn makegroup [spawn limit] [dist] [max x] [min x] [max y] [min y] [delay]"); + return; + } - uint32 new_rs = 0; - uint32 new_var = s2->GetVariance(); - if(!sep->IsNumber(2)) - { - c->Message(0, "editrespawn FAILED -- cannot set respawn to be 0"); - return; - } - else - { - new_rs = atoi(sep->arg[2]); - } + std::string query = StringFormat("INSERT INTO spawngroup " + "(name, spawn_limit, dist, max_x, min_x, max_y, min_y, delay) " + "VALUES (\"%s\", %i, %f, %f, %f, %f, %f, %i)", + sep->arg[2], + (sep->arg[3]? atoi(sep->arg[3]): 0), + (sep->arg[4]? atof(sep->arg[4]): 0), + (sep->arg[5]? atof(sep->arg[5]): 0), + (sep->arg[6]? atof(sep->arg[6]): 0), + (sep->arg[7]? atof(sep->arg[7]): 0), + (sep->arg[8]? atof(sep->arg[8]): 0), + (sep->arg[9]? atoi(sep->arg[9]): 0)); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } - if(sep->IsNumber(3)) - { - new_var = atoi(sep->arg[3]); - } + c->LogSQL(query.c_str()); + c->Message(0, "Group ID %i created successfully!", results.LastInsertedID()); + return; + } - if(!s2) { - c->Message(0, "editrespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); - } - else - { - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET respawntime=%u, variance=%u WHERE id='%i'", new_rs, new_var, s2->GetID()), errbuf)) - { - c->LogSQL(query); - c->Message(0, "Updating respawn timer successful."); - s2->SetRespawnTimer(new_rs); - s2->SetVariance(new_var); - } - else - { - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - } - } - else if (strcasecmp(sep->arg[1], "setversion") == 0) { - int16 Version = 0; - if (!target || !target->IsNPC()) - c->Message(0, "Error: Need an NPC target."); - else { - if(sep->IsNumber(2)){ - Version = atoi(sep->arg[2]); - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET version=%i WHERE spawngroupID='%i'", Version, c->GetTarget()->CastToNPC()->GetSp2()), errbuf)){ - c->LogSQL(query); - c->Message(0, "Version change to %i was successful from SpawnGroupID %i", Version, c->GetTarget()->CastToNPC()->GetSp2()); - c->GetTarget()->Depop(false); - } - else{ - c->Message(13, "Update failed! MySQL gave the following error:"); - c->Message(13, errbuf); - } - safe_delete_array(query); - } - else{ - c->Message(0, "setversion FAILED -- You must set a version number"); - return; - } - } - } - else if (strcasecmp(sep->arg[1], "testload") == 0 && atoi(sep->arg[2])!=0) { - database.LoadSpawnGroupsByID(atoi(sep->arg[2]),&zone->spawn_group_list); - c->Message(0, "Group %i loaded successfully!", atoi(sep->arg[2])); - } - else { - c->Message(0, "Error: #advnpcspawn: Invalid command."); - c->Message(0, "Usage: #advnpcspawn [maketype|makegroup|addgroupentry|addgroupspawn|setversion]"); - c->Message(0, "Usage: #advnpcspawn [removegroupspawn|movespawn|editrespawn|editgroupbox|cleargroupbox]"); - } + if (strcasecmp(sep->arg[1], "addgroupentry") == 0) { + if(!atoi(sep->arg[2]) || !atoi(sep->arg[3]) || !atoi(sep->arg[4])) { + c->Message(0, "Format: #advnpdspawn addgroupentry "); + return; + } + + std::string query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) " + "VALUES (%i, %i, %i)", + atoi(sep->arg[2]), atoi(sep->arg[3]), atoi(sep->arg[4])); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "NPC %i added to group %i with %i chance!", atoi(sep->arg[3]), atoi(sep->arg[2]), atoi(sep->arg[4]) ); + + return; + } + + if (strcasecmp(sep->arg[1], "editgroupbox") == 0) { + if(!atof(sep->arg[2]) || !atof(sep->arg[3]) || !atof(sep->arg[4]) || !atof(sep->arg[5]) || !atof(sep->arg[6]) || !atof(sep->arg[7]) || !atof(sep->arg[8])) { + c->Message(0, "Format: #advnpdspawn editgroupbox "); + return; + } + + std::string query = StringFormat("UPDATE spawngroup SET dist = '%f', max_x = '%f', min_x = '%f', " + "max_y = '%f', min_y = '%f', delay = '%i' WHERE id = '%i'", + atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), + atof(sep->arg[6]), atof(sep->arg[7]), atoi(sep->arg[8]), + atoi(sep->arg[2])); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Group ID %i created successfully!", results.LastInsertedID()); + + return; + } + + if (strcasecmp(sep->arg[1], "cleargroupbox") == 0) { + if(!atoi(sep->arg[2])) { + c->Message(0, "Format: #advnpdspawn cleargroupbox "); + return; + } + + std::string query = StringFormat("UPDATE spawngroup " + "SET dist = '0', max_x = '0', min_x = '0', " + "max_y = '0', min_y = '0', delay = '0' " + "WHERE id = '%i' ", atoi(sep->arg[2])); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid Arguments -- MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Group ID %i created successfully!", results.LastInsertedID()); + + return; + } + + if (strcasecmp(sep->arg[1], "addgroupspawn") == 0 && atoi(sep->arg[2])!=0) { + database.NPCSpawnDB(5, zone->GetShortName(), zone->GetInstanceVersion(), c, 0, atoi(sep->arg[2])); + c->Message(0, "Mob of group %i added successfully!", atoi(sep->arg[2])); + return; + } + + if (strcasecmp(sep->arg[1], "removegroupspawn") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + Spawn2* s2 = target->CastToNPC()->respawn2; + + if(!s2) { + c->Message(0, "removegroupspawn FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", s2->GetID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Spawnpoint Removed successfully."); + target->Depop(false); + + return; + } + + if (strcasecmp(sep->arg[1], "movespawn") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + Spawn2* s2 = target->CastToNPC()->respawn2; + + if(!s2) { + c->Message(0, "movespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("UPDATE spawn2 SET x = '%f', y = '%f', z = '%f', heading = '%f' " + "WHERE id = '%i'", + c->GetX(), c->GetY(), c->GetZ(), c->GetHeading(),s2->GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Updating coordinates successful."); + target->CastToNPC()->GMMove(c->GetX(), c->GetY(), c->GetZ(), c->GetHeading()); + target->CastToNPC()->SaveGuardSpot(true); + target->SendPosition(); + + return; + } + + if (strcasecmp(sep->arg[1], "editrespawn") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + Spawn2* s2 = target->CastToNPC()->respawn2; + + uint32 new_rs = 0; + uint32 new_var = s2->GetVariance(); + if(!sep->IsNumber(2)) { + c->Message(0, "editrespawn FAILED -- cannot set respawn to be 0"); + return; + } + + new_rs = atoi(sep->arg[2]); + + if(sep->IsNumber(3)) + new_var = atoi(sep->arg[3]); + + if(!s2) { + c->Message(0, "editrespawn FAILED -- cannot determine which spawn entry in the database this mob came from."); + return; + } + + std::string query = StringFormat("UPDATE spawn2 SET respawntime = %u, variance = %u " + "WHERE id = '%i'", new_rs, new_var, s2->GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Updating respawn timer successful."); + s2->SetRespawnTimer(new_rs); + s2->SetVariance(new_var); + + return; + } + + if (strcasecmp(sep->arg[1], "setversion") == 0) { + if (!target || !target->IsNPC()) { + c->Message(0, "Error: Need an NPC target."); + return; + } + + if(!sep->IsNumber(2)) { + c->Message(0, "setversion FAILED -- You must set a version number"); + return; + } + + int16 version = atoi(sep->arg[2]); + std::string query = StringFormat("UPDATE spawn2 SET version = %i " + "WHERE spawngroupID = '%i'", + version, c->GetTarget()->CastToNPC()->GetSp2()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(13, "Update failed! MySQL gave the following error:"); + c->Message(13, results.ErrorMessage().c_str()); + return; + } + + c->LogSQL(query.c_str()); + c->Message(0, "Version change to %i was successful from SpawnGroupID %i", version, c->GetTarget()->CastToNPC()->GetSp2()); + c->GetTarget()->Depop(false); + + return; + } + + if (strcasecmp(sep->arg[1], "testload") == 0 && atoi(sep->arg[2])!=0) { + database.LoadSpawnGroupsByID(atoi(sep->arg[2]),&zone->spawn_group_list); + c->Message(0, "Group %i loaded successfully!", atoi(sep->arg[2])); + return; + } + + c->Message(0, "Error: #advnpcspawn: Invalid command."); + c->Message(0, "Usage: #advnpcspawn [maketype|makegroup|addgroupentry|addgroupspawn|setversion]"); + c->Message(0, "Usage: #advnpcspawn [removegroupspawn|movespawn|editrespawn|editgroupbox|cleargroupbox]"); } void command_aggrozone(Client *c, const Seperator *sep) { @@ -9390,28 +9166,15 @@ void command_netstats(Client *c, const Seperator *sep) void command_object(Client *c, const Seperator *sep) { if (!c) - { return; // Crash Suppressant: No client. How did we get here? - } // Save it here. We sometimes have need to refer to it in multiple places. const char* usage_string = "Usage: #object List|Add|Edit|Move|Rotate|Save|Copy|Delete|Undo"; - if ((!sep) || (sep->argnum == 0)) - { - // Crash Suppressant: Shouldn't be able to get here, either, but fail gracefully if we do. + if ((!sep) || (sep->argnum == 0)) { c->Message(0, usage_string); - return; - } - - char errbuf[MYSQL_ERRMSG_SIZE]; - char query[512]; - uint32 col; - MYSQL_RES *result; - MYSQL_ROW row; - int iObjectsFound = 0; - int len; + } Object* o = nullptr; Object_Struct od; @@ -9428,1361 +9191,1045 @@ void command_object(Client *c, const Seperator *sep) bool bNewObject = false; - errbuf[0] = '\0'; - float x2; float y2; // Temporary object type for static objects to allow manipulation // NOTE: Zone::LoadZoneObjects() currently loads this as an uint8, so max value is 255! - static const uint32 TempStaticType = 255; + static const uint32 staticType = 255; // Case insensitive commands (List == list == LIST) strlwr(sep->arg[1]); - // Protip: We only really care about the first letter. You can abbreviate Delete to just D if desired. - switch (sep->arg[1][0]) - { - case 'l': // List Objects - // Insufficient or invalid args - if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) - { - c->Message(0, "Usage: #object List All|(radius)"); - - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') - { - radius = 0; // List All - } - else if ((radius = atoi(sep->arg[2])) <= 0) - { - radius = 500; // Invalid radius. Default to 500 units. - } - - if (radius == 0) - { - c->Message(0, "Objects within this zone:"); - } - else - { - c->Message(0, "Objects within %u units of your current location:", radius); - } - - if (radius) - { - len = snprintf(query, sizeof(query), - "SELECT id, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (zoneid=%u)" - " AND (version=%u)" - " AND (xpos BETWEEN %.1f AND %.1f)" - " AND (ypos BETWEEN %.1f AND %.1f)" - " AND (zpos BETWEEN %.1f AND %.1f)" - " ORDER BY id", - zone->GetZoneID(), - zone->GetInstanceVersion(), - c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. - c->GetX() + radius, // Much less processing power used this way. - c->GetY() - radius, - c->GetY() + radius, - c->GetZ() - radius, - c->GetZ() + radius); - } - else - { - len = snprintf(query, sizeof(query), - "SELECT id, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (zoneid=%u)" - " AND (version=%u)" - " ORDER BY id", - zone->GetZoneID(), - zone->GetInstanceVersion()); - } - - if (database.RunQuery(query, len, errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) - { - col = 0; - id = atoi(row[col++]); - od.x = atof(row[col++]); - od.y = atof(row[col++]); - od.z = atof(row[col++]); - od.heading = atof(row[col++]); - itemid = atoi(row[col++]); - strn0cpy(od.object_name, row[col++], sizeof(od.object_name)); - od.object_name[sizeof(od.object_name) - 1] = '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) - - od.object_type = atoi(row[col++]); - icon = atoi(row[col++]); - od.unknown008 = atoi(row[col++]); - od.unknown010 = atoi(row[col++]); - od.unknown020 = atoi(row[col++]); - - switch (od.object_type) - { - case 0: // Static Object - case TempStaticType: // Static Object unlocked for changes - if (od.unknown008 == 0) // Unknown08 field is optional Size parameter for static objects - { - od.unknown008 = 100; // Static object default Size is 100% - } - - c->Message(0, - "- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, size %u, solidtype %u, incline %u", - (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, od.heading, od.object_name, od.unknown008, od.unknown010, od.unknown020); - break; - case OT_DROPPEDITEM: // Ground Spawn - c->Message(0, - "- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, model %s, icon %u", - id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon); - break; - default: // All others == Tradeskill Objects - c->Message(0, - "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, type %u, icon %u", - id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon); - break; - } - - iObjectsFound++; - } - - mysql_free_result(result); - } - - c->Message(0, "%u object%s found", iObjectsFound, (iObjectsFound == 1) ? "" : "s"); - break; - case 'a': // Add Object - // Insufficient or invalid arguments - if ((sep->argnum < 3) || ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) - { - c->Message(0, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] [SolidType] [Incline]"); - c->Message(0, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); - c->Message(0, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), 1 (Sometimes Non-Solid)"); - - return; - } - - if (sep->argnum > 3) - { - // Model name in arg3? - if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) - { - // Nope, user must have specified ObjectID. Extract it. - id = atoi(sep->arg[2]); - - col = 1; // Bump all other arguments one to the right. Model is in arg4. - } - else - { - // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 - id = 0; - col = 0; - } - } - else - { - // Nope, only 3 args. Object ID must be omitted and arg3 must be model. - id = 0; - col = 0; - } - - memset(&od, 0, sizeof(od)); - - od.object_type = atoi(sep->arg[2 + col]); - - switch (od.object_type) - { - case 0: // Static Object - if ((sep->argnum - col) > 3) - { - od.unknown008 = atoi(sep->arg[4 + col]); // Size specified - - if ((sep->argnum - col) > 4) - { - od.unknown010 = atoi(sep->arg[5 + col]); // SolidType specified - - if ((sep->argnum - col) > 5) - { - od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified - } - } - } - break; - case 1: // Ground Spawn - c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); - - return; - break; - default: // Everything else == Tradeskill Object - icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; - - if (icon == 0) - { - c->Message(0, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); - - return; - } - break; - } - - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - (c->GetSize() * 0.625f); - od.heading = c->GetHeading() * 2.0f; // GetHeading() is half of actual. Compensate by doubling. - - if (id) - { - // ID specified. Verify that it doesn't already exist. - - len = snprintf(query, sizeof(query), "SELECT COUNT(*) FROM object WHERE ID=%u", id); - - // Already in database? - if (database.RunQuery(query, len, errbuf, &result)) - { - if ((row = mysql_fetch_row(result)) != nullptr) - { - if (atoi(row[0]) > 0) - { - // Yep, in database already. - - id = 0; - } - } - - mysql_free_result(result); - } - - if (id) - { - // Not in database. Already spawned, just not saved? - if (entity_list.FindObject(id)) - { - // Yep, already spawned. - - id = 0; - } - } - - if (id == 0) - { - c->Message(0, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); - - return; - } - } - - // Verify no other objects already in this spot (accidental double-click of Hotkey?) - len = snprintf(query, sizeof(query), - "SELECT COUNT(*) FROM object " - "WHERE (zoneid=%u) " - "AND (version=%u) " - "AND (posx BETWEEN %.1f AND %.1f) " - "AND (posy BETWEEN %.1f AND %.1f) " - "AND (posz BETWEEN %.1f AND %.1f)", - zone->GetZoneID(), - zone->GetInstanceVersion(), - od.x - 0.2f, od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. - od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. - od.z - 0.2f, od.z + 0.2f); // It's pretty forgiving, though, allowing for close-proximity objects - - iObjectsFound = 0; - if (database.RunQuery(query, len, errbuf, &result)) - { - if ((row = mysql_fetch_row(result)) != nullptr) - { - iObjectsFound = atoi(row[0]); // Number of nearby objects from database - } - - mysql_free_result(result); - } - - if (iObjectsFound == 0) - { - // No objects found in database too close. How about spawned but not yet saved? - if (entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) - { - iObjectsFound++; - } - } - - if (iObjectsFound) - { - c->Message(0, "ERROR: Object already at this location."); - - return; - } - - // Strip any single quotes from objectname (SQL injection FTL!) - strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); - - len = strlen(od.object_name); - for (col = 0; col < (uint32)len; col++) - { - if (od.object_name[col] == '\'') - { - // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! - memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); - - len--; - col--; - } - } - - strupr(od.object_name); // Model names are always upper-case. - - if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) - { - c->Message(0, "ERROR: Model name must start with a letter."); - - return; - } - - if (id == 0) - { - // No ID specified. Get a best-guess next number from the database - - // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No biggie. - - strn0cpy(query, "SELECT MAX(id) FROM object", sizeof(query)); - - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - id = atoi(row[0]); - } - - mysql_free_result(result); - } - - id++; - } - - // Make sure not to overwrite already-spawned objects that haven't been saved yet. - while (o = entity_list.FindObject(id)) - { - id++; - } - - if (od.object_type == 0) // Static object - { - od.object_type = TempStaticType; // Temporary. We'll make it 0 when we Save - } - - od.zone_id = zone->GetZoneID(); - od.zone_instance = zone->GetInstanceVersion(); - - o = new Object(id, od.object_type, icon, od, nullptr); - - // Add to our zone entity list and spawn immediately for all clients - entity_list.AddObject(o, true); - - // Bump player back to avoid getting stuck inside new object - - // GetHeading() returns half of the actual heading, for some reason, so we'll double it here for computation - x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2); - - c->Message(0, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use '#object Save' to save to database when satisfied with placement.", id, od.x, od.y, od.z, od.heading); - - if (od.object_type == TempStaticType) // Temporary Static Object - { - c->Message(0, "- Note: Static Object will act like a tradeskill container and will not reflect size, solidtype, or incline values until you commit with '#object Save', after which it will be unchangeable until you use '#object Edit' and zone back in."); - } - break; - case 'e': // Edit - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) - { - c->Message(0, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); - c->Message(0, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); - c->Message(0, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); - - return; - } - - o = entity_list.FindObject(id); - - // Object already available in-zone? - if (o) - { - // Yep, looks like we can make real-time changes. - if (sep->argnum < 4) - { - // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue - - c->Message(0, "Note: Object %u already unlocked and ready for changes", id); - - return; - } - } - else - { - // Object not found in-zone in a modifiable form. Check for valid matching circumstances. - - len = snprintf(query, sizeof(query), "SELECT zoneid, version, type FROM object WHERE id=%u", id); - - iObjectsFound = 0; - if (database.RunQuery(query, len, errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - iObjectsFound++; - } - - mysql_free_result(result); - } - - // Object ID not found? - if (iObjectsFound == 0) - { - c->Message(0, "ERROR: Object %u not found", id); - - return; - } - - // Object not in this zone? - if (od.zone_id != zone->GetZoneID()) - { - c->Message(0, "ERROR: Object %u not in this zone.", id); - - return; - } - - // Object not in this instance? - if (od.zone_instance != zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Object %u not part of this instance version.", id); - - return; - } - - switch (od.object_type) - { - case 0: // Static object needing unlocking + if (strcasecmp(sep->arg[1], "list") == 0) { + // Insufficient or invalid args + if ((sep->argnum < 2) || (sep->arg[2][0] < '0') || ((sep->arg[2][0] > '9') && ((sep->arg[2][0] & 0xDF) != 'A'))) { + c->Message(0, "Usage: #object List All|(radius)"); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') + radius = 0; // List All + else if ((radius = atoi(sep->arg[2])) <= 0) + radius = 500; // Invalid radius. Default to 500 units. + + if (radius == 0) + c->Message(0, "Objects within this zone:"); + else + c->Message(0, "Objects within %u units of your current location:", radius); + + std::string query; + if (radius) + query = StringFormat("SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "AND (xpos BETWEEN %.1f AND %.1f) " + "AND (ypos BETWEEN %.1f AND %.1f) " + "AND (zpos BETWEEN %.1f AND %.1f) " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion(), + c->GetX() - radius, // Yes, we're actually using a bounding box instead of a radius. + c->GetX() + radius, // Much less processing power used this way. + c->GetY() - radius, + c->GetY() + radius, + c->GetZ() - radius, + c->GetZ() + radius); + else + query = StringFormat("SELECT id, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u AND version = %u " + "ORDER BY id", + zone->GetZoneID(), zone->GetInstanceVersion()); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Error in objects query"); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + id = atoi(row[0]); + od.x = atof(row[1]); + od.y = atof(row[2]); + od.z = atof(row[3]); + od.heading = atof(row[4]); + itemid = atoi(row[5]); + strn0cpy(od.object_name, row[6], sizeof(od.object_name)); + od.object_name[sizeof(od.object_name) - 1] = '\0'; // Required if strlen(row[col++]) exactly == sizeof(object_name) + + od.object_type = atoi(row[7]); + icon = atoi(row[8]); + od.unknown008 = atoi(row[9]); + od.unknown010 = atoi(row[10]); + od.unknown020 = atoi(row[11]); + + switch (od.object_type) + { + case 0: // Static Object + case staticType: // Static Object unlocked for changes + if (od.unknown008 == 0) // Unknown08 field is optional Size parameter for static objects + od.unknown008 = 100; // Static object default Size is 100% + + c->Message(0,"- STATIC Object (%s): id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, size %u, solidtype %u, incline %u", (od.object_type == 0) ? "locked" : "unlocked", id, od.x, od.y, od.z, od.heading, od.object_name, od.unknown008, od.unknown010, od.unknown020); + break; + + case OT_DROPPEDITEM: // Ground Spawn + c->Message(0,"- TEMPORARY Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, itemid %u, model %s, icon %u",id, od.x, od.y, od.z, od.heading, itemid, od.object_name, icon); + break; + + default: // All others == Tradeskill Objects + c->Message(0, "- TRADESKILL Object: id %u, x %.1f, y %.1f, z %.1f, h %.1f, model %s, type %u, icon %u",id, od.x, od.y, od.z, od.heading, od.object_name, od.object_type, icon); + break; + } + } + + c->Message(0, "%u object%s found", results.RowCount(), (results.RowCount() == 1)? "": "s"); + return; + } + + if (strcasecmp(sep->arg[1], "add") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || ((sep->arg[3][0] == '\0') && (sep->arg[4][0] < '0') && (sep->arg[4][0] > '9'))) { + c->Message(0, "Usage: (Static Object): #object Add [ObjectID] 0 Model [SizePercent] [SolidType] [Incline]"); + c->Message(0, "Usage: (Tradeskill Object): #object Add [ObjectID] TypeNum Model Icon"); + c->Message(0, "- Notes: Model must start with a letter, max length 16. SolidTypes = 0 (Solid), 1 (Sometimes Non-Solid)"); + return; + } + + int col; + + if (sep->argnum > 3) { // Model name in arg3? + if ((sep->arg[3][0] <= '9') && (sep->arg[3][0] >= '0')) { + // Nope, user must have specified ObjectID. Extract it. + id = atoi(sep->arg[2]); + col = 1; // Bump all other arguments one to the right. Model is in arg4. + } + else { + // Yep, arg3 is non-numeric, ObjectID must be omitted and model must be arg3 + id = 0; + col = 0; + } + } + else { + // Nope, only 3 args. Object ID must be omitted and arg3 must be model. + id = 0; + col = 0; + } + + memset(&od, 0, sizeof(od)); + + od.object_type = atoi(sep->arg[2 + col]); + + switch (od.object_type) { + case 0: // Static Object + if ((sep->argnum - col) > 3) { + od.unknown008 = atoi(sep->arg[4 + col]); // Size specified + + if ((sep->argnum - col) > 4) { + od.unknown010 = atoi(sep->arg[5 + col]); // SolidType specified + + if ((sep->argnum - col) > 5) + od.unknown020 = atoi(sep->arg[6 + col]); // Incline specified + } + } + break; + + case 1: // Ground Spawn + c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); + return; + + default: // Everything else == Tradeskill Object + icon = ((sep->argnum - col) > 3) ? atoi(sep->arg[4 + col]) : 0; + + if (icon == 0) { + c->Message(0, "ERROR: Required property 'Icon' not specified for Tradeskill Object"); + return; + } + + break; + } + + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - (c->GetSize() * 0.625f); + od.heading = c->GetHeading() * 2.0f; // GetHeading() is half of actual. Compensate by doubling. + + std::string query; + if (id) { + // ID specified. Verify that it doesn't already exist. + query = StringFormat("SELECT COUNT(*) FROM object WHERE ID = %u", id); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + if (atoi(row[0]) > 0) // Yep, in database already. + id = 0; + } + + // Not in database. Already spawned, just not saved? + // Yep, already spawned. + if (id && entity_list.FindObject(id)) + id = 0; + + if (id == 0) { + c->Message(0, "ERROR: An object already exists with the id %u", atoi(sep->arg[2])); + return; + } + } + + int objectsFound = 0; + // Verify no other objects already in this spot (accidental double-click of Hotkey?) + query = StringFormat("SELECT COUNT(*) FROM object WHERE zoneid = %u " + "AND version=%u AND (posx BETWEEN %.1f AND %.1f) " + "AND (posy BETWEEN %.1f AND %.1f) " + "AND (posz BETWEEN %.1f AND %.1f)", + zone->GetZoneID(), zone->GetInstanceVersion(), + od.x - 0.2f, od.x + 0.2f, // Yes, we're actually using a bounding box instead of a radius. + od.y - 0.2f, od.y + 0.2f, // Much less processing power used this way. + od.z - 0.2f, od.z + 0.2f); // It's pretty forgiving, though, allowing for close-proximity objects + + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + objectsFound = atoi(row[0]); // Number of nearby objects from database + } + + // No objects found in database too close. How about spawned but not yet saved? + if (objectsFound == 0 && entity_list.FindNearbyObject(od.x, od.y, od.z, 0.2f)) + objectsFound = 1; + + if (objectsFound) { + c->Message(0, "ERROR: Object already at this location."); + return; + } + + // Strip any single quotes from objectname (SQL injection FTL!) + strn0cpy(od.object_name, sep->arg[3 + col], sizeof(od.object_name)); + + uint32 len = strlen(od.object_name); + for (col = 0; col < (uint32)len; col++) { + if (od.object_name[col] != '\'') + continue; + + // Uh oh, 1337 h4x0r monkeying around! Strip that apostrophe! + memcpy(&od.object_name[col], &od.object_name[col + 1], len - col); + len--; + col--; + } + + strupr(od.object_name); // Model names are always upper-case. + + if ((od.object_name[0] < 'A') || (od.object_name[0] > 'Z')) { + c->Message(0, "ERROR: Model name must start with a letter."); + return; + } + + if (id == 0) { + // No ID specified. Get a best-guess next number from the database + // If there's a problem retrieving an ID from the database, it'll end up being object # 1. No biggie. + + query = "SELECT MAX(id) FROM object"; + results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + id = atoi(row[0]); + } + + id++; + } + + // Make sure not to overwrite already-spawned objects that haven't been saved yet. + while (o = entity_list.FindObject(id)) + id++; + + // Static object + if (od.object_type == 0) + od.object_type = staticType; // Temporary. We'll make it 0 when we Save + + od.zone_id = zone->GetZoneID(); + od.zone_instance = zone->GetInstanceVersion(); + + o = new Object(id, od.object_type, icon, od, nullptr); + + // Add to our zone entity list and spawn immediately for all clients + entity_list.AddObject(o, true); + + // Bump player back to avoid getting stuck inside new object + + // GetHeading() returns half of the actual heading, for some reason, so we'll double it here for computation + x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2); + + c->Message(0, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use '#object Save' to save to database when satisfied with placement.", id, od.x, od.y, od.z, od.heading); + + // Temporary Static Object + if (od.object_type == staticType) + c->Message(0, "- Note: Static Object will act like a tradeskill container and will not reflect size, solidtype, or incline values until you commit with '#object Save', after which it will be unchangeable until you use '#object Edit' and zone back in."); + + return; + } + + if (strcasecmp(sep->arg[1], "edit") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) < 1)) { + c->Message(0, "Usage: #object Edit (ObjectID) [PropertyName] [NewValue]"); + c->Message(0, "- Static Object (Type 0) Properties: model, type, size, solidtype, incline"); + c->Message(0, "- Tradeskill Object (Type 2+) Properties: model, type, icon"); + + return; + } + + o = entity_list.FindObject(id); + + // Object already available in-zone? + if (o) { + // Yep, looks like we can make real-time changes. + if (sep->argnum < 4) { + // Or not. '#object Edit (ObjectID)' called without PropertyName and NewValue + c->Message(0, "Note: Object %u already unlocked and ready for changes", id); + return; + } + } + else { + // Object not found in-zone in a modifiable form. Check for valid matching circumstances. + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + uint32 objectsFound = 1; + + // Object not in this zone? + if (od.zone_id != zone->GetZoneID()) { + c->Message(0, "ERROR: Object %u not in this zone.", id); + return; + } + + // Object not in this instance? + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Object %u not part of this instance version.", id); + return; + } + + switch (od.object_type) + { + case 0: // Static object needing unlocking // Convert to tradeskill object temporarily for changes - - len = snprintf(query, sizeof(query), "UPDATE object SET type=%u WHERE id=%u", TempStaticType, id); - - database.RunQuery(query, len); - - c->Message(0, "Static Object %u unlocked for editing. You must zone out and back in to make your changes, then commit them with '#object Save'.", id); - - if (sep->argnum >= 4) - { - c->Message(0, "NOTE: The change you specified has not been applied, since the static object had not been unlocked for editing yet."); - } - - return; - break; - case OT_DROPPEDITEM: - c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); - - return; - break; - case TempStaticType: - c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in for your client to refresh its object table before you can make changes to it.", id); - - return; - break; - default: - // Unknown error preventing us from seeing the object in the zone. - - c->Message(0, "ERROR: Unknown problem attempting to manipulate object %u", id); - - return; - break; - } - } - - // If we're here, we have a manipulable object ready for changes. - - strlwr(sep->arg[3]); // Case insensitive PropertyName - strupr(sep->arg[4]); // In case it's model name, which should always be upper-case - - // Read current object info for reference - icon = o->GetIcon(); - o->GetObjectData(&od); - - // We'll be a little more picky with property names, to prevent errors. Check against the whole word. - switch (sep->arg[3][0]) - { - case 'm': - if (strcmp(sep->arg[3], "model") == 0) - { - if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) - { - c->Message(0, "ERROR: Model names must begin with a letter."); - - return; - } - - strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); - - o->SetObjectData(&od); - - c->Message(0, "Object %u now being rendered with model '%s'", id, od.object_name); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - case 't': - if (strcmp(sep->arg[3], "type") == 0) - { - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid type number"); - - return; - } - - od.object_type = atoi(sep->arg[4]); - - switch (od.object_type) - { - case 0: - // Convert Static Object to temporary changeable type - od.object_type = TempStaticType; - c->Message(0, "Note: Static Object will still act like tradeskill object and will not reflect size, solidtype, or incline settings until committed to the database with '#object Save', after which it will be unchangeable until it is unlocked again with '#object Edit'."); - break; - case OT_DROPPEDITEM: - c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); - - return; - break; - default: - c->Message(0, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); - break; - } - - o->SetType(od.object_type); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - case 's': - if (strcmp(sep->arg[3], "size") == 0) - { - if (od.object_type != TempStaticType) - { - c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Size property", id); - - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid size specified. Please enter a number."); - - return; - } - - od.unknown008 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - if (od.unknown008 == 0) // 0 == unspecified == 100% - { - od.unknown008 = 100; - } - - c->Message(0, "Static Object %u set to %u%% size. Size will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown008); - } - else if (strcmp(sep->arg[3], "solidtype") == 0) - { - if (od.object_type != TempStaticType) - { - c->Message(0, "ERROR: Object %u is not a Static Object and does not support the SolidType property", id); - - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid solidtype specified. Please enter a number."); - - return; - } - - od.unknown010 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(0, "Static Object %u set to SolidType %u. Change will take effect when you commit to the database with '#object Save'. Support for this property is on a per-model basis, mostly seen in smaller objects such as chests and tables.", id, od.unknown010); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - case 'i': - if (strcmp(sep->arg[3], "icon") == 0) - { - if ((od.object_type < 2) || (od.object_type == TempStaticType)) - { - c->Message(0, "ERROR: Object %u is not a Tradeskill Object and does not support the Icon property", id); - - return; - } - - if ((icon = atoi(sep->arg[4])) == 0) - { - c->Message(0, "ERROR: Invalid Icon specified. Please enter an icon number."); - - return; - } - - o->SetIcon(icon); - - c->Message(0, "Tradeskill Object %u icon set to %u", id, icon); - } - else if (strcmp(sep->arg[3], "incline") == 0) - { - if (od.object_type != TempStaticType) - { - c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Incline property", id); - - return; - } - - if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) - { - c->Message(0, "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512."); - - return; - } - - od.unknown020 = atoi(sep->arg[4]); - o->SetObjectData(&od); - - c->Message(0, "Static Object %u set to %u incline. Incline will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown020); - } - else - { - id = 0; // Setting ID to 0 will signify invalid input - } - break; - default: - id = 0; // Setting ID to 0 will signify invalid input - break; - } - - if (id == 0) - { - c->Message(0, "ERROR: Unrecognized property name: %s", sep->arg[3]); - - return; - } - - // Repop object to have it reflect the change. - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - break; - case 'm': // Move - if ((sep->argnum < 2) || // Not enough arguments - ((id = atoi(sep->arg[2])) == 0) || // ID not specified - (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && - ((sep->arg[3][0] & 0xDF) != 'T') && - (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) // Location argument not specified correctly - { - c->Message(0, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); - - return; - } - - if (!(o = entity_list.FindObject(id))) - { - len = snprintf(query, sizeof(query), "SELECT zoneid, version, type FROM object WHERE id=%u", id); - - if ((!database.RunQuery(query, len, errbuf, &result)) || ((row = mysql_fetch_row(result)) == 0)) - { - if (result) - { - mysql_free_result(result); - } - - c->Message(0, "ERROR: Object %u not found", id); - - return; - } - - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - mysql_free_result(result); - - if (od.zone_id != zone->GetZoneID()) - { - c->Message(0, "ERROR: Object %u is not in this zone", id); - - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Object %u is not in this instance version", id); - - return; - } - - switch (od.object_type) - { - case 0: - c->Message(0, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' then zone out and back in to move it.", id); - - return; - break; - case TempStaticType: - c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in before your client sees the change and will allow you to move it.", id); - - return; - break; - case 1: - c->Message(0, "ERROR: Object %u is a temporary spawned object and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); - - return; - break; - default: - c->Message(0, "ERROR: Object %u not located in zone.", id); - - return; - break; - } - } - - if ((sep->arg[3][0] & 0xDF) == 'T') // Move To Me - { - od.x = c->GetX(); - od.y = c->GetY(); - od.z = c->GetZ() - (c->GetSize() * 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. - - o->SetHeading(c->GetHeading() * 2.0f); // Compensate for GetHeading() returning half of actual - - // Bump player back to avoid getting stuck inside object - - // GetHeading() returns half of the actual heading, for some reason - x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); - c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2.0f); - } - else // Move to x, y, z [h] - { - od.x = atof(sep->arg[3]); - if (sep->argnum > 3) - { - od.y = atof(sep->arg[4]); - } - else - { - o->GetLocation(nullptr, &od.y, nullptr); - } - - if (sep->argnum > 4) - { - od.z = atof(sep->arg[5]); - } - else - { - o->GetLocation(nullptr, nullptr, &od.z); - } - - if (sep->argnum > 5) - { - o->SetHeading(atof(sep->arg[6])); - } - } - - o->SetLocation(od.x, od.y, od.z); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - break; - case 'r': // Rotate - // Insufficient or invalid arguments - if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) - { - c->Message(0, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); - - return; - } - - if ((o = entity_list.FindObject(id)) == nullptr) - { - c->Message(0, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with '#object Edit' for editing.", id); - - return; - } - - o->SetHeading(atof(sep->arg[3])); - - // Despawn and respawn object to reflect change - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - app = new EQApplicationPacket(); - o->CreateSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - break; - case 's': // Save - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) - { - c->Message(0, "Usage: #object Save (ObjectID)"); - - return; - } - - o = entity_list.FindObject(id); - - sprintf(query, "SELECT zoneid, version, type FROM object WHERE id=%u", id); - - od.zone_id = 0; - od.zone_instance = 0; - od.object_type = 0; - - // If this ID isn't in the database yet, it's a new object - bNewObject = true; - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - od.zone_id = atoi(row[0]); - od.zone_instance = atoi(row[1]); - od.object_type = atoi(row[2]); - - // ID already in database. Not a new object. - bNewObject = false; - } - - mysql_free_result(result); - } - - if (!o) - { - // Object not found in zone. Can't save an object we can't see. - - if (bNewObject) - { - c->Message(0, "ERROR: Object %u not found", id); - - return; - } - - if (od.zone_id != zone->GetZoneID()) - { - c->Message(0, "ERROR: Wrong Object ID. %u is not part of this zone.", id); - - return; - } - - if (od.zone_instance != zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); - - return; - } - - if (od.object_type == 0) - { - c->Message(0, "ERROR: Static Object %u has already been committed. Use '#object Edit %u' and zone out and back in to make changes.", id, id); - - return; - } - - if (od.object_type == 1) - { - c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); - - return; - } - - c->Message(0, "ERROR: Object %u not found.", id); - - return; - } - - if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) - { - // Oops! Another GM already saved an object with our id from another zone. - // We'll have to get a new one. - - id = 0; - } - - if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) - { - // Oops! Another GM already saved an object with our id from another instance. - // We'll have to get a new one. - - id = 0; - } - - // If we're asking for a new ID, it's a new object. - bNewObject |= (id == 0); - - o->GetObjectData(&od); - od.object_type = o->GetType(); - icon = o->GetIcon(); - - // We're committing to the database now. Return temporary object type to actual. - if (od.object_type == TempStaticType) - { - od.object_type = 0; - } - - if (bNewObject) - { - if (id == 0) - { - len = snprintf(query, sizeof(query), - "INSERT INTO object (zoneid, version, xpos, ypos, zpos, heading, objectname, type, icon, unknown08, unknown10, unknown20)" - " VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - zone->GetZoneID(), zone->GetInstanceVersion(), - od.x, od.y, od.z, od.heading, - od.object_name, od.object_type, icon, - od.unknown008, od.unknown010, od.unknown020); - } - else - { - len = snprintf(query, sizeof(query), - "INSERT INTO object (id, zoneid, version, xpos, ypos, zpos, heading, objectname, type, icon, unknown08, unknown10, unknown20)" - " VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", - id, zone->GetZoneID(), zone->GetInstanceVersion(), - od.x, od.y, od.z, od.heading, - od.object_name, od.object_type, icon, - od.unknown008, od.unknown010, od.unknown020); - } - } - else - { - len = snprintf(query, sizeof(query), - "UPDATE object SET " - " zoneid=%u, version=%u," - " xpos=%.1f, ypos=%.1f, zpos=%.1f, heading=%.1f," - " objectname='%s', type=%u, icon=%u," - " unknown08=%u, unknown10=%u, unknown20=%u" - " WHERE ID=%u", - zone->GetZoneID(), zone->GetInstanceVersion(), - od.x, od.y, od.z, od.heading, - od.object_name, od.object_type, icon, - od.unknown008, od.unknown010, od.unknown020, - id); - } - - if (!database.RunQuery(query, len, errbuf, 0, &col, &newid)) - { - col = 0; - } - - if (col == 0) - { - if (errbuf[0] == '\0') - { - // No change made, but no error message given - c->Message(0, "Database Error: Could not save change to Object %u", id); - } - else - { - c->Message(0, "Database Error: %s", errbuf); - } - - return; - } - else - { - if (bNewObject) - { - if (newid == id) - { - c->Message(0, "Saved new Object %u to database", id); - } - else - { - c->Message(0, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); - id = newid; - } - } - else - { - c->Message(0, "Saved changes to Object %u", id); - - newid = id; - } - } - - if (od.object_type == 0) - { - // Static Object - Respawn as nonfunctional door - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - safe_delete(app); - - entity_list.RemoveObject(o->GetID()); - - memset(&door, 0, sizeof(door)); - - strn0cpy(door.zone_name, zone->GetShortName(), sizeof(door.zone_name)); - - door.db_id = 1000000000 + id; // Out of range of normal use for doors.id - door.door_id = -1; // Client doesn't care if these are all the same door_id - door.pos_x = od.x; // xpos - door.pos_y = od.y; // ypos - door.pos_z = od.z; // zpos - door.heading = od.heading; // heading - - strn0cpy(door.door_name, od.object_name, sizeof(door.door_name)); // objectname - - // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. - len = strlen(door.door_name); - if ((len > 9) && (memcmp(&door.door_name[len - 9], "_ACTORDEF", 10) == 0)) - { - door.door_name[len - 9] = '\0'; - } - - memcpy(door.dest_zone, "NONE", 5); - - if ((door.size = od.unknown008) == 0) // unknown08 = optional size percentage - { - door.size = 100; - } - - switch (door.opentype = od.unknown010) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) - { - case 0: - door.opentype = 31; - break; - case 1: - door.opentype = 9; - break; - } - - door.incline = od.unknown020; // unknown20 = optional incline value - door.client_version_mask = 0xFFFFFFFF; - - doors = new Doors(&door); - entity_list.AddDoor(doors); - - app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); - ds = (Door_Struct*)app->pBuffer; - - memset(ds, 0, sizeof(Door_Struct)); - memcpy(ds->name, door.door_name, 32); - ds->xPos = door.pos_x; - ds->yPos = door.pos_y; - ds->zPos = door.pos_z; - ds->heading = door.heading; - ds->incline = door.incline; - ds->size = door.size; - ds->doorId = door.door_id; - ds->opentype = door.opentype; - ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() - ds->unknown0052[11] = 1; - - entity_list.QueueClients(0, app); - safe_delete(app); - - c->Message(0, "NOTE: Object %u is now a static object, and is unchangeable. To make future changes, use '#object Edit' to convert it to a changeable form, then zone out and back in.", id); - } - break; - case 'c': // Copy - // Insufficient or invalid arguments - if ((sep->argnum < 3) || (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) - { - c->Message(0, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); - c->Message(0, "- Note: Only objects saved in the database can be copied to another instance."); - - return; - } - - od.zone_instance = atoi(sep->arg[3]); - - if (od.zone_instance == zone->GetInstanceVersion()) - { - c->Message(0, "ERROR: Source and destination instance versions are the same."); - - return; - } - - if ((sep->arg[2][0] & 0xDF) == 'A') - { - // Copy All - - len = snprintf(query, sizeof(query), - "INSERT INTO object (zoneid, version, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20)" - " SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (zoneid=%u) AND (version=%u)", - od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); - - if (database.RunQuery(query, len, errbuf, 0, &col)) - { - c->Message(0, "Copied %u object%s into instance version %u", col, (col == 1) ? "" : "s", od.zone_instance); - } - else - { - if (errbuf[0] == '\0') - { - c->Message(0, "Database Error: No objects were copied into instance version %u", od.zone_instance); - } - else - { - c->Message(0, "Database Error: %s", errbuf); - } - } - } - else - { - // Copy ObjectID - id = atoi(sep->arg[2]); - - len = snprintf(query, sizeof(query), - "INSERT INTO object (zoneid, version, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20)" - " SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object" - " WHERE (id=%u) AND (zoneid=%u) AND (version=%u)", - od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); - - if ((database.RunQuery(query, len, errbuf, 0, &col)) && (col > 0)) - { - c->Message(0, "Copied Object %u into instance version %u", id, od.zone_instance); - } - else - { - // Couldn't copy the object. - - if (errbuf[0] == '\0') - { - // No database error returned. See if we can figure out why. - - len = snprintf(query, sizeof(query), "SELECT zoneid, version FROM object WHERE id=%u", id); - - if (database.RunQuery(query, len, errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - // Wrong ZoneID? - if (atoi(row[0]) != zone->GetZoneID()) - { - mysql_free_result(result); - - c->Message(0, "ERROR: Object %u is not part of this zone.", id); - - return; - } - - // Wrong Instance Version? - if (atoi(row[1]) != zone->GetInstanceVersion()) - { - mysql_free_result(result); - - c->Message(0, "ERROR: Object %u is not part of this instance version.", id); - - return; - } - - // Well, NO clue at this point. Just let 'em know something screwed up. - mysql_free_result(result); - - c->Message(0, "ERROR: Unknown database error copying Object %u to instance version %u", id, od.zone_instance); - - return; - } - - mysql_free_result(result); - } - - // Typo? - c->Message(0, "ERROR: Object %u not found", id); - } - else - { - c->Message(0, "Database Error: %s", errbuf); - } - } - } - break; - case 'd': // Delete - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) - { - c->Message(0, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and cannot be undone!"); - - return; - } - - o = entity_list.FindObject(id); - - if (o) - { - // Object found in zone. - - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(nullptr, app); - - entity_list.RemoveObject(o->GetID()); - - // Verifying ZoneID and Version in case someone else ended up adding an object with our ID - // from a different zone/version. Don't want to delete someone else's work. - sprintf(query, "DELETE FROM object WHERE (id=%u) AND (zoneid=%u) AND (version=%u) LIMIT 1", id, zone->GetZoneID(), zone->GetInstanceVersion()); - database.RunQuery(query, strlen(query)); - - c->Message(0, "Object %u deleted", id); - } - else - { - // Object not found in zone. - - sprintf(query, "SELECT type FROM object WHERE (id=%u) AND (zoneid=%u) AND (version=%u) LIMIT 1", id, zone->GetZoneID(), zone->GetInstanceVersion()); - - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - if (row = mysql_fetch_row(result)) - { - switch (atoi(row[0])) - { - case 0: // Static Object - mysql_free_result(result); - - sprintf(query, "DELETE FROM object WHERE (id=%u) AND (zoneid=%u) AND (version=%u) LIMIT 1", id, zone->GetZoneID(), zone->GetInstanceVersion()); - database.RunQuery(query, strlen(query)); - - c->Message(0, "Object %u deleted. NOTE: This static object will remain for anyone currently in the zone until they next zone out and in.", id); - - mysql_free_result(result); - - return; - break; - case 1: // Temporary Spawn - c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); - - mysql_free_result(result); - - return; - break; - } - } - - mysql_free_result(result); - } - - c->Message(0, "ERROR: Object %u not found in this zone or instance!", id); - } - break; - case 'u': // Undo - Reload object from database to undo changes - // Insufficient or invalid arguments - if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) - { - c->Message(0, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any changes you have made"); - - return; - } - - o = entity_list.FindObject(id); - - if (!o) - { - c->Message(0, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", id); - - return; - } - - if (o->GetType() == OT_DROPPEDITEM) - { - c->Message(0, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); - - return; - } - - // Despawn current item for reloading from database - app = new EQApplicationPacket(); - o->CreateDeSpawnPacket(app); - entity_list.QueueClients(0, app); - entity_list.RemoveObject(o->GetID()); - safe_delete(app); - - len = snprintf(query, sizeof(query), - "SELECT xpos, ypos, zpos, heading, objectname, type, icon, unknown08, unknown10, unknown20" - " FROM object WHERE id=%u", id); - - if ((!database.RunQuery(query, len, errbuf, &result)) || ((row = mysql_fetch_row(result)) == 0)) - { - if (result) - { - mysql_free_result(result); - } - - if (errbuf[0] == '\0') - { - c->Message(0, "Database Error: Could not retrieve Object %u from object table.", id); - - return; - } - - c->Message(0, "Database Error: %s", errbuf); - - return; - } - - memset(&od, 0, sizeof(od)); - - col = 0; - od.x = atof(row[col++]); - od.y = atof(row[col++]); - od.z = atof(row[col++]); - od.heading = atof(row[col++]); - strn0cpy(od.object_name, row[col++], sizeof(od.object_name)); - od.object_type = atoi(row[col++]); - icon = atoi(row[col++]); - od.unknown008 = atoi(row[col++]); - od.unknown010 = atoi(row[col++]); - od.unknown020 = atoi(row[col++]); - - if (od.object_type == 0) - { - od.object_type = TempStaticType; - } - - o = new Object(id, od.object_type, icon, od, nullptr); - entity_list.AddObject(o, true); - - c->Message(0, "Object %u reloaded from database.", id); - break; - default: // Unrecognized command - c->Message(0, usage_string); - break; - } + query = StringFormat("UPDATE object SET type = %u WHERE id = %u", staticType, id); + + database.QueryDatabase(query); + + c->Message(0, "Static Object %u unlocked for editing. You must zone out and back in to make your changes, then commit them with '#object Save'.", id); + if (sep->argnum >= 4) + c->Message(0, "NOTE: The change you specified has not been applied, since the static object had not been unlocked for editing yet."); + return; + + case OT_DROPPEDITEM: + c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); + return; + + case staticType: + c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in for your client to refresh its object table before you can make changes to it.", id); + return; + + default: + // Unknown error preventing us from seeing the object in the zone. + c->Message(0, "ERROR: Unknown problem attempting to manipulate object %u", id); + return; + } + } + + // If we're here, we have a manipulable object ready for changes. + strlwr(sep->arg[3]); // Case insensitive PropertyName + strupr(sep->arg[4]); // In case it's model name, which should always be upper-case + + // Read current object info for reference + icon = o->GetIcon(); + o->GetObjectData(&od); + + // We'll be a little more picky with property names, to prevent errors. Check against the whole word. + if (strcmp(sep->arg[3], "model") == 0) { + + if ((sep->arg[4][0] < 'A') || (sep->arg[4][0] > 'Z')) { + c->Message(0, "ERROR: Model names must begin with a letter."); + return; + } + + strn0cpy(od.object_name, sep->arg[4], sizeof(od.object_name)); + + o->SetObjectData(&od); + + c->Message(0, "Object %u now being rendered with model '%s'", id, od.object_name); + } + else if (strcmp(sep->arg[3], "type") == 0) { + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid type number"); + return; + } + + od.object_type = atoi(sep->arg[4]); + + switch (od.object_type) { + case 0: + // Convert Static Object to temporary changeable type + od.object_type = staticType; + c->Message(0, "Note: Static Object will still act like tradeskill object and will not reflect size, solidtype, or incline settings until committed to the database with '#object Save', after which it will be unchangeable until it is unlocked again with '#object Edit'."); + break; + + case OT_DROPPEDITEM: + c->Message(0, "ERROR: Object Type 1 is used for temporarily spawned ground spawns and dropped items, which are not supported with #object. See the 'ground_spawns' table in the database."); + return; + + default: + c->Message(0, "Object %u changed to Tradeskill Object Type %u", id, od.object_type); + break; + } + + o->SetType(od.object_type); + } + else if (strcmp(sep->arg[3], "size") == 0) { + if (od.object_type != staticType) { + c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Size property", id); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid size specified. Please enter a number."); + return; + } + + od.unknown008 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + if (od.unknown008 == 0) // 0 == unspecified == 100% + od.unknown008 = 100; + + c->Message(0, "Static Object %u set to %u%% size. Size will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown008); + } + else if (strcmp(sep->arg[3], "solidtype") == 0) { + + if (od.object_type != staticType) { + c->Message(0, "ERROR: Object %u is not a Static Object and does not support the SolidType property", id); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid solidtype specified. Please enter a number."); + return; + } + + od.unknown010 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message(0, "Static Object %u set to SolidType %u. Change will take effect when you commit to the database with '#object Save'. Support for this property is on a per-model basis, mostly seen in smaller objects such as chests and tables.", id, od.unknown010); + } + else if (strcmp(sep->arg[3], "icon") == 0) { + + if ((od.object_type < 2) || (od.object_type == staticType)) { + c->Message(0, "ERROR: Object %u is not a Tradeskill Object and does not support the Icon property", id); + return; + } + + if ((icon = atoi(sep->arg[4])) == 0) { + c->Message(0, "ERROR: Invalid Icon specified. Please enter an icon number."); + return; + } + + o->SetIcon(icon); + c->Message(0, "Tradeskill Object %u icon set to %u", id, icon); + } + else if (strcmp(sep->arg[3], "incline") == 0) { + if (od.object_type != staticType) { + c->Message(0, "ERROR: Object %u is not a Static Object and does not support the Incline property", id); + return; + } + + if ((sep->arg[4][0] < '0') || (sep->arg[4][0] > '9')) { + c->Message(0, "ERROR: Invalid Incline specified. Please enter a number. Normal range is 0-512."); + return; + } + + od.unknown020 = atoi(sep->arg[4]); + o->SetObjectData(&od); + + c->Message(0, "Static Object %u set to %u incline. Incline will take effect when you commit to the database with '#object Save', after which the object will be unchangeable until you unlock it again with '#object Edit' and zone out and back in.", id, od.unknown020); + } + else { + c->Message(0, "ERROR: Unrecognized property name: %s", sep->arg[3]); + return; + } + + // Repop object to have it reflect the change. + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "move") == 0) { + + if ((sep->argnum < 2) || // Not enough arguments + ((id = atoi(sep->arg[2])) == 0) || // ID not specified + (((sep->arg[3][0] < '0') || (sep->arg[3][0] > '9')) && + ((sep->arg[3][0] & 0xDF) != 'T') && + (sep->arg[3][0] != '-') && (sep->arg[3][0] != '.'))) { // Location argument not specified correctly + c->Message(0, "Usage: #object Move (ObjectID) ToMe|(x y z [h])"); + return; + } + + if (!(o = entity_list.FindObject(id))) { + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + + if (od.zone_id != zone->GetZoneID()) { + c->Message(0, "ERROR: Object %u is not in this zone", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Object %u is not in this instance version", id); + return; + } + + switch (od.object_type) { + case 0: + c->Message(0, "ERROR: Object %u is not yet unlocked for editing. Use '#object Edit' then zone out and back in to move it.", id); + return; + + case staticType: + c->Message(0, "ERROR: Object %u has been unlocked for editing, but you must zone out and back in before your client sees the change and will allow you to move it.", id); + return; + + case 1: + c->Message(0, "ERROR: Object %u is a temporary spawned object and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); + return; + + default: + c->Message(0, "ERROR: Object %u not located in zone.", id); + return; + } + } + + // Move To Me + if ((sep->arg[3][0] & 0xDF) == 'T') { + od.x = c->GetX(); + od.y = c->GetY(); + od.z = c->GetZ() - (c->GetSize() * 0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size. + + o->SetHeading(c->GetHeading() * 2.0f); // Compensate for GetHeading() returning half of actual + + // Bump player back to avoid getting stuck inside object + + // GetHeading() returns half of the actual heading, for some reason + x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f); + c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2.0f); + } // Move to x, y, z [h] + else { + od.x = atof(sep->arg[3]); + if (sep->argnum > 3) + od.y = atof(sep->arg[4]); + else + o->GetLocation(nullptr, &od.y, nullptr); + + if (sep->argnum > 4) + od.z = atof(sep->arg[5]); + else + o->GetLocation(nullptr, nullptr, &od.z); + + if (sep->argnum > 5) + o->SetHeading(atof(sep->arg[6])); + } + + o->SetLocation(od.x, od.y, od.z); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "rotate") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(0, "Usage: #object Rotate (ObjectID) (Heading, 0-512)"); + return; + } + + if ((o = entity_list.FindObject(id)) == nullptr) { + c->Message(0, "ERROR: Object %u not found in zone, or is a static object not yet unlocked with '#object Edit' for editing.", id); + return; + } + + o->SetHeading(atof(sep->arg[3])); + + // Despawn and respawn object to reflect change + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + app = new EQApplicationPacket(); + o->CreateSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + return; + } + + if (strcasecmp(sep->arg[1], "save") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(0, "Usage: #object Save (ObjectID)"); + return; + } + + o = entity_list.FindObject(id); + + od.zone_id = 0; + od.zone_instance = 0; + od.object_type = 0; + + // If this ID isn't in the database yet, it's a new object + bNewObject = true; + std::string query = StringFormat("SELECT zoneid, version, type FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() != 0) { + auto row = results.begin(); + od.zone_id = atoi(row[0]); + od.zone_instance = atoi(row[1]); + od.object_type = atoi(row[2]); + + // ID already in database. Not a new object. + bNewObject = false; + } + + if (!o) { + // Object not found in zone. Can't save an object we can't see. + + if (bNewObject) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + if (od.zone_id != zone->GetZoneID()) { + c->Message(0, "ERROR: Wrong Object ID. %u is not part of this zone.", id); + return; + } + + if (od.zone_instance != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Wrong Object ID. %u is not part of this instance version.", id); + return; + } + + if (od.object_type == 0) { + c->Message(0, "ERROR: Static Object %u has already been committed. Use '#object Edit %u' and zone out and back in to make changes.", id, id); + return; + } + + if (od.object_type == 1) { + c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); + return; + } + + c->Message(0, "ERROR: Object %u not found.", id); + return; + } + + // Oops! Another GM already saved an object with our id from another zone. + // We'll have to get a new one. + if ((od.zone_id > 0) && (od.zone_id != zone->GetZoneID())) + id = 0; + + // Oops! Another GM already saved an object with our id from another instance. + // We'll have to get a new one. + if ((id > 0) && (od.zone_instance != zone->GetInstanceVersion())) + id = 0; + + // If we're asking for a new ID, it's a new object. + bNewObject |= (id == 0); + + o->GetObjectData(&od); + od.object_type = o->GetType(); + icon = o->GetIcon(); + + // We're committing to the database now. Return temporary object type to actual. + if (od.object_type == staticType) + od.object_type = 0; + + if (!bNewObject) + query = StringFormat("UPDATE object SET zoneid = %u, version = %u, " + "xpos = %.1f, ypos=%.1f, zpos=%.1f, heading=%.1f, " + "objectname = '%s', type = %u, icon = %u, " + "unknown08 = %u, unknown10 = %u, unknown20 = %u " + "WHERE ID = %u", + zone->GetZoneID(), zone->GetInstanceVersion(), + od.x, od.y, od.z, od.heading, + od.object_name, od.object_type, icon, + od.unknown008, od.unknown010, od.unknown020, id); + else if (id == 0) + query = StringFormat("INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + zone->GetZoneID(), zone->GetInstanceVersion(), + od.x, od.y, od.z, od.heading, + od.object_name, od.object_type, icon, + od.unknown008, od.unknown010, od.unknown020); + else + query = StringFormat("INSERT INTO object " + "(id, zoneid, version, xpos, ypos, zpos, heading, objectname, " + "type, icon, unknown08, unknown10, unknown20) " + "VALUES (%u, %u, %u, %.1f, %.1f, %.1f, %.1f, '%s', %u, %u, %u, %u, %u)", + id, zone->GetZoneID(), zone->GetInstanceVersion(), + od.x, od.y, od.z, od.heading, + od.object_name, od.object_type, icon, + od.unknown008, od.unknown010, od.unknown020); + + results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + if (results.RowsAffected() == 0) { + // No change made, but no error message given + c->Message(0, "Database Error: Could not save change to Object %u", id); + return; + } + + if (bNewObject) { + if (newid == results.LastInsertedID()) { + c->Message(0, "Saved new Object %u to database", id); + return; + } + + c->Message(0, "Saved Object. NOTE: Database returned a new ID number for object: %u", newid); + id = newid; + return; + } + + c->Message(0, "Saved changes to Object %u", id); + newid = id; + + if (od.object_type == 0) { + // Static Object - Respawn as nonfunctional door + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + safe_delete(app); + + entity_list.RemoveObject(o->GetID()); + + memset(&door, 0, sizeof(door)); + + strn0cpy(door.zone_name, zone->GetShortName(), sizeof(door.zone_name)); + + door.db_id = 1000000000 + id; // Out of range of normal use for doors.id + door.door_id = -1; // Client doesn't care if these are all the same door_id + door.pos_x = od.x; // xpos + door.pos_y = od.y; // ypos + door.pos_z = od.z; // zpos + door.heading = od.heading; // heading + + strn0cpy(door.door_name, od.object_name, sizeof(door.door_name)); // objectname + + // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. + uint32 len = strlen(door.door_name); + if ((len > 9) && (memcmp(&door.door_name[len - 9], "_ACTORDEF", 10) == 0)) + door.door_name[len - 9] = '\0'; + + memcpy(door.dest_zone, "NONE", 5); + + if ((door.size = od.unknown008) == 0) // unknown08 = optional size percentage + door.size = 100; + + switch (door.opentype = od.unknown010) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) + { + case 0: + door.opentype = 31; + break; + + case 1: + door.opentype = 9; + break; + + } + + door.incline = od.unknown020; // unknown20 = optional incline value + door.client_version_mask = 0xFFFFFFFF; + + doors = new Doors(&door); + entity_list.AddDoor(doors); + + app = new EQApplicationPacket(OP_SpawnDoor, sizeof(Door_Struct)); + ds = (Door_Struct*)app->pBuffer; + + memset(ds, 0, sizeof(Door_Struct)); + memcpy(ds->name, door.door_name, 32); + ds->xPos = door.pos_x; + ds->yPos = door.pos_y; + ds->zPos = door.pos_z; + ds->heading = door.heading; + ds->incline = door.incline; + ds->size = door.size; + ds->doorId = door.door_id; + ds->opentype = door.opentype; + ds->unknown0052[9] = 1; // *ptr-1 and *ptr-3 from EntityList::MakeDoorSpawnPacket() + ds->unknown0052[11] = 1; + + entity_list.QueueClients(0, app); + safe_delete(app); + + c->Message(0, "NOTE: Object %u is now a static object, and is unchangeable. To make future changes, use '#object Edit' to convert it to a changeable form, then zone out and back in.", id); + } + return; + } + + if (strcasecmp(sep->arg[1], "copy") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 3) || (((sep->arg[2][0] & 0xDF) != 'A') && ((sep->arg[2][0] < '0') || (sep->arg[2][0] > '9')))) { + c->Message(0, "Usage: #object Copy All|(ObjectID) (InstanceVersion)"); + c->Message(0, "- Note: Only objects saved in the database can be copied to another instance."); + return; + } + + od.zone_instance = atoi(sep->arg[3]); + + if (od.zone_instance == zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Source and destination instance versions are the same."); + return; + } + + if ((sep->arg[2][0] & 0xDF) == 'A') { + // Copy All + + std::string query = StringFormat("INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE zoneid = %u) AND version = %u", + od.zone_instance, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + c->Message(0, "Copied %u object%s into instance version %u", results.RowCount(), (results.RowCount() == 1) ? "" : "s", od.zone_instance); + return; + } + + id = atoi(sep->arg[2]); + + std::string query = StringFormat("INSERT INTO object " + "(zoneid, version, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20) " + "SELECT zoneid, %u, xpos, ypos, zpos, heading, itemid, " + "objectname, type, icon, unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u AND zoneid = %u AND version = %u", + od.zone_instance, id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowsAffected() > 0) { + c->Message(0, "Copied Object %u into instance version %u", id, od.zone_instance); + return; + } + + // Couldn't copy the object. + + if (results.ErrorMessage().c_str() != '\0') { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + // No database error returned. See if we can figure out why. + + query = StringFormat("SELECT zoneid, version FROM object WHERE id = %u", id); + results = database.QueryDatabase(query); + if (!results.Success()) + return; + + if (results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found", id); + return; + } + + auto row = results.begin(); + // Wrong ZoneID? + if (atoi(row[0]) != zone->GetZoneID()) { + c->Message(0, "ERROR: Object %u is not part of this zone.", id); + return; + } + + // Wrong Instance Version? + if (atoi(row[1]) != zone->GetInstanceVersion()) { + c->Message(0, "ERROR: Object %u is not part of this instance version.", id); + return; + } + + // Well, NO clue at this point. Just let 'em know something screwed up. + c->Message(0, "ERROR: Unknown database error copying Object %u to instance version %u", id, od.zone_instance); + return; + } + + if (strcasecmp(sep->arg[1], "delete") == 0) { + + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) <= 0)) { + c->Message(0, "Usage: #object Delete (ObjectID) -- NOTE: Object deletions are permanent and cannot be undone!"); + return; + } + + o = entity_list.FindObject(id); + + if (o) { + // Object found in zone. + + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(nullptr, app); + + entity_list.RemoveObject(o->GetID()); + + // Verifying ZoneID and Version in case someone else ended up adding an object with our ID + // from a different zone/version. Don't want to delete someone else's work. + std::string query = StringFormat("DELETE FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + + c->Message(0, "Object %u deleted", id); + return; + } + + + // Object not found in zone. + std::string query = StringFormat("SELECT type FROM object " + "WHERE id = %u AND zoneid = %u " + "AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + if (results.RowCount() == 0) { + c->Message(0, "ERROR: Object %u not found in this zone or instance!", id); + return; + } + + auto row = results.begin(); + + switch (atoi(row[0])) { + case 0: // Static Object + query = StringFormat("DELETE FROM object WHERE id = %u " + "AND zoneid = %u AND version = %u LIMIT 1", + id, zone->GetZoneID(), zone->GetInstanceVersion()); + results = database.QueryDatabase(query); + + c->Message(0, "Object %u deleted. NOTE: This static object will remain for anyone currently in the zone until they next zone out and in.", id); + return; + + case 1: // Temporary Spawn + c->Message(0, "ERROR: Object %u is a temporarily spawned ground spawn or dropped item, which is not supported with #object. See the 'ground_spawns' table in the database.", id); + return; + + } + + return; + } + + if (strcasecmp(sep->arg[1], "undo") == 0) { + // Insufficient or invalid arguments + if ((sep->argnum < 2) || ((id = atoi(sep->arg[2])) == 0)) { + c->Message(0, "Usage: #object Undo (ObjectID) -- Reload object from database, undoing any changes you have made"); + return; + } + + o = entity_list.FindObject(id); + + if (!o) { + c->Message(0, "ERROR: Object %u not found in zone in a manipulable form. No changes to undo.", id); + return; + } + + if (o->GetType() == OT_DROPPEDITEM) { + c->Message(0, "ERROR: Object %u is a temporary spawned item and cannot be manipulated with #object. See the 'ground_spawns' table in the database.", id); + return; + } + + // Despawn current item for reloading from database + app = new EQApplicationPacket(); + o->CreateDeSpawnPacket(app); + entity_list.QueueClients(0, app); + entity_list.RemoveObject(o->GetID()); + safe_delete(app); + + std::string query = StringFormat("SELECT xpos, ypos, zpos, " + "heading, objectname, type, icon, " + "unknown08, unknown10, unknown20 " + "FROM object WHERE id = %u", id); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) { + c->Message(0, "Database Error: %s", results.ErrorMessage().c_str()); + return; + } + + memset(&od, 0, sizeof(od)); + + auto row = results.begin(); + + od.x = atof(row[0]); + od.y = atof(row[1]); + od.z = atof(row[2]); + od.heading = atof(row[3]); + strn0cpy(od.object_name, row[4], sizeof(od.object_name)); + od.object_type = atoi(row[5]); + icon = atoi(row[6]); + od.unknown008 = atoi(row[7]); + od.unknown010 = atoi(row[8]); + od.unknown020 = atoi(row[9]); + + if (od.object_type == 0) + od.object_type = staticType; + + o = new Object(id, od.object_type, icon, od, nullptr); + entity_list.AddObject(o, true); + + c->Message(0, "Object %u reloaded from database.", id); + return; + } + + c->Message(0, usage_string); } void command_showspellslist(Client *c, const Seperator *sep) @@ -11270,7 +10717,9 @@ void command_mysql(Client *c, const Seperator *sep) { if(!sep->arg[1][0] || !sep->arg[2][0]) { c->Message(0, "Usage: #mysql query \"Query here\""); + return; } + if ( strcasecmp( sep->arg[1], "help" ) == 0 ) { c->Message(0, "MYSQL In-Game CLI Interface:"); c->Message(0, "Example: #mysql query \"Query goes here quoted\" -s -h"); @@ -11278,81 +10727,70 @@ void command_mysql(Client *c, const Seperator *sep) c->Message(0, "Example: #mysql query \"select * from table where name like \"#something#\""); c->Message(0, "-s - Spaces select entries apart"); c->Message(0, "-h - Colors every other select result"); + return; } + if ( strcasecmp( sep->arg[1], "query" ) == 0 ) { ///Parse switches here - int argnum = 3; bool Options = false, Optionh = false; bool Fail = false; + int argnum = 3; + bool optionS = false; + bool optionH = false; while(sep->arg[argnum] && strlen(sep->arg[argnum]) > 1){ switch(sep->arg[argnum][1]){ - case 's': Options = true; break; - case 'h': Optionh = true; break; - default: c->Message(15, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); Fail = true; + case 's': optionS = true; break; + case 'h': optionH = true; break; + default: + c->Message(15, "%s, there is no option '%c'", c->GetName(), sep->arg[argnum][1]); + return; } ++argnum; } - if(!Fail) { - char errbuf[MYSQL_ERRMSG_SIZE]; - int HText = 0; - MYSQL_RES *result; - std::stringstream MsgText; - std::string QueryText(sep->arg[2]); - //swap # for % so like queries can work - std::replace(QueryText.begin(), QueryText.end(), '#', '%'); + int highlightTextIndex = 0; + std::string query(sep->arg[2]); + //swap # for % so like queries can work + std::replace(query.begin(), query.end(), '#', '%'); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + c->Message(0, "Invalid query: '%s', '%s'", sep->arg[2], results.ErrorMessage().c_str()); + return; + } - if (database.RunQuery(QueryText.c_str(), QueryText.length(), errbuf, &result)) { - //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message - QueryText = sep->arg[2]; - int pos = QueryText.find('#'); - while(pos != std::string::npos) - { - QueryText.erase(pos,1); - QueryText.insert(pos, "%%"); - pos = QueryText.find('#'); - } + //Using sep->arg[2] again, replace # with %% so it doesn't screw up when sent through vsnprintf in Message + query = sep->arg[2]; + int pos = query.find('#'); + while(pos != std::string::npos) { + query.erase(pos,1); + query.insert(pos, "%%"); + pos = query.find('#'); + } + c->Message(15, "---Running query: '%s'", query.c_str()); - MsgText << "---Running query: '" << QueryText << "'"; - c->Message (15, MsgText.str().c_str()); - MsgText.str(""); + for (auto row = results.begin(); row != results.end(); ++row) { + std::stringstream lineText; + std::vector lineVec; + for(int i = 0; i < results.RowCount(); i++) { + //split lines that could overflow the buffer in Client::Message and get cut off + //This will crash MQ2 @ 4000 since their internal buffer is only 2048. + //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. + if(lineText.str().length() > 4000) { + lineVec.push_back(lineText.str()); + lineText.str(""); + } + lineText << results.FieldName(i) << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; + } - MYSQL_ROW row; - while ((row = mysql_fetch_row(result))) { + lineVec.push_back(lineText.str()); - MYSQL_FIELD *fields = mysql_fetch_fields(result); - unsigned int num_fields = mysql_num_fields(result); - std::stringstream LineText; - std::vector LineVec; - for(int i = 0; i < num_fields; i++) { - //split lines that could overflow the buffer in Client::Message and get cut off - //This will crash MQ2 @ 4000 since their internal buffer is only 2048. - //Reducing it to 2000 fixes that but splits more results from tables with a lot of columns. - if(LineText.str().length() > 4000) { - LineVec.push_back(LineText.str()); - LineText.str(""); - } - LineText << fields[i].name << ":" << "[" << (row[i] ? row[i] : "nullptr") << "] "; - } - LineVec.push_back(LineText.str()); + if(optionS) //This provides spacing for the space switch + c->Message(0, " "); + if(optionH) //This option will highlight every other row + highlightTextIndex = 1 - highlightTextIndex; - if(Options) { //This provides spacing for the space switch - c->Message(0, " "); - } - if(Optionh) { //This option will highlight every other row - HText = 1 - HText; - } - for(int lineNum = 0; lineNum < LineVec.size(); ++lineNum) - { - c->Message(HText, LineVec[lineNum].c_str()); - } - } - } - else { - MsgText << "Invalid query: ' " << sep->arg[2] << " ', ' " << errbuf << " '"; - c->Message(0, MsgText.str().c_str()); - MsgText.str(""); - } - } - } + for(int lineNum = 0; lineNum < lineVec.size(); ++lineNum) + c->Message(highlightTextIndex, lineVec[lineNum].c_str()); + } + } } void command_xtargets(Client *c, const Seperator *sep) diff --git a/zone/command.h b/zone/command.h index 4846dd09d..c02c127dc 100644 --- a/zone/command.h +++ b/zone/command.h @@ -125,7 +125,6 @@ void command_worldshutdown(Client *c, const Seperator *sep); void command_sendzonespawns(Client *c, const Seperator *sep); void command_zsave(Client *c, const Seperator *sep); void command_dbspawn2(Client *c, const Seperator *sep); -void command_copychar(Client *c, const Seperator *sep); void command_shutdown(Client *c, const Seperator *sep); void command_delacct(Client *c, const Seperator *sep); void command_setpass(Client *c, const Seperator *sep); @@ -151,7 +150,6 @@ void command_texture(Client *c, const Seperator *sep); void command_npctypespawn(Client *c, const Seperator *sep); void command_heal(Client *c, const Seperator *sep); void command_appearance(Client *c, const Seperator *sep); -void command_charbackup(Client *c, const Seperator *sep); void command_nukeitem(Client *c, const Seperator *sep); void command_peekinv(Client *c, const Seperator *sep); void command_findnpctype(Client *c, const Seperator *sep); @@ -217,7 +215,6 @@ void command_guild(Client *c, const Seperator *sep); bool helper_guild_edit(Client *c, uint32 dbid, uint32 eqid, uint8 rank, const char* what, const char* value); void command_zonestatus(Client *c, const Seperator *sep); void command_manaburn(Client *c, const Seperator *sep); -void command_viewmessage(Client *c, const Seperator *sep); void command_doanim(Client *c, const Seperator *sep); void command_randomfeatures(Client *c, const Seperator *sep); void command_face(Client *c, const Seperator *sep); diff --git a/zone/common.h b/zone/common.h index 1401af860..fb56b3f01 100644 --- a/zone/common.h +++ b/zone/common.h @@ -17,6 +17,10 @@ #define _NPCPET(x) (x && x->IsNPC() && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsNPC()) #define _BECOMENPCPET(x) (x && x->CastToMob()->GetOwner() && x->CastToMob()->GetOwner()->IsClient() && x->CastToMob()->GetOwner()->CastToClient()->IsBecomeNPC()) +#define USE_ITEM_SPELL_SLOT 10 +#define POTION_BELT_SPELL_SLOT 11 +#define DISCIPLINE_SPELL_SLOT 10 +#define ABILITY_SPELL_SLOT 9 //LOS Parameters: #define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from @@ -290,6 +294,7 @@ struct StatBonuses { int16 ResistFearChance; //i bool Fearless; //i bool IsFeared; //i + bool IsBlind; //i int16 StunResist; //i int16 MeleeSkillCheck; //i uint8 MeleeSkillCheckSkill; diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 7300db608..f028b4555 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -97,6 +97,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna for (unsigned int i=0; i < dbpcs->itemcount; i++) { tmp = new ServerLootItem_Struct; memcpy(tmp, &dbpcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); + tmp->equipSlot = CorpseToServerSlot(tmp->equipSlot); // temp hack until corpse blobs are removed itemlist.push_back(tmp); } @@ -147,6 +148,7 @@ Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charna for (unsigned int i=0; i < dbpc->itemcount; i++) { tmp = new ServerLootItem_Struct; memcpy(tmp, &dbpc->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); + tmp->equipSlot = CorpseToServerSlot(tmp->equipSlot); // temp hack until corpse blobs are removed itemlist.push_back(tmp); } @@ -600,6 +602,7 @@ bool Corpse::Save() { end = itemlist.end(); for(; cur != end; ++cur) { ServerLootItem_Struct* item = *cur; + item->equipSlot = ServerToCorpseSlot(item->equipSlot); // temp hack until corpse blobs are removed memcpy((char*) &dbpc->items[x++], (char*) item, sizeof(player_lootitem::ServerLootItem_Struct)); } @@ -971,8 +974,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a } RemoveCash(); - Save(); - client->Save(); + Save(); } outapp->priority = 6; @@ -2064,15 +2066,111 @@ void Corpse::LoadPlayerCorpseDecayTime(uint32 dbid){ } /* -uint32 Corpse::ServerToCorpseSlot(int16 server_slot) { - // reserved -} +** Corpse slot translations are needed until corpse database blobs are converted +** +** To account for the addition of MainPowerSource, MainGeneral9 and MainGeneral10 into +** the contiguous possessions slot enumeration, the following designations will be used: +** +** Designatiom Server Corpse Offset +** -------------------------------------------------- +** MainCharm 0 0 0 +** ... ... ... 0 +** MainWaist 20 20 0 +** MainPowerSource 21 9999 +9978 +** MainAmmo 22 21 -1 +** +** MainGeneral1 23 22 -1 +** ... ... ... -1 +** MainGeneral8 30 29 -1 +** MainGeneral9 31 9997 +9966 +** MainGeneral10 32 9998 +9966 +** +** MainCursor 33 30 -3 +** +** MainGeneral1_1 251 251 0 +** ... ... ... 0 +** MainGeneral8_10 330 330 0 +** MainGeneral9_1 331 341 +10 +** ... ... ... +10 +** MainGeneral10_10 350 360 +10 +** +** MainCursor_1 351 331 -20 +** ... ... ... -20 +** MainCursor_10 360 340 -20 +** +** (Not all slot designations are valid to all clients..see ##_constants.h files for valid slot enumerations) */ -/* -int16 Corpse::CorpseToServerSlot(uint32 corpse_slot) { - // reserved +int16 Corpse::ServerToCorpseSlot(int16 server_slot) +{ + return server_slot; // temporary return + + /* + switch (server_slot) + { + case MainPowerSource: + return 9999; + case MainGeneral9: + return 9997; + case MainGeneral10: + return 9998; + case MainCursor: + return 30; + case MainAmmo: + case MainGeneral1: + case MainGeneral2: + case MainGeneral3: + case MainGeneral4: + case MainGeneral5: + case MainGeneral6: + case MainGeneral7: + case MainGeneral8: + return server_slot - 1; + default: + if (server_slot >= EmuConstants::CURSOR_BAG_BEGIN && server_slot <= EmuConstants::CURSOR_BAG_END) + return server_slot - 20; + else if (server_slot >= EmuConstants::GENERAL_BAGS_END - 19 && server_slot <= EmuConstants::GENERAL_BAGS_END) + return server_slot + 10; + else + return server_slot; + } + */ +} + +int16 Corpse::CorpseToServerSlot(int16 corpse_slot) +{ + return corpse_slot; // temporary return + + /* + switch (corpse_slot) + { + case 9999: + return MainPowerSource; + case 9997: + return MainGeneral9; + case 9998: + return MainGeneral10; + case 30: + return MainCursor; + case 21: // old SLOT_AMMO + case 22: // old PERSONAL_BEGIN + case 23: + case 24: + case 25: + case 26: + case 27: + case 28: + case 29: // old PERSONAL_END + return corpse_slot + 1; + default: + if (corpse_slot >= 331 && corpse_slot <= 340) + return corpse_slot + 20; + else if (corpse_slot >= 341 && corpse_slot <= 360) + return corpse_slot - 10; + else + return corpse_slot; + } + */ } -*/ /* void Corpse::CastRezz(uint16 spellid, Mob* Caster){ diff --git a/zone/corpse.h b/zone/corpse.h index e76b3b4ce..2d1eecc98 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -112,8 +112,8 @@ public: inline int GetRezzExp() { return rezzexp; } // these are a temporary work-around until corpse inventory is removed from the database blob - //static uint32 ServerToCorpseSlot(int16 server_slot); // encode - //static int16 CorpseToServerSlot(uint32 corpse_slot); // decode + static int16 ServerToCorpseSlot(int16 server_slot); // encode + static int16 CorpseToServerSlot(int16 corpse_slot); // decode protected: std::list MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot); diff --git a/zone/effects.cpp b/zone/effects.cpp index a24a6b39b..7822d2e5e 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -56,7 +56,7 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value -= target->GetFcDamageAmtIncoming(this, spell_id)/spells[spell_id].buffduration; } - value += dmg*SpellFocusDMG/100; + value += dmg*GetSpellFocusDMG()/100; if (AI_HasSpellsEffects()){ int16 chance = 0; @@ -275,7 +275,7 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { //Scale all NPC spell healing via SetSpellFocusHeal(value) - value += value*SpellFocusHeal/100; + value += value*GetSpellFocusHeal()/100; if (target) { value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); @@ -606,6 +606,7 @@ bool Client::TrainDiscipline(uint32 itemid) { return(false); } else if(m_pp.disciplines.values[r] == 0) { m_pp.disciplines.values[r] = spell_id; + database.SaveCharacterDisc(this->CharacterID(), r, spell_id); SendDisciplineUpdate(); Message(0, "You have learned a new discipline!"); return(true); @@ -616,13 +617,9 @@ bool Client::TrainDiscipline(uint32 itemid) { } void Client::SendDisciplineUpdate() { - //this dosent seem to work right now - EQApplicationPacket app(OP_DisciplineUpdate, sizeof(Disciplines_Struct)); Disciplines_Struct *d = (Disciplines_Struct*)app.pBuffer; - //dunno why I dont just send the one from m_pp memcpy(d, &m_pp.disciplines, sizeof(m_pp.disciplines)); - QueuePacket(&app); } diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 5adbdedbd..6e337cc03 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -3402,6 +3402,41 @@ XS(XS__qs_player_event) XSRETURN_EMPTY; } +XS(XS__crosszonesetentityvariablebynpctypeid); +XS(XS__crosszonesetentityvariablebynpctypeid) +{ + dXSARGS; + + if (items != 3) + Perl_croak(aTHX_ "Usage: crosszonesetentityvariablebynpctypeid(npctype_id, id, m_var)"); + + if (items == 3) { + uint32 npctype_id = (uint32)SvIV(ST(0)); + const char *id = (const char *)SvPV_nolen(ST(1)); + const char *m_var = (const char *)SvPV_nolen(ST(2)); + quest_manager.CrossZoneSetEntityVariableByNPCTypeID(npctype_id, id, m_var); + } + + XSRETURN_EMPTY; +} + +XS(XS__crosszonesignalnpcbynpctypeid); +XS(XS__crosszonesignalnpcbynpctypeid) +{ + dXSARGS; + + if (items != 2) + Perl_croak(aTHX_ "Usage: crosszonesignalnpcbynpctypeid(npctype_id, data)"); + + if (items == 2) { + uint32 npctype_id = (uint32)SvIV(ST(0)); + uint32 data = (uint32)SvIV(ST(1)); + quest_manager.CrossZoneSignalNPCByNPCTypeID(npctype_id, data); + } + + XSRETURN_EMPTY; +} + /* This is the callback perl will look for to setup the quest package's XSUBs @@ -3624,7 +3659,9 @@ EXTERN_C XS(boot_quest) newXS(strcpy(buf, "disablerecipe"), XS__disablerecipe, file); newXS(strcpy(buf, "clear_npctype_cache"), XS__clear_npctype_cache, file); newXS(strcpy(buf, "qs_send_query"), XS__qs_send_query, file); - newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file); + newXS(strcpy(buf, "qs_player_event"), XS__qs_player_event, file); + newXS(strcpy(buf, "crosszonesetentityvariablebynpctypeid"), XS__crosszonesetentityvariablebynpctypeid, file); + newXS(strcpy(buf, "crosszonesignalnpcbynpctypeid"), XS__crosszonesignalnpcbynpctypeid, file); XSRETURN_YES; } diff --git a/zone/entity.cpp b/zone/entity.cpp index 7054ba8a1..0ce530419 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -40,7 +40,6 @@ #include "../common/spdat.h" #include "../common/features.h" #include "string_ids.h" -#include "../common/dbasync.h" #include "guild_mgr.h" #include "raids.h" #include "quest_parser_collection.h" @@ -57,7 +56,6 @@ extern WorldServer worldserver; extern NetConnection net; extern uint32 numclients; extern PetitionList petition_list; -extern DBAsync *dbasync; extern char errorname[32]; extern uint16 adverrornum; @@ -65,12 +63,11 @@ extern uint16 adverrornum; Entity::Entity() { id = 0; - pDBAsyncWorkID = 0; } Entity::~Entity() { - dbasync->CancelWork(pDBAsyncWorkID); + } Client *Entity::CastToClient() @@ -493,23 +490,36 @@ void EntityList::MobProcess() #endif auto it = mob_list.begin(); while (it != mob_list.end()) { - if (!it->second) { + uint16 id = it->first; + Mob *mob = it->second; + + size_t sz = mob_list.size(); + bool p_val = mob->Process(); + size_t a_sz = mob_list.size(); + + if(a_sz > sz) { + //increased size can potentially screw with iterators so reset it to current value + //if buckets are re-orderered we may skip a process here and there but since + //process happens so often it shouldn't matter much + it = mob_list.find(id); + ++it; + } else { ++it; - continue; } - if (!it->second->Process()) { - Mob *mob = it->second; - uint16 tempid = it->first; - ++it; // we don't erase here because the destructor will - if (mob->IsNPC()) { - entity_list.RemoveNPC(mob->CastToNPC()->GetID()); - } else if (mob->IsMerc()) { - entity_list.RemoveMerc(mob->CastToMerc()->GetID()); + + if(!p_val) { + if(mob->IsNPC()) { + entity_list.RemoveNPC(id); + } + else if(mob->IsMerc()) { + entity_list.RemoveMerc(id); #ifdef BOTS - } else if (mob->IsBot()) { - entity_list.RemoveBot(mob->CastToBot()->GetID()); + } + else if(mob->IsBot()) { + entity_list.RemoveBot(id); #endif - } else { + } + else { #ifdef _WINDOWS struct in_addr in; in.s_addr = mob->CastToClient()->GetIP(); @@ -517,20 +527,19 @@ void EntityList::MobProcess() #endif zone->StartShutdownTimer(); Group *g = GetGroupByMob(mob); - if (g) { + if(g) { LogFile->write(EQEMuLog::Error, "About to delete a client still in a group."); g->DelMember(mob); } Raid *r = entity_list.GetRaidByClient(mob->CastToClient()); - if (r) { + if(r) { LogFile->write(EQEMuLog::Error, "About to delete a client still in a raid."); r->MemberZoned(mob->CastToClient()); } - entity_list.RemoveClient(mob->GetID()); + entity_list.RemoveClient(id); } - entity_list.RemoveMob(tempid); - } else { - ++it; + + entity_list.RemoveMob(id); } } } @@ -1342,7 +1351,7 @@ void EntityList::RefreshClientXTargets(Client *c) } void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *app, - bool iSendToSender, Mob *SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits) + bool iSendToSender, Mob *SkipThisMob, bool ackreq, bool HoTT, uint32 ClientVersionBits, bool inspect_buffs) { auto it = client_list.begin(); while (it != client_list.end()) { @@ -1356,8 +1365,7 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap Mob *TargetsTarget = nullptr; - if (Target) - TargetsTarget = Target->GetTarget(); + TargetsTarget = Target->GetTarget(); bool Send = false; @@ -1369,11 +1377,30 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap Send = true; if (c != sender) { - if (Target == sender) - Send = true; - else if (HoTT) - if (TargetsTarget == sender) + if (Target == sender) { + if (inspect_buffs) { // if inspect_buffs is true we're sending a mob's buffs to those with the LAA + if (c->IsRaidGrouped()) { + Raid *raid = c->GetRaid(); + if (!raid) + continue; + uint32 gid = raid->GetGroup(c); + if (gid > 11 || raid->GroupCount(gid) < 3) + continue; + if (raid->GetLeadershipAA(groupAAInspectBuffs, gid)) + Send = true; + } else { + Group *group = c->GetGroup(); + if (!group || group->GroupCount() < 3) + continue; + if (group->GetLeadershipAA(groupAAInspectBuffs)) + Send = true; + } + } else { Send = true; + } + } else if (HoTT && TargetsTarget == sender) { + Send = true; + } } if (Send && (c->GetClientVersionBit() & ClientVersionBits)) @@ -4111,15 +4138,10 @@ void EntityList::UnMarkNPC(uint16 ID) // each group to remove the dead mobs entity ID from the groups list of NPCs marked via the // Group Leadership AA Mark NPC ability. // - auto it = client_list.begin(); - while (it != client_list.end()) { - if (it->second) { - Group *g = nullptr; - g = it->second->GetGroup(); - - if (g) - g->UnMarkNPC(ID); - } + auto it = group_list.begin(); + while (it != group_list.end()) { + if (*it) + (*it)->UnMarkNPC(ID); ++it; } } diff --git a/zone/entity.h b/zone/entity.h index cc302fe75..5776ed936 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -28,7 +28,6 @@ #include "zonedb.h" #include "zonedump.h" -#include "zonedbasync.h" #include "qglobals.h" class EQApplicationPacket; @@ -53,7 +52,7 @@ class Bot; class BotRaids; #endif -extern EntityList entity_list; +extern EntityList entity_list; class Entity { @@ -100,7 +99,6 @@ public: inline const uint16& GetID() const { return id; } virtual const char* GetName() { return ""; } - virtual void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { pDBAsyncWorkID = 0; } bool CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk=1); #ifdef BOTS @@ -299,7 +297,7 @@ public: void QueueClientsGuild(Mob* sender, const EQApplicationPacket* app, bool ignore_sender = false, uint32 guildeqid = 0); void QueueClientsGuildBankItemUpdate(const GuildBankItemUpdate_Struct *gbius, uint32 GuildID); void QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, Mob* SkipThisMob = 0, bool ackreq = true, - bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF); + bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF, bool inspect_buffs = false); void QueueClientsByXTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true); void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app); diff --git a/zone/exp.cpp b/zone/exp.cpp index 9415022ca..0e01a4c0e 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -51,14 +51,7 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level) void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { - /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items - This will stopgap players from items being returned if global_npc.pl has a catch all return_items - */ - struct timeval read_time; - char buffer[50]; - gettimeofday(&read_time, 0); - sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Stop_Return", buffer); + this->EVENT_ITEM_ScriptStopReturn(); uint32 add_exp = in_add_exp; @@ -140,30 +133,60 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { } } - if(IsLeadershipEXPOn() && ((conlevel == CON_BLUE) || (conlevel == CON_WHITE) || (conlevel == CON_YELLOW) || (conlevel == CON_RED))) { + if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) { add_exp = static_cast(static_cast(add_exp) * 0.8f); - if(GetGroup()) - { - if((m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())) - && (RuleI(Character, KillsPerGroupLeadershipAA) > 0)) - { - AddLeadershipEXP(GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA), 0); - Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); - } - else + if (GetGroup()) { + if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()) + && RuleI(Character, KillsPerGroupLeadershipAA) > 0) { + uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA); + Client *mentoree = GetGroup()->GetMentoree(); + if (GetGroup()->GetMentorPercent() && mentoree && + mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) { + uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f); + exp -= mentor_exp; + mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down + mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } + if (exp > 0) { // possible if you mentor 100% to the other client + AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it + Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } + } else { Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); - } - else - { - if((m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())) - && (RuleI(Character, KillsPerRaidLeadershipAA) > 0)) - { - AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA)); - Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP); } - else - Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS); + } else { + Raid *raid = GetRaid(); + // Raid leaders CAN NOT gain group AA XP, other group leaders can though! + if (raid->IsLeader(this)) { + if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel()) + && RuleI(Character, KillsPerRaidLeadershipAA) > 0) { + AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA)); + Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP); + } else { + Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS); + } + } else { + if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()) + && RuleI(Character, KillsPerGroupLeadershipAA) > 0) { + uint32 group_id = raid->GetGroup(this); + uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA); + Client *mentoree = raid->GetMentoree(group_id); + if (raid->GetMentorPercent(group_id) && mentoree && + mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) { + uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f); + exp -= mentor_exp; + mentoree->AddLeadershipEXP(mentor_exp, 0); + mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } + if (exp > 0) { + AddLeadershipEXP(exp, 0); + Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP); + } + } else { + Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS); + } + } } } diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 636422b3f..47fc53a96 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -31,12 +31,10 @@ #define snprintf _snprintf #endif - extern Zone* zone; #define FEAR_PATHING_DEBUG - //this is called whenever we are damaged to process possible fleeing void Mob::CheckFlee() { //if were allready fleeing, dont need to check more... @@ -55,7 +53,7 @@ void Mob::CheckFlee() { float ratio = GetHPRatio(); float fleeratio = GetSpecialAbility(FLEE_PERCENT); fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); - + if(ratio >= fleeratio) return; @@ -101,12 +99,13 @@ void Mob::CheckFlee() { } } - -void Mob::ProcessFlee() { +void Mob::ProcessFlee() +{ //Stop fleeing if effect is applied after they start to run. //When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee. - if(flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && !spellbonuses.IsFeared){ + if (flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && + !spellbonuses.IsFeared && !spellbonuses.IsBlind) { curfp = false; return; } @@ -114,40 +113,42 @@ void Mob::ProcessFlee() { //see if we are still dying, if so, do nothing float fleeratio = GetSpecialAbility(FLEE_PERCENT); fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); - if(GetHPRatio() < fleeratio) + if (GetHPRatio() < fleeratio) return; //we are not dying anymore... see what we do next flee_mode = false; - //see if we are legitimately feared now - if(!spellbonuses.IsFeared) { - //not feared... were done... + //see if we are legitimately feared or blind now + if (!spellbonuses.IsFeared && !spellbonuses.IsBlind) { + //not feared or blind... were done... curfp = false; return; } } -float Mob::GetFearSpeed() { - if(flee_mode) { +float Mob::GetFearSpeed() +{ + if (flee_mode) { //we know ratio < FLEE_HP_RATIO float speed = GetBaseRunspeed(); float ratio = GetHPRatio(); float multiplier = RuleR(Combat, FleeMultiplier); - if(GetSnaredAmount() > 40) + if (GetSnaredAmount() > 40) multiplier = multiplier / 6.0f; speed = speed * ratio * multiplier / 100; //NPC will eventually stop. Snares speeds this up. - if(speed < 0.09) + if (speed < 0.09) speed = 0.0001f; - - return(speed); + + return speed; } - return(GetRunspeed()); + // fear and blind use their normal run speed + return GetRunspeed(); } void Mob::CalculateNewFearpoint() @@ -209,17 +210,3 @@ void Mob::CalculateNewFearpoint() } } - - - - - - - - - - - - - - diff --git a/zone/groups.cpp b/zone/groups.cpp index c73b8f093..b9eda9836 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -46,6 +46,7 @@ Group::Group(uint32 gid) : GroupIDConsumer(gid) { leader = nullptr; + mentoree = nullptr; memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS); AssistTargetID = 0; TankTargetID = 0; @@ -81,6 +82,7 @@ Group::Group(Mob* leader) TankTargetID = 0; PullerTargetID = 0; memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); + mentoree = nullptr; uint32 i; for(i=0;iIsClient()) { // If Group Member is Client Client *c = members[i]->CastToClient(); //I could not get MoneyOnCorpse to work, so we use this - c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); + c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); c->Message(2, msg.c_str()); } } @@ -467,6 +469,11 @@ bool Group::UpdatePlayer(Mob* update){ return true; } } + + // mentoree isn't set, the name has a length and the name is ours! update the pointer + if (update->IsClient() && !mentoree && mentoree_name.length() && !mentoree_name.compare(update->GetName())) + mentoree = update->CastToClient(); + return false; } @@ -500,6 +507,9 @@ void Group::MemberZoned(Mob* removemob) { if(removemob->IsClient() && HasRole(removemob, RolePuller)) SetGroupPullerTarget(0); + + if (removemob->IsClient() && removemob == mentoree) + mentoree = nullptr; } bool Group::DelMemberOOZ(const char *Name) { @@ -528,6 +538,8 @@ bool Group::DelMemberOOZ(const char *Name) { } ClearAllNPCMarks(); } + if (Name == mentoree_name) + ClearGroupMentor(); return true; } } @@ -642,6 +654,9 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) UnDelegatePuller(oldmember->GetName()); } + if (oldmember->GetName() == mentoree_name) + ClearGroupMentor(); + if(oldmember->IsClient()) SendMarkedNPCsToMember(oldmember->CastToClient(), true); @@ -969,31 +984,28 @@ void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float } bool Group::LearnMembers() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT name FROM group_id WHERE groupid=%lu", (unsigned long)GetID()), - errbuf,&result)){ - safe_delete_array(query); - if(mysql_num_rows(result) < 1) { //could prolly be 2 - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), errbuf); - return(false); - } - int i = 0; - while((row = mysql_fetch_row(result))) { - if(!row[0]) - continue; - members[i] = nullptr; - strn0cpy(membername[i], row[0], 64); + std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %lu", (unsigned long)GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return false; - i++; - } - mysql_free_result(result); + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); + return false; + } + + int memberIndex = 0; + for(auto row = results.begin(); row != results.end(); ++row) { + if(!row[0]) + continue; + + members[memberIndex] = nullptr; + strn0cpy(membername[memberIndex], row[0], 64); + + memberIndex++; } - return(true); + return true; } void Group::VerifyGroup() { @@ -1077,7 +1089,7 @@ void Group::HealGroup(uint32 heal_amt, Mob* caster, int32 range) if (!range) range = 200; - + float distance; float range2 = range*range; @@ -1114,10 +1126,10 @@ void Group::BalanceHP(int32 penalty, int32 range, Mob* caster, int32 limit) return; if (!range) - range = 200; - + range = 200; + int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; - + float distance; float range2 = range*range; @@ -1164,16 +1176,16 @@ void Group::BalanceMana(int32 penalty, int32 range, Mob* caster, int32 limit) return; if (!range) - range = 200; + range = 200; float distance; float range2 = range*range; - + int manataken = 0, numMem = 0, manataken_tmp = 0; unsigned int gi = 0; for(; gi < MAX_GROUP_MEMBERS; gi++) { - if(members[gi] && (members[gi]->GetMaxMana() > 0)){ + if(members[gi] && (members[gi]->GetMaxMana() > 0)){ distance = caster->DistNoRoot(*members[gi]); if(distance <= range2){ @@ -1341,15 +1353,12 @@ void Group::DelegateMainTank(const char *NewMainTankName, uint8 toggle) } if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = nullptr; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='%s' WHERE gid=%i LIMIT 1", - MainTankName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main tank: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET maintank = '%s' WHERE gid = %i LIMIT 1", + MainTankName.c_str(), GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group main tank: %s\n", results.ErrorMessage().c_str()); } } @@ -1390,15 +1399,13 @@ void Group::DelegateMainAssist(const char *NewMainAssistName, uint8 toggle) } if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = nullptr; + std::string query = StringFormat("UPDATE group_leaders SET assist = '%s' WHERE gid = %i LIMIT 1", + MainAssistName.c_str(), GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group main assist: %s\n", results.ErrorMessage().c_str()); - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='%s' WHERE gid=%i LIMIT 1", - MainAssistName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main assist: %s\n", errbuff); - - safe_delete_array(Query); } } @@ -1439,15 +1446,13 @@ void Group::DelegatePuller(const char *NewPullerName, uint8 toggle) } if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = nullptr; + std::string query = StringFormat("UPDATE group_leaders SET puller = '%s' WHERE gid = %i LIMIT 1", + PullerName.c_str(), GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group main puller: %s\n", results.ErrorMessage().c_str()); - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='%s' WHERE gid=%i LIMIT 1", - PullerName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main puller: %s\n", errbuff); - - safe_delete_array(Query); } } @@ -1593,15 +1598,11 @@ void Group::UnDelegateMainTank(const char *OldMainTankName, uint8 toggle) // informing them of the change and update the group_leaders table. // if(OldMainTankName == MainTankName) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main tank: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET maintank = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group main tank: %s\n", results.ErrorMessage().c_str()); if(!toggle) { for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { @@ -1647,15 +1648,10 @@ void Group::UnDelegateMainAssist(const char *OldMainAssistName, uint8 toggle) safe_delete(outapp); - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main assist: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET assist = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group main assist: %s\n", results.ErrorMessage().c_str()); if(!toggle) { @@ -1679,15 +1675,11 @@ void Group::UnDelegatePuller(const char *OldPullerName, uint8 toggle) // informing them of the change and update the group_leaders table. // if(OldPullerName == PullerName) { - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main puller: %s\n", errbuff); - - safe_delete_array(Query); + std::string query = StringFormat("UPDATE group_leaders SET puller = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group main puller: %s\n", results.ErrorMessage().c_str()); if(!toggle) { for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { @@ -1759,6 +1751,31 @@ void Group::SetGroupPullerTarget(Mob *m) } } +void Group::SetGroupMentor(int percent, char *name) +{ + mentoree_name = name; + mentor_percent = percent; + Client *client = entity_list.GetClientByName(name); + + mentoree = client ? client : nullptr; + std::string query = StringFormat("UPDATE group_leaders SET mentoree = '%s', mentor_percent = %i WHERE gid = %i LIMIT 1", + mentoree_name.c_str(), mentor_percent, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group mentor: %s\n", results.ErrorMessage().c_str()); +} + +void Group::ClearGroupMentor() +{ + mentoree_name.clear(); + mentor_percent = 0; + mentoree = nullptr; + std::string query = StringFormat("UPDATE group_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group mentor: %s\n", results.ErrorMessage().c_str()); +} + void Group::NotifyAssistTarget(Client *c) { // Send a packet to the specified client notifying them of the group target selected by the Main Assist. @@ -1822,16 +1839,11 @@ void Group::DelegateMarkNPC(const char *NewNPCMarkerName) if(members[i] && members[i]->IsClient()) NotifyMarkNPC(members[i]->CastToClient()); - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='%s' WHERE gid=%i LIMIT 1", - NewNPCMarkerName, GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group mark npc: %s\n", errbuff); - - safe_delete_array(Query); - + std::string query = StringFormat("UPDATE group_leaders SET marknpc = '%s' WHERE gid = %i LIMIT 1", + NewNPCMarkerName, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set group mark npc: %s\n", results.ErrorMessage().c_str()); } void Group::NotifyMarkNPC(Client *c) @@ -1908,37 +1920,29 @@ void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName) NPCMarkerName.clear(); - char errbuff[MYSQL_ERRMSG_SIZE]; - char *Query = 0; + std::string query = StringFormat("UPDATE group_leaders SET marknpc = '' WHERE gid = %i LIMIT 1", GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear group marknpc: %s\n", results.ErrorMessage().c_str()); - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group marknpc: %s\n", errbuff); - - safe_delete_array(Query); } void Group::SaveGroupLeaderAA() { // Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table. // This is done so that group members not in the same zone as the Leader still have access to this information. + char *queryBuffer = new char[sizeof(GroupLeadershipAA_Struct) * 2 + 1]; + database.DoEscapeString(queryBuffer, (char*)&LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); - char *Query = new char[200 + sizeof(GroupLeadershipAA_Struct)*2]; + std::string query = "UPDATE group_leaders SET leadershipaa = '"; + query += queryBuffer; + query += StringFormat("' WHERE gid = %i LIMIT 1", GetID()); + safe_delete_array(queryBuffer); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); - char *End = Query; - - End += sprintf(End, "UPDATE group_leaders SET leadershipaa='"); - - End += database.DoEscapeString(End, (char*)&LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); - - End += sprintf(End,"' WHERE gid=%i LIMIT 1", GetID()); - - char errbuff[MYSQL_ERRMSG_SIZE]; - if (!database.RunQuery(Query, End - Query, errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", errbuff); - - safe_delete_array(Query); } void Group::UnMarkNPC(uint16 ID) diff --git a/zone/groups.h b/zone/groups.h index fe21ff3a4..250b86946 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -132,6 +132,11 @@ public: const char *GetClientNameByIndex(uint8 index); void UpdateXTargetMarkedNPC(uint32 Number, Mob *m); + void SetGroupMentor(int percent, char *name); + void ClearGroupMentor(); + inline int GetMentorPercent() { return mentor_percent; } + inline Client *GetMentoree() { return mentoree; } + Mob* members[MAX_GROUP_MEMBERS]; char membername[MAX_GROUP_MEMBERS][64]; uint8 MemberRoles[MAX_GROUP_MEMBERS]; @@ -151,6 +156,9 @@ private: uint16 PullerTargetID; uint16 MarkedNPCs[MAX_MARKED_NPCS]; + std::string mentoree_name; + Client *mentoree; + int mentor_percent; }; #endif diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index d21ae6bf6..bad060aef 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -24,59 +24,6 @@ #include "client.h" #include "entity.h" -/* - -CREATE TABLE guilds ( - id MEDIUMINT UNSIGNED NOT NULL, - name VARCHAR(32) NOT NULL, - leader int NOT NULL, - minstatus SMALLINT NOT NULL, - tribute INT UNSIGNED NOT NULL, - motd TEXT NOT NULL DEFAULT '', - PRIMARY KEY(id), - UNIQUE KEY(name), - UNIQUE KEY(leader) -); - -CREATE TABLE guild_ranks ( - guild_id MEDIUMINT UNSIGNED NOT NULL, - rank TINYINT UNSIGNED NOT NULL, - title VARCHAR(128) NOT NULL, - can_hear TINYINT UNSIGNED NOT NULL, - can_speak TINYINT UNSIGNED NOT NULL, - can_invite TINYINT UNSIGNED NOT NULL, - can_remove TINYINT UNSIGNED NOT NULL, - can_promote TINYINT UNSIGNED NOT NULL, - can_demote TINYINT UNSIGNED NOT NULL, - can_motd TINYINT UNSIGNED NOT NULL, - can_warpeace TINYINT UNSIGNED NOT NULL, - PRIMARY KEY(guild_id,rank) -); - -# guild1 < guild2 by definition. -CREATE TABLE guild_relations ( - guild1 MEDIUMINT UNSIGNED NOT NULL, - guild2 MEDIUMINT UNSIGNED NOT NULL, - relation TINYINT NOT NULL, - PRIMARY KEY(guild1, guild1) -); - -CREATE TABLE guild_members ( - char_id INT NOT NULL, - guild_id MEDIUMINT UNSIGNED NOT NULL, - rank TINYINT UNSIGNED NOT NULL, - tribute_enable TINYINT UNSIGNED NOT NULL DEFAULT 0, - total_tribute INT UNSIGNED NOT NULL DEFAULT 0, - last_tribute INT UNSIGNED NOT NULL DEFAULT 0, - banker TINYINT UNSIGNED NOT NULL DEFAULT 0, - public_note TEXT NOT NULL DEFAULT '', - PRIMARY KEY(char_id) -); - - -*/ - - ZoneGuildManager guild_mgr; GuildBankManager *GuildBanks; diff --git a/zone/inventory.cpp b/zone/inventory.cpp index a4625bf80..76841c86d 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -200,14 +200,7 @@ bool Client::CheckLoreConflict(const Item_Struct* item) { } bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { - /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items - This will stopgap players from items being returned if global_npc.pl has a catch all return_items - */ - struct timeval read_time; - char buffer[50]; - gettimeofday(&read_time, 0); - sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Recieved_Item", buffer); + this->EVENT_ITEM_ScriptStopReturn(); // TODO: update calling methods and script apis to handle a failure return @@ -1877,7 +1870,9 @@ void Client::DyeArmor(DyeStruct* dye){ uint8 slot2=SlotConvert(i); ItemInst* inst = this->m_inv.GetItem(slot2); if(inst){ - inst->SetColor((dye->dye[i].rgb.red*65536)+(dye->dye[i].rgb.green*256)+(dye->dye[i].rgb.blue)); + uint32 armor_color = (dye->dye[i].rgb.red * 65536) + (dye->dye[i].rgb.green * 256) + (dye->dye[i].rgb.blue); + inst->SetColor(armor_color); + database.SaveCharacterMaterialColor(this->CharacterID(), i, armor_color); database.SaveInventory(CharacterID(),inst,slot2); if(dye->dye[i].rgb.use_tint) m_pp.item_tint[i].rgb.use_tint = 0xFF; @@ -1898,7 +1893,7 @@ void Client::DyeArmor(DyeStruct* dye){ EQApplicationPacket* outapp=new EQApplicationPacket(OP_Dye,0); QueuePacket(outapp); safe_delete(outapp); - Save(); + } /*bool Client::DecreaseByItemType(uint32 type, uint8 amt) { @@ -2417,10 +2412,8 @@ void Client::CreateBandolier(const EQApplicationPacket *app) { _log(INVENTORY__BANDOLIER, "Char: %s Creating Bandolier Set %i, Set Name: %s", GetName(), bs->number, bs->name); strcpy(m_pp.bandoliers[bs->number].name, bs->name); - const ItemInst* InvItem; - - const Item_Struct *BaseItem; - + const ItemInst* InvItem; + const Item_Struct *BaseItem; int16 WeaponSlot; for(int BandolierSlot = bandolierMainHand; BandolierSlot <= bandolierAmmo; BandolierSlot++) { @@ -2431,6 +2424,7 @@ void Client::CreateBandolier(const EQApplicationPacket *app) { _log(INVENTORY__BANDOLIER, "Char: %s adding item %s to slot %i", GetName(),BaseItem->Name, WeaponSlot); m_pp.bandoliers[bs->number].items[BandolierSlot].item_id = BaseItem->ID; m_pp.bandoliers[bs->number].items[BandolierSlot].icon = BaseItem->Icon; + database.SaveCharacterBandolier(this->CharacterID(), bs->number, BandolierSlot, m_pp.bandoliers[bs->number].items[BandolierSlot].item_id, m_pp.bandoliers[bs->number].items[BandolierSlot].icon, bs->name); } else { _log(INVENTORY__BANDOLIER, "Char: %s no item in slot %i", GetName(), WeaponSlot); @@ -2438,21 +2432,17 @@ void Client::CreateBandolier(const EQApplicationPacket *app) { m_pp.bandoliers[bs->number].items[BandolierSlot].icon = 0; } } - Save(); } void Client::RemoveBandolier(const EQApplicationPacket *app) { - - // Delete bandolier with the specified number - BandolierDelete_Struct *bds = (BandolierDelete_Struct*)app->pBuffer; _log(INVENTORY__BANDOLIER, "Char: %s removing set", GetName(), bds->number); memset(m_pp.bandoliers[bds->number].name, 0, 32); for(int i = bandolierMainHand; i <= bandolierAmmo; i++) { m_pp.bandoliers[bds->number].items[i].item_id = 0; - m_pp.bandoliers[bds->number].items[i].icon = 0; + m_pp.bandoliers[bds->number].items[i].icon = 0; } - Save(); + database.DeleteCharacterBandolier(this->CharacterID(), bds->number); } void Client::SetBandolier(const EQApplicationPacket *app) { diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 32affc06e..56abe5f09 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -1239,6 +1239,11 @@ void Lua_Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in self->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg); } +void Lua_Client::SendColoredText(uint32 type, std::string msg) { + Lua_Safe_Call_Void(); + self->SendColoredText(type, msg); +} + void Lua_Client::PlayMP3(std::string file) { Lua_Safe_Call_Void(); @@ -1492,6 +1497,7 @@ luabind::scope lua_register_client() { .def("SetThirst", (void(Lua_Client::*)(int))&Lua_Client::SetThirst) .def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption) .def("SendMarqueeMessage", (void(Lua_Client::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_Client::SendMarqueeMessage) + .def("SendColoredText", (void(Lua_Client::*)(uint32, std::string))&Lua_Client::SendColoredText) .def("PlayMP3", (void(Lua_Client::*)(std::string))&Lua_Client::PlayMP3); } diff --git a/zone/lua_client.h b/zone/lua_client.h index 8809779e9..ef2af1181 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -275,6 +275,7 @@ public: void SetThirst(int in_thirst); void SetConsumption(int in_hunger, int in_thirst); void SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg); + void SendColoredText(uint32 type, std::string msg); void PlayMP3(std::string file); }; diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index dabc1c46f..869efb019 100644 --- a/zone/lua_npc.cpp +++ b/zone/lua_npc.cpp @@ -407,8 +407,18 @@ void Lua_NPC::SetSpellFocusHeal(int focus) { self->SetSpellFocusHeal(focus); } -float Lua_NPC::GetSlowMitigation() { +int Lua_NPC::GetSpellFocusDMG() { Lua_Safe_Call_Int(); + return self->GetSpellFocusDMG(); +} + +int Lua_NPC::GetSpellFocusHeal() { + Lua_Safe_Call_Int(); + return self->GetSpellFocusHeal(); +} + +float Lua_NPC::GetSlowMitigation() { + Lua_Safe_Call_Real(); return self->GetSlowMitigation(); } @@ -535,6 +545,8 @@ luabind::scope lua_register_npc() { .def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell) .def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG) .def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal) + .def("GetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusDMG) + .def("GetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::GetSpellFocusHeal) .def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation) .def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed) .def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating) diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 7db7f7e4b..2b201f323 100644 --- a/zone/lua_npc.h +++ b/zone/lua_npc.h @@ -107,6 +107,8 @@ public: void RemoveAISpell(int spell_id); void SetSpellFocusDMG(int focus); void SetSpellFocusHeal(int focus); + int GetSpellFocusDMG(); + int GetSpellFocusHeal(); float GetSlowMitigation(); float GetAttackSpeed(); int GetAccuracyRating(); diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index ea761b9b2..e16a85c6d 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -14,14 +14,31 @@ Lua_Packet::Lua_Packet(int opcode, int size) { owned_ = true; } +Lua_Packet::Lua_Packet(int opcode, int size, bool raw) { + if(raw) { + SetLuaPtrData(new EQApplicationPacket(OP_Unknown, size)); + owned_ = true; + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(opcode); + } else { + SetLuaPtrData(new EQApplicationPacket(static_cast(opcode), size)); + owned_ = true; + } +} + Lua_Packet& Lua_Packet::operator=(const Lua_Packet& o) { if(o.owned_) { owned_ = true; EQApplicationPacket *app = reinterpret_cast(o.d_); - if(app) + if(app) { d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size); - else + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(app->GetOpcodeBypass()); + } else { d_ = nullptr; + } } else { owned_ = false; d_ = o.d_; @@ -33,10 +50,14 @@ Lua_Packet::Lua_Packet(const Lua_Packet& o) { if(o.owned_) { owned_ = true; EQApplicationPacket *app = reinterpret_cast(o.d_); - if(app) + if(app) { d_ = new EQApplicationPacket(app->GetOpcode(), app->pBuffer, app->size); - else + + EQApplicationPacket *self = reinterpret_cast(d_); + self->SetOpcodeBypass(app->GetOpcodeBypass()); + } else { d_ = nullptr; + } } else { owned_ = false; d_ = o.d_; @@ -54,6 +75,16 @@ int Lua_Packet::GetOpcode() { } void Lua_Packet::SetOpcode(int op) { + Lua_Safe_Call_Void(); + self->SetOpcodeBypass(static_cast(op)); +} + +int Lua_Packet::GetRawOpcode() { + Lua_Safe_Call_Int(); + return static_cast(self->GetOpcodeBypass()); +} + +void Lua_Packet::SetRawOpcode(int op) { Lua_Safe_Call_Void(); self->SetOpcode(static_cast(op)); } @@ -244,11 +275,14 @@ luabind::scope lua_register_packet() { return luabind::class_("Packet") .def(luabind::constructor<>()) .def(luabind::constructor()) + .def(luabind::constructor()) .property("null", &Lua_Packet::Null) .property("valid", &Lua_Packet::Valid) .def("GetSize", &Lua_Packet::GetSize) .def("GetOpcode", &Lua_Packet::GetOpcode) .def("SetOpcode", &Lua_Packet::SetOpcode) + .def("GetRawOpcode", &Lua_Packet::GetRawOpcode) + .def("SetRawOpcode", &Lua_Packet::SetRawOpcode) .def("WriteInt8", &Lua_Packet::WriteInt8) .def("WriteInt16", &Lua_Packet::WriteInt16) .def("WriteInt32", &Lua_Packet::WriteInt32) @@ -267,6 +301,7 @@ luabind::scope lua_register_packet() { .def("ReadFixedLengthString", &Lua_Packet::ReadFixedLengthString); } +//TODO: Reorder these to match emu_oplist.h again luabind::scope lua_register_packet_opcodes() { return luabind::class_("Opcode") .enum_("constants") @@ -412,7 +447,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("YellForHelp", static_cast(OP_YellForHelp)), luabind::value("SafePoint", static_cast(OP_SafePoint)), luabind::value("Buff", static_cast(OP_Buff)), - luabind::value("BuffFadeMsg", static_cast(OP_BuffFadeMsg)), + luabind::value("ColoredText", static_cast(OP_ColoredText)), luabind::value("SpecialMesg", static_cast(OP_SpecialMesg)), luabind::value("Consent", static_cast(OP_Consent)), luabind::value("ConsentResponse", static_cast(OP_ConsentResponse)), @@ -809,7 +844,10 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("MercenaryDismiss", static_cast(OP_MercenaryDismiss)), luabind::value("MercenaryTimerRequest", static_cast(OP_MercenaryTimerRequest)), luabind::value("OpenInventory", static_cast(OP_OpenInventory)), - luabind::value("OpenContainer", static_cast(OP_OpenContainer)) + luabind::value("OpenContainer", static_cast(OP_OpenContainer)), + luabind::value("Marquee", static_cast(OP_Marquee)), + luabind::value("ClientTimeStamp", static_cast(OP_ClientTimeStamp)), + luabind::value("GuildPromote", static_cast(OP_GuildPromote)) ]; } diff --git a/zone/lua_packet.h b/zone/lua_packet.h index c036594d1..a3c5ac553 100644 --- a/zone/lua_packet.h +++ b/zone/lua_packet.h @@ -21,6 +21,7 @@ public: Lua_Packet() : Lua_Ptr(nullptr), owned_(false) { } Lua_Packet(EQApplicationPacket *d) : Lua_Ptr(d), owned_(false) { } Lua_Packet(int opcode, int size); + Lua_Packet(int opcode, int size, bool raw); Lua_Packet& operator=(const Lua_Packet& o); Lua_Packet(const Lua_Packet& o); virtual ~Lua_Packet() { if(owned_) { EQApplicationPacket *ptr = GetLuaPtrData(); if(ptr) { delete ptr; } } } @@ -28,6 +29,8 @@ public: int GetSize(); int GetOpcode(); void SetOpcode(int op); + int GetRawOpcode(); + void SetRawOpcode(int op); void WriteInt8(int offset, int value); void WriteInt16(int offset, int value); void WriteInt32(int offset, int value); diff --git a/zone/lua_spell.cpp b/zone/lua_spell.cpp index 597982770..e99b9ba84 100644 --- a/zone/lua_spell.cpp +++ b/zone/lua_spell.cpp @@ -419,6 +419,56 @@ bool Lua_Spell::GetAllowRest() { return self->AllowRest; } +bool Lua_Spell::GetInCombat() { + Lua_Safe_Call_Bool(); + return self->InCombat; +} + +bool Lua_Spell::GetOutOfCombat() { + Lua_Safe_Call_Bool(); + return self->OutofCombat; +} + +int Lua_Spell::GetAEMaxTargets() { + Lua_Safe_Call_Int(); + return self->aemaxtargets; +} + +int Lua_Spell::GetMaxTargets() { + Lua_Safe_Call_Int(); + return self->maxtargets; +} + +bool Lua_Spell::GetPersistDeath() { + Lua_Safe_Call_Bool(); + return self->persistdeath; +} + +float Lua_Spell::GetMinDist() { + Lua_Safe_Call_Real(); + return self->min_dist; +} + +float Lua_Spell::GetMinDistMod() { + Lua_Safe_Call_Real(); + return self->min_dist_mod; +} + +float Lua_Spell::GetMaxDist() { + Lua_Safe_Call_Real(); + return self->max_dist; +} + +float Lua_Spell::GetMaxDistMod() { + Lua_Safe_Call_Real(); + return self->max_dist_mod; +} + +float Lua_Spell::GetMinRange() { + Lua_Safe_Call_Real(); + return self->min_range; +} + int Lua_Spell::GetDamageShieldType() { Lua_Safe_Call_Int(); return self->DamageShieldType; @@ -501,6 +551,16 @@ luabind::scope lua_register_spell() { .def("PowerfulFlag", &Lua_Spell::GetPowerfulFlag) .def("CastRestriction", &Lua_Spell::GetCastRestriction) .def("AllowRest", &Lua_Spell::GetAllowRest) + .def("InCombat", &Lua_Spell::GetInCombat) + .def("OutOfCombat", &Lua_Spell::GetOutOfCombat) + .def("AEMaxTargets", &Lua_Spell::GetAEMaxTargets) + .def("MaxTargets", &Lua_Spell::GetMaxTargets) + .def("PersistDeath", &Lua_Spell::GetPersistDeath) + .def("MinDist", &Lua_Spell::GetMinDist) + .def("MinDistMod", &Lua_Spell::GetMinDistMod) + .def("MaxDist", &Lua_Spell::GetMaxDist) + .def("MaxDistMod", &Lua_Spell::GetMaxDistMod) + .def("MinRange", &Lua_Spell::GetMinRange) .def("DamageShieldType", &Lua_Spell::GetDamageShieldType); } diff --git a/zone/lua_spell.h b/zone/lua_spell.h index 9a8435cc5..bfa5a19ed 100644 --- a/zone/lua_spell.h +++ b/zone/lua_spell.h @@ -96,6 +96,16 @@ public: int GetPowerfulFlag(); int GetCastRestriction(); bool GetAllowRest(); + bool GetInCombat(); + bool GetOutOfCombat(); + int GetAEMaxTargets(); + int GetMaxTargets(); + bool GetPersistDeath(); + float GetMinDist(); + float GetMinDistMod(); + float GetMaxDist(); + float GetMaxDistMod(); + float GetMinRange(); int GetDamageShieldType(); }; diff --git a/zone/merc.cpp b/zone/merc.cpp index 2316c6af0..6f500b12e 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -4619,13 +4619,7 @@ void Merc::DoClassAttacks(Mob *target) { if(!ca_time) return; - float HasteModifier = 0; - if(GetHaste() > 0) - HasteModifier = 10000 / (100 + GetHaste()); - else if(GetHaste() < 0) - HasteModifier = (100 - GetHaste()); - else - HasteModifier = 100; + float HasteModifier = GetHaste() * 0.01f; int level = GetLevel(); int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will @@ -4689,7 +4683,7 @@ void Merc::DoClassAttacks(Mob *target) { } } - classattack_timer.Start(reuse*HasteModifier/100); + classattack_timer.Start(reuse / HasteModifier); } bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) diff --git a/zone/mob.cpp b/zone/mob.cpp index b7f32bf94..8d53d50ef 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1932,6 +1932,7 @@ void Mob::SetZone(uint32 zone_id, uint32 instance_id) { CastToClient()->GetPP().zone_id = zone_id; CastToClient()->GetPP().zoneInstance = instance_id; + CastToClient()->Save(); } Save(); } @@ -2734,12 +2735,29 @@ uint32 Mob::GetZoneID() const { return(zone->GetZoneID()); } -int Mob::GetHaste() { - int h = spellbonuses.haste + spellbonuses.hastetype2; +int Mob::GetHaste() +{ + // See notes in Client::CalcHaste + // Need to check if the effect of inhibit melee differs for NPCs + if (spellbonuses.haste < 0) { + if (-spellbonuses.haste <= spellbonuses.inhibitmelee) + return 100 - spellbonuses.inhibitmelee; + else + return 100 + spellbonuses.haste; + } + + if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee) + return 100 - spellbonuses.inhibitmelee; + + int h = 0; int cap = 0; - int overhaste = 0; int level = GetLevel(); + if (spellbonuses.haste) + h += spellbonuses.haste - spellbonuses.inhibitmelee; + if (spellbonuses.hastetype2 && level > 49) + h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2; + // 26+ no cap, 1-25 10 if (level > 25) // 26+ h += itembonuses.haste; @@ -2759,21 +2777,13 @@ int Mob::GetHaste() { // 51+ 25 (despite there being higher spells...), 1-50 10 if (level > 50) // 51+ - overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3; else // 1-50 - overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; + h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3; - h += overhaste; h += ExtraHaste; //GM granted haste. - if (spellbonuses.inhibitmelee) { - if (h >= 0) - h -= spellbonuses.inhibitmelee; - else - h -= ((100 + h) * spellbonuses.inhibitmelee / 100); - } - - return(h); + return 100 + h; } void Mob::SetTarget(Mob* mob) { @@ -3030,12 +3040,11 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) } } -void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) +bool Mob::TrySpellTrigger(Mob *target, uint32 spell_id, int effect) { - if(target == nullptr || !IsValidSpell(spell_id)) - { - return; - } + if(!target || !IsValidSpell(spell_id)) + return false; + int spell_trig = 0; // Count all the percentage chances to trigger for all effects for(int i = 0; i < EFFECT_COUNT; i++) @@ -3054,8 +3063,10 @@ void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) if(MakeRandomInt(0, trig_chance) <= spells[spell_id].base[i]) { // If we trigger an effect then its over. - SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); - break; + if (IsValidSpell(spells[spell_id].base2[i])){ + SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); + return true; + } } else { @@ -3069,37 +3080,15 @@ void Mob::TrySpellTrigger(Mob *target, uint32 spell_id) // if the chances don't add to 100, then each effect gets a chance to fire, chance for no trigger as well. else { - for(int i = 0; i < EFFECT_COUNT; i++) + if(MakeRandomInt(0, 100) <= spells[spell_id].base[effect]) { - if (spells[spell_id].effectid[i] == SE_SpellTrigger) - { - if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) - { - SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); - } - } - } - } -} - -void Mob::TryApplyEffect(Mob *target, uint32 spell_id) -{ - if(target == nullptr || !IsValidSpell(spell_id)) - { - return; - } - - for(int i = 0; i < EFFECT_COUNT; i++) - { - if (spells[spell_id].effectid[i] == SE_ApplyEffect) - { - if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) - { - if(target) - SpellFinished(spells[spell_id].base2[i], target, 10, 0, -1, spells[spell_id].ResistDiff); + if (IsValidSpell(spells[spell_id].base2[effect])){ + SpellFinished(spells[spell_id].base2[effect], target, 10, 0, -1, spells[spells[spell_id].base2[effect]].ResistDiff); + return true; //Only trigger once of these per spell effect. } } } + return false; } void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsPet) @@ -5013,3 +5002,119 @@ float Mob::HeadingAngleToMob(Mob *other) return (90.0 - angle + 270.0) * 511.5 * 0.0027777778; } +int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot) +{ + if (!IsValidSpell(spell_id)) + return 0; + + if (!identifier) + return 0; + + int32 stat = 0; + + if (slot > 0) + slot = slot - 1; + + std::string id = identifier; + for(int i = 0; i < id.length(); ++i) + { + id[i] = tolower(id[i]); + } + + if (slot < 16){ + if (id == "classes") {stat = spells[spell_id].classes[slot]; } + else if (id == "dieties") {stat = spells[spell_id].deities[slot];} + } + + if (slot < 12){ + if (id == "base") {stat = spells[spell_id].base[slot];} + else if (id == "base2") {stat = spells[spell_id].base2[slot];} + else if (id == "max") {stat = spells[spell_id].max[slot];} + else if (id == "formula") {spells[spell_id].formula[slot];} + else if (id == "effectid") {spells[spell_id].effectid[slot];} + } + + if (slot < 4){ + if (id == "components") { spells[spell_id].components[slot];} + else if (id == "component_counts") {spells[spell_id].component_counts[slot];} + else if (id == "NoexpendReagent") {spells[spell_id].NoexpendReagent[slot];} + } + + if (id == "range") {stat = spells[spell_id].range; } + else if (id == "aoerange") {stat = spells[spell_id].aoerange;} + else if (id == "pushback") {stat = spells[spell_id].pushback;} + else if (id == "pushup") {stat = spells[spell_id].pushup;} + else if (id == "cast_time") {stat = spells[spell_id].cast_time;} + else if (id == "recovery_time") {stat = spells[spell_id].recovery_time;} + else if (id == "recast_time") {stat = spells[spell_id].recast_time;} + else if (id == "buffdurationformula") {stat = spells[spell_id].buffdurationformula;} + else if (id == "buffduration") {stat = spells[spell_id].buffduration;} + else if (id == "AEDuration") {stat = spells[spell_id].AEDuration;} + else if (id == "mana") {stat = spells[spell_id].mana;} + //else if (id == "LightType") {stat = spells[spell_id].LightType;} - Not implemented + else if (id == "goodEffect") {stat = spells[spell_id].goodEffect;} + else if (id == "Activated") {stat = spells[spell_id].Activated;} + else if (id == "resisttype") {stat = spells[spell_id].resisttype;} + else if (id == "targettype") {stat = spells[spell_id].targettype;} + else if (id == "basedeiff") {stat = spells[spell_id].basediff;} + else if (id == "skill") {stat = spells[spell_id].skill;} + else if (id == "zonetype") {stat = spells[spell_id].zonetype;} + else if (id == "EnvironmentType") {stat = spells[spell_id].EnvironmentType;} + else if (id == "TimeOfDay") {stat = spells[spell_id].TimeOfDay;} + else if (id == "CastingAnim") {stat = spells[spell_id].CastingAnim;} + else if (id == "SpellAffectIndex") {stat = spells[spell_id].SpellAffectIndex; } + else if (id == "disallow_sit") {stat = spells[spell_id].disallow_sit; } + //else if (id == "spellanim") {stat = spells[spell_id].spellanim; } - Not implemented + else if (id == "uninterruptable") {stat = spells[spell_id].uninterruptable; } + else if (id == "ResistDiff") {stat = spells[spell_id].ResistDiff; } + else if (id == "dot_stacking_exemp") {stat = spells[spell_id].dot_stacking_exempt; } + else if (id == "RecourseLink") {stat = spells[spell_id].RecourseLink; } + else if (id == "no_partial_resist") {stat = spells[spell_id].no_partial_resist; } + else if (id == "short_buff_box") {stat = spells[spell_id].short_buff_box; } + else if (id == "descnum") {stat = spells[spell_id].descnum; } + else if (id == "effectdescnum") {stat = spells[spell_id].effectdescnum; } + else if (id == "npc_no_los") {stat = spells[spell_id].npc_no_los; } + else if (id == "reflectable") {stat = spells[spell_id].reflectable; } + else if (id == "bonushate") {stat = spells[spell_id].bonushate; } + else if (id == "EndurCost") {stat = spells[spell_id].EndurCost; } + else if (id == "EndurTimerIndex") {stat = spells[spell_id].EndurTimerIndex; } + else if (id == "IsDisciplineBuf") {stat = spells[spell_id].IsDisciplineBuff; } + else if (id == "HateAdded") {stat = spells[spell_id].HateAdded; } + else if (id == "EndurUpkeep") {stat = spells[spell_id].EndurUpkeep; } + else if (id == "numhitstype") {stat = spells[spell_id].numhitstype; } + else if (id == "numhits") {stat = spells[spell_id].numhits; } + else if (id == "pvpresistbase") {stat = spells[spell_id].pvpresistbase; } + else if (id == "pvpresistcalc") {stat = spells[spell_id].pvpresistcalc; } + else if (id == "pvpresistcap") {stat = spells[spell_id].pvpresistcap; } + else if (id == "spell_category") {stat = spells[spell_id].spell_category; } + else if (id == "can_mgb") {stat = spells[spell_id].can_mgb; } + else if (id == "dispel_flag") {stat = spells[spell_id].dispel_flag; } + else if (id == "MinResist") {stat = spells[spell_id].MinResist; } + else if (id == "MaxResist") {stat = spells[spell_id].MaxResist; } + else if (id == "viral_targets") {stat = spells[spell_id].viral_targets; } + else if (id == "viral_timer") {stat = spells[spell_id].viral_timer; } + else if (id == "NimbusEffect") {stat = spells[spell_id].NimbusEffect; } + else if (id == "directional_start") {stat = spells[spell_id].directional_start; } + else if (id == "directional_end") {stat = spells[spell_id].directional_end; } + else if (id == "not_extendable") {stat = spells[spell_id].not_extendable; } + else if (id == "suspendable") {stat = spells[spell_id].suspendable; } + else if (id == "viral_range") {stat = spells[spell_id].viral_range; } + else if (id == "spellgroup") {stat = spells[spell_id].spellgroup; } + else if (id == "rank") {stat = spells[spell_id].rank; } + else if (id == "powerful_flag") {stat = spells[spell_id].powerful_flag; } + else if (id == "CastRestriction") {stat = spells[spell_id].CastRestriction; } + else if (id == "AllowRest") {stat = spells[spell_id].AllowRest; } + else if (id == "InCombat") {stat = spells[spell_id].InCombat; } + else if (id == "OutofCombat") {stat = spells[spell_id].OutofCombat; } + else if (id == "aemaxtargets") {stat = spells[spell_id].aemaxtargets; } + else if (id == "maxtargets") {stat = spells[spell_id].maxtargets; } + else if (id == "persistdeath") {stat = spells[spell_id].persistdeath; } + else if (id == "min_dist") {stat = spells[spell_id].min_dist; } + else if (id == "min_dist_mod") {stat = spells[spell_id].min_dist_mod; } + else if (id == "max_dist") {stat = spells[spell_id].max_dist; } + else if (id == "min_range") {stat = spells[spell_id].min_range; } + else if (id == "DamageShieldType") {stat = spells[spell_id].DamageShieldType; } + + return stat; +} + diff --git a/zone/mob.h b/zone/mob.h index 07459eab9..352a610eb 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -205,7 +205,7 @@ public: void SendSpellBarEnable(uint16 spellid); void ZeroCastingVars(); virtual void SpellProcess(); - virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, + virtual bool CastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, uint32 type = 0, int16 *resist_adjust = nullptr); virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = 10, int32 casttime = -1, @@ -526,7 +526,7 @@ public: //More stuff to sort: - virtual bool IsRaidTarget() { return false; }; + virtual bool IsRaidTarget() const { return false; }; virtual bool IsAttackAllowed(Mob *target, bool isSpellAttack = false); bool IsTargeted() const { return (targeted > 0); } inline void IsTargeted(int in_tar) { targeted += in_tar; if(targeted < 0) targeted = 0;} @@ -579,8 +579,7 @@ public: void DoBuffWearOffEffect(uint32 index); void TryTriggerOnCast(uint32 spell_id, bool aa_trigger); void TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger); - void TrySpellTrigger(Mob *target, uint32 spell_id); - void TryApplyEffect(Mob *target, uint32 spell_id); + bool TrySpellTrigger(Mob *target, uint32 spell_id, int effect); void TryTriggerOnValueAmount(bool IsHP = false, bool IsMana = false, bool IsEndur = false, bool IsPet = false); void TryTwincast(Mob *caster, Mob *target, uint32 spell_id); void TrySympatheticProc(Mob *target, uint32 spell_id); @@ -621,6 +620,7 @@ public: void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr); inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; }; inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; }; + int32 GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot = 0); void ModSkillDmgTaken(SkillUseTypes skill_num, int value); int16 GetModSkillDmgTaken(const SkillUseTypes skill_num); @@ -684,7 +684,6 @@ 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); @@ -769,6 +768,7 @@ public: inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); } void ProcessFlee(); void CheckFlee(); + inline bool IsBlind() { return spellbonuses.IsBlind; } inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);} float CalculateHeadingToTarget(float in_x, float in_y); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 125e1d85f..5f8ea2c11 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -1040,7 +1040,7 @@ void Mob::AI_Process() { // if(RuleB(Combat, EnableFearPathing)){ if(curfp) { - if(IsRooted()) { + if(IsRooted() || (IsBlind() && CombatRange(hate_list.GetClosest(this)))) { //make sure everybody knows were not moving, for appearance sake if(IsMoving()) { @@ -1087,7 +1087,9 @@ void Mob::AI_Process() { if (engaged) { - if (IsRooted()) + // we are prevented from getting here if we are blind and don't have a target in range + // from above, so no extra blind checks needed + if (IsRooted() || IsBlind()) SetTarget(hate_list.GetClosest(this)); else { diff --git a/zone/net.cpp b/zone/net.cpp index 8a8421786..498ed5102 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -97,8 +97,6 @@ extern Zone* zone; EQStreamFactory eqsf(ZoneStream); npcDecayTimes_Struct npcCorpseDecayTimes[100]; TitleManager title_manager; -DBAsyncFinishedQueue MTdbafq; -DBAsync *dbasync = nullptr; QueryServ *QServ = 0; TaskManager *taskmanager = 0; QuestParserCollection *parse = 0; @@ -168,8 +166,6 @@ int main(int argc, char** argv) { _log(ZONE__INIT_ERR, "Cannot continue without a database connection."); return 1; } - dbasync = new DBAsync(&database); - dbasync->AddFQ(&MTdbafq); guild_mgr.SetDatabase(&database); GuildBanks = nullptr; @@ -444,10 +440,6 @@ int main(int argc, char** argv) { } } - DBAsyncWork* dbaw = 0; - while ((dbaw = MTdbafq.Pop())) { - DispatchFinishedDBAsync(dbaw); - } if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); @@ -507,8 +499,6 @@ int main(int argc, char** argv) { //Fix for Linux world server problem. eqsf.Close(); worldserver.Disconnect(); - dbasync->CommitWrites(); - dbasync->StopThread(); safe_delete(taskmanager); command_deinit(); safe_delete(parse); diff --git a/zone/npc.cpp b/zone/npc.cpp index a4ecaec3a..b4c726acb 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -965,240 +965,352 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, } } -uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - uint32 tmp2 = 0; +uint32 ZoneDatabase::CreateNewNPCCommand(const char* zone, uint32 zone_version,Client *client, NPC* spawn, uint32 extra) { + + uint32 npc_type_id = 0; + + if (extra && client && client->GetZoneID()) + { + // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) + int starting_npc_id = client->GetZoneID() * 1000; + + std::string query = StringFormat("SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", + starting_npc_id, starting_npc_id + 1000); + auto results = QueryDatabase(query); + if (results.Success()) { + if (results.RowCount() != 0) + { + auto row = results.begin(); + npc_type_id = atoi(row[0]) + 1; + // Prevent the npc_type id from exceeding the range for this zone + if (npc_type_id >= (starting_npc_id + 1000)) + npc_type_id = 0; + } + else // No npc_type IDs set in this range yet + npc_type_id = starting_npc_id; + } + } + + char tmpstr[64]; + EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); + std::string query; + if (npc_type_id) + { + query = StringFormat("INSERT INTO npc_types (id, name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(%i, \"%s\" , %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + npc_type_id = results.LastInsertedID(); + } + else + { + query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + npc_type_id = results.LastInsertedID(); + } + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawngroup (id, name) VALUES(%i, '%s-%s')", 0, zone, spawn->GetName()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + uint32 spawngroupid = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, + spawn->GetHeading(), spawngroupid); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", + spawngroupid, npc_type_id, 100); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + if(client) + client->LogSQL(query.c_str()); + + return true; +} + +uint32 ZoneDatabase::AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime) { + uint32 last_insert_id = 0; + + std::string query = StringFormat("INSERT INTO spawngroup (name) VALUES('%s%s%i')", + zone, spawn->GetName(), Timer::GetCurrentTime()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + last_insert_id = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + uint32 respawntime = 0; + uint32 spawnid = 0; + if (respawnTime) + respawntime = respawnTime; + else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) + respawntime = spawn->respawn2->RespawnTimer(); + else + respawntime = 1200; + + query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, + spawn->GetHeading(), last_insert_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + spawnid = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("INSERT INTO spawnentry (spawngroupID, npcID, chance) VALUES(%i, %i, %i)", + last_insert_id, spawn->GetNPCTypeID(), 100); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "CreateNewNPCSpawnGroupCommand Error: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if(client) + client->LogSQL(query.c_str()); + + return spawnid; +} + +uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client *client, NPC* spawn) { + + std::string query = StringFormat("UPDATE npc_types SET name = \"%s\", level = %i, race = %i, class = %i, " + "hp = %i, gender = %i, texture = %i, helmtexture = %i, size = %i, " + "loottable_id = %i, merchant_id = %i, face = %i, WHERE id = %i", + spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, spawn->GetNPCTypeID()); + auto results = QueryDatabase(query); + if (!results.Success() && client) + client->LogSQL(query.c_str()); + + return results.Success() == true? 1: 0; +} + +uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn) { + uint32 id = 0; + uint32 spawngroupID = 0; + + std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE " + "zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + if (row[0]) + id = atoi(row[0]); + + if (row[1]) + spawngroupID = atoi(row[1]); + + query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + return 1; +} + +uint32 ZoneDatabase::DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn) { + + uint32 id = 0; + uint32 spawngroupID = 0; + + std::string query = StringFormat("SELECT id, spawngroupID FROM spawn2 WHERE zone = '%s' " + "AND version = %u AND spawngroupID = %i", + zone, zone_version, spawn->GetSp2()); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + if (row[0]) + id = atoi(row[0]); + + if (row[1]) + spawngroupID = atoi(row[1]); + + query = StringFormat("DELETE FROM spawn2 WHERE id = '%i'", id); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawngroup WHERE id = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM spawnentry WHERE spawngroupID = '%i'", spawngroupID); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM npc_types WHERE id = '%i'", spawn->GetNPCTypeID()); + results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + return 1; +} + +uint32 ZoneDatabase::AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { + uint32 last_insert_id = 0; + std::string query = StringFormat("INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) " + "VALUES('%s', %u, %f, %f, %f, %i, %f, %i)", + zone, zone_version, client->GetX(), client->GetY(), client->GetZ(), + 120, client->GetHeading(), spawnGroupID); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + + if(client) + client->LogSQL(query.c_str()); + + return 1; +} + +uint32 ZoneDatabase::AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID) { + + uint32 npc_type_id; + char numberlessName[64]; + + EntityList::RemoveNumbers(strn0cpy(numberlessName, spawn->GetName(), sizeof(numberlessName))); + std::string query = StringFormat("INSERT INTO npc_types (name, level, race, class, hp, gender, " + "texture, helmtexture, size, loottable_id, merchant_id, face, " + "runspeed, prim_melee_type, sec_melee_type) " + "VALUES(\"%s\", %i, %i, %i, %i, %i, %i, %i, %f, %i, %i, %i, %f, %i, %i)", + numberlessName, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), + spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), + spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), + spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28); + auto results = QueryDatabase(query); + if (!results.Success()) + return 0; + npc_type_id = results.LastInsertedID(); + + if(client) + client->LogSQL(query.c_str()); + + if(client) + client->Message(0, "%s npc_type ID %i created successfully!", numberlessName, npc_type_id); + + return 1; +} + +uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { + switch (command) { case 0: { // Create a new NPC and add all spawn related data - uint32 npc_type_id = 0; - uint32 spawngroupid; - if (extra && c && c->GetZoneID()) - { - // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) - int starting_npc_id = c->GetZoneID() * 1000; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) - { - if (row[0]) - { - npc_type_id = atoi(row[0]) + 1; - // Prevent the npc_type id from exceeding the range for this zone - if (npc_type_id >= (starting_npc_id + 1000)) - { - npc_type_id = 0; - } - } - else - { - // row[0] is nullptr - No npc_type IDs set in this range yet - npc_type_id = starting_npc_id; - } - } - - safe_delete_array(query); - mysql_free_result(result); - } - } - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (npc_type_id) - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - if(c) c->LogSQL(query); - safe_delete_array(query); - snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; + return CreateNewNPCCommand(zone, zone_version, c, spawn, extra); } case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID - tmp2 = spawn->GetNPCTypeID(); - char tmpstr[64]; - snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - uint32 respawntime = 0; - uint32 spawnid = 0; - if (extra) - respawntime = extra; - else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) - respawntime = spawn->respawn2->RespawnTimer(); - else - respawntime = 1200; - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return spawnid; - break; + return AddNewNPCSpawnGroupCommand(zone, zone_version, c, spawn, extra); } case 2: { // Update npc_type appearance and other data on targeted spawn - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - } - else { - safe_delete_array(query); - return false; - } - break; + return UpdateNPCTypeAppearance(c, spawn); } case 3: { // delete spawn from spawning, but leave in npc_types table - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return 0; - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - - - break; + return DeleteSpawnLeaveInNPCTypeTable(zone, c, spawn); } case 4: { //delete spawn from DB (including npc_type) - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return(0); - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - mysql_free_result(result); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; + return DeleteSpawnRemoveFromNPCTypeTable(zone, zone_version, c, spawn); } case 5: { // add a spawn from spawngroup - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, c->GetX(), c->GetY(), c->GetZ(), 120, c->GetHeading(), extra), errbuf, 0, 0, &tmp)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - return true; - break; - } + return AddSpawnFromSpawnGroup(zone, zone_version, c, spawn, extra); + } case 6: { // add npc_type - uint32 npc_type_id; - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if(c) c->Message(0, "%s npc_type ID %i created successfully!", tmpstr, npc_type_id); - return true; - break; + return AddNPCTypes(zone, zone_version, c, spawn, extra); } } return false; @@ -1743,7 +1855,7 @@ void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) else ns->spawn.IsMercenary = 0; } - + //Not recommended if using above (However, this will work better on older clients). if (RuleB(Pets, UnTargetableSwarmPet)) { if(GetOwnerID() || GetSwarmOwner()) { diff --git a/zone/npc.h b/zone/npc.h index b23522bee..f1e4c790c 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -136,10 +136,6 @@ public: 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;} - inline void SetSpellFocusHeal(int32 NewSpellFocusHeal) {SpellFocusHeal = NewSpellFocusHeal;} - int32 SpellFocusDMG; - int32 SpellFocusHeal; virtual void SetTarget(Mob* mob); virtual uint16 GetSkill(SkillUseTypes skill_num) const { if (skill_num <= HIGHEST_SKILL) { return skills[skill_num]; } return 0; } @@ -387,6 +383,12 @@ public: inline void SetHealScale(float amt) { healscale = amt; } inline float GetHealScale() { return healscale; } + + inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;} + inline int32 GetSpellFocusDMG() const { return SpellFocusDMG;} + + inline void SetSpellFocusHeal(int32 NewSpellFocusHeal) {SpellFocusHeal = NewSpellFocusHeal;} + inline int32 GetSpellFocusHeal() const {return SpellFocusHeal;} uint32 GetSpawnKillCount(); int GetScore(); @@ -452,6 +454,8 @@ protected: uint32 npc_mana; float spellscale; float healscale; + int32 SpellFocusDMG; + int32 SpellFocusHeal; //pet crap: uint16 pet_spell_id; diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 72e2865c9..a33e9a8b9 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -5072,6 +5072,35 @@ XS(XS_Client_UpdateTaskActivity) XSRETURN_EMPTY; } +XS(XS_Client_GetTaskActivityDoneCount); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_GetTaskActivityDoneCount) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::GetTaskActivityDoneCount(THIS, TaskID, ActivityID)"); + { + Client * THIS; + int RETVAL; + int TaskID = (int)SvIV(ST(1)); + int ActivityID = (int)SvIV(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *, tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if (THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + + RETVAL = THIS->GetTaskActivityDoneCountFromTaskID(TaskID, ActivityID); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + XS(XS_Client_AssignTask); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_AssignTask) { @@ -5991,6 +6020,58 @@ XS(XS_Client_SendMarqueeMessage) XSRETURN_EMPTY; } +XS(XS_Client_SendColoredText); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendColoredText) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: Client::SendColoredText(color, message)"); + { + Client * THIS; + uint32 color = (uint32)SvUV(ST(1)); + std::string msg = (std::string)SvPV_nolen(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendColoredText(color, msg); + } + XSRETURN_EMPTY; +} + +XS(XS_Client_SendSpellAnim); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_SendSpellAnim) +{ + dXSARGS; + if (items != 3) + Perl_croak(aTHX_ "Usage: SendSpellAnim(uint16 spell_id, uint32 seq)"); + { + Client * THIS; + uint16 targetid = (uint16)SvUV(ST(1)); + uint16 spell_id = (uint16)SvUV(ST(2)); + dXSTARG; + + if (sv_derived_from(ST(0), "Client")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Client *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Client"); + if(THIS == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + THIS->SendSpellAnim(targetid,spell_id); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -6199,6 +6280,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "IsTaskCompleted"), XS_Client_IsTaskCompleted, file, "$$"); newXSproto(strcpy(buf, "IsTaskActive"), XS_Client_IsTaskActive, file, "$$"); newXSproto(strcpy(buf, "IsTaskActivityActive"), XS_Client_IsTaskActivityActive, file, "$$$"); + newXSproto(strcpy(buf, "GetTaskActivityDoneCount"), XS_Client_GetTaskActivityDoneCount, file, "$$$"); newXSproto(strcpy(buf, "GetCorpseCount"), XS_Client_GetCorpseCount, file, "$"); newXSproto(strcpy(buf, "GetCorpseID"), XS_Client_GetCorpseID, file, "$$"); newXSproto(strcpy(buf, "GetCorpseItemAt"), XS_Client_GetCorpseItemAt, file, "$$$"); @@ -6229,6 +6311,9 @@ XS(boot_Client) newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$"); newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$"); newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$"); + newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$"); + newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$"); + XSRETURN_YES; } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index 53a5aade8..762246519 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -8137,6 +8137,39 @@ XS(XS_Mob_GetFlurryChance) XSRETURN(1); } +XS(XS_Mob_GetSpellStat); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSpellStat) +{ + dXSARGS; + if (items < 3 || items > 4) + Perl_croak(aTHX_ "Usage: Mob::GetSpellStat(THIS, itemid, stat, slot)"); + { + Mob * THIS; + int32 RETVAL; + uint32 spellid = (uint32)SvUV(ST(1)); + Const_char * stat = (Const_char *)SvPV_nolen(ST(2)); + uint8 slot = (uint8)SvUV(ST(3)); + dXSTARG; + + if (sv_derived_from(ST(0), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Mob *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Mob"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + if (items > 4) { slot = 0; } + + + RETVAL = THIS->GetSpellStat(spellid, stat, slot); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + + #ifdef __cplusplus extern "C" #endif @@ -8436,6 +8469,7 @@ XS(boot_Mob) newXSproto(strcpy(buf, "IsMeleeDisabled"), XS_Mob_IsMeleeDisabled, file, "$$"); newXSproto(strcpy(buf, "SetFlurryChance"), XS_Mob_SetFlurryChance, file, "$$"); newXSproto(strcpy(buf, "GetFlurryChance"), XS_Mob_GetFlurryChance, file, "$"); + newXSproto(strcpy(buf, "GetSpellStat"), XS_Mob_GetSpellStat, file, "$$$$"); XSRETURN_YES; } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index dcc66b886..c1f8d8828 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -1991,6 +1991,32 @@ XS(XS_NPC_SetSpellFocusDMG) XSRETURN_EMPTY; } +XS(XS_NPC_GetSpellFocusDMG); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpellFocusDMG) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusDMG(THIS)"); + { + NPC * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetSpellFocusDMG(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + XS(XS_NPC_SetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_SetSpellFocusHeal) { @@ -2015,6 +2041,32 @@ XS(XS_NPC_SetSpellFocusHeal) XSRETURN_EMPTY; } +XS(XS_NPC_GetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */ +XS(XS_NPC_GetSpellFocusHeal) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusHeal(THIS)"); + { + NPC * THIS; + int32 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "NPC")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(NPC *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type NPC"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetSpellFocusHeal(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + XS(XS_NPC_GetSlowMitigation); /* prototype to pass -Wmissing-prototypes */ XS(XS_NPC_GetSlowMitigation) { @@ -2286,8 +2338,10 @@ XS(boot_NPC) newXSproto(strcpy(buf, "RemoveAISpell"), XS_NPC_RemoveSpellFromNPCList, file, "$$"); newXSproto(strcpy(buf, "SetSpellFocusDMG"), XS_NPC_SetSpellFocusDMG, file, "$$"); newXSproto(strcpy(buf, "SetSpellFocusHeal"), XS_NPC_SetSpellFocusHeal, file, "$$"); - newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetAttackSpeed, file, "$"); - newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetSlowMitigation, file, "$"); + newXSproto(strcpy(buf, "GetSpellFocusDMG"), XS_NPC_GetSpellFocusDMG, file, "$"); + newXSproto(strcpy(buf, "GetSpellFocusHeal"), XS_NPC_GetSpellFocusHeal, file, "$"); + newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetSlowMitigation, file, "$"); + newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetAttackSpeed, file, "$"); newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$"); newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$"); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index fc31178ae..90f0d370d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -172,7 +172,7 @@ void QuestManager::EndQuest() { cur = QTimerList.erase(cur); else ++cur; - } + } run.owner->Depop(); } quests_running_.pop(); @@ -447,7 +447,7 @@ void QuestManager::settimer(const char *timer_name, int seconds) { end = QTimerList.end(); while (cur != end) { - if(cur->mob && cur->mob == owner && cur->name == timer_name) + if(cur->mob && cur->mob == owner && cur->name == timer_name) { cur->Timer_.Enable(); cur->Timer_.Start(seconds * 1000, false); @@ -471,7 +471,7 @@ void QuestManager::settimerMS(const char *timer_name, int milliseconds) { end = QTimerList.end(); while (cur != end) { - if(cur->mob && cur->mob == owner && cur->name == timer_name) + if(cur->mob && cur->mob == owner && cur->name == timer_name) { cur->Timer_.Enable(); cur->Timer_.Start(milliseconds, false); @@ -946,7 +946,7 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { uint16 count; uint16 curspell; - uint16 Char_ID = initiator->CharacterID(); + uint32 Char_ID = initiator->CharacterID(); bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals); bool SpellGlobalCheckResult = 0; @@ -960,7 +960,7 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { spells[curspell].skill != 52 && ( !RuleB(Spells, UseCHAScribeHack) || spells[curspell].effectid[EFFECT_COUNT - 1] != 10 ) ) - { + { if(IsDiscipline(curspell)){ //we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) { @@ -974,18 +974,20 @@ uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) { SpellGlobalCheckResult = initiator->SpellGlobalCheck(curspell, Char_ID); if (SpellGlobalCheckResult) { initiator->GetPP().disciplines.values[r] = curspell; + database.SaveCharacterDisc(Char_ID, r, curspell); initiator->SendDisciplineUpdate(); initiator->Message(0, "You have learned a new discipline!"); count++; //success counter } - break; //continue the 1st loop + break; //continue the 1st loop } else { - initiator->GetPP().disciplines.values[r] = curspell; - initiator->SendDisciplineUpdate(); - initiator->Message(0, "You have learned a new discipline!"); - count++; //success counter - break; //continue the 1st loop + initiator->GetPP().disciplines.values[r] = curspell; + database.SaveCharacterDisc(Char_ID, r, curspell); + initiator->SendDisciplineUpdate(); + initiator->Message(0, "You have learned a new discipline!"); + count++; //success counter + break; //continue the 1st loop } } //if we get to this point, there's already a discipline in this slot, so we skip it } @@ -1308,7 +1310,7 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti if (initiator && initiator->IsClient()){ // some events like waypoint and spawn don't have a player involved qgCharid=initiator->CharacterID(); - } + } else { qgCharid=-qgNpcid; // make char id negative npc id as a fudge } @@ -1335,81 +1337,69 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti /* Inserts global variable into quest_globals table */ int QuestManager::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varname, const char *varvalue, int duration) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; // Make duration string either "unix_timestamp(now()) + xxx" or "NULL" - std::stringstream duration_ss; - if (duration == INT_MAX) { - duration_ss << "NULL"; - } - else { - duration_ss << "unix_timestamp(now()) + " << duration; - } + std::string durationText = (duration == INT_MAX)? "NULL": StringFormat("unix_timestamp(now()) + %i", duration); /* NOTE: this should be escaping the contents of arglist npcwise a malicious script can arbitrarily alter the DB */ - uint32 last_id = 0; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "REPLACE INTO quest_globals (charid, npcid, zoneid, name, value, expdate)" - "VALUES (%i, %i, %i, '%s', '%s', %s)", - charid, npcid, zoneid, varname, varvalue, duration_ss.str().c_str() - ), errbuf, nullptr, nullptr, &last_id)) - { - std::cerr << "setglobal error inserting " << varname << " : " << errbuf << std::endl; - } - safe_delete_array(query); - if(zone) { - /* Delete existing qglobal data and update zone processes */ - ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); - ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer; - qgd->npc_id = npcid; - qgd->char_id = charid; - qgd->zone_id = zoneid; - qgd->from_zone_id = zone->GetZoneID(); - qgd->from_instance_id = zone->GetInstanceID(); - strcpy(qgd->name, varname); + std::string query = StringFormat("REPLACE INTO quest_globals " + "(charid, npcid, zoneid, name, value, expdate)" + "VALUES (%i, %i, %i, '%s', '%s', %s)", + charid, npcid, zoneid, varname, varvalue, durationText.c_str()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + std::cerr << "setglobal error inserting " << varname << " : " << results.ErrorMessage() << std::endl; - entity_list.DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); - zone->DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); + if(!zone) + return 0; - worldserver.SendPacket(pack); - safe_delete(pack); + /* Delete existing qglobal data and update zone processes */ + ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); + ServerQGlobalDelete_Struct *qgd = (ServerQGlobalDelete_Struct*)pack->pBuffer; + qgd->npc_id = npcid; + qgd->char_id = charid; + qgd->zone_id = zoneid; + qgd->from_zone_id = zone->GetZoneID(); + qgd->from_instance_id = zone->GetInstanceID(); + strcpy(qgd->name, varname); - /* Create new qglobal data and update zone processes */ - pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct)); - ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer; - qgu->npc_id = npcid; - qgu->char_id = charid; - qgu->zone_id = zoneid; - if(duration == INT_MAX) { - qgu->expdate = 0xFFFFFFFF; - } - else { - qgu->expdate = Timer::GetTimeSeconds() + duration; - } - strcpy((char*)qgu->name, varname); - strn0cpy((char*)qgu->value, varvalue, 128); - qgu->id = last_id; - qgu->from_zone_id = zone->GetZoneID(); - qgu->from_instance_id = zone->GetInstanceID(); + entity_list.DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); + zone->DeleteQGlobal(std::string((char*)qgd->name), qgd->npc_id, qgd->char_id, qgd->zone_id); - QGlobal temp; - temp.npc_id = npcid; - temp.char_id = charid; - temp.zone_id = zoneid; - temp.expdate = qgu->expdate; - temp.name.assign(qgu->name); - temp.value.assign(qgu->value); - entity_list.UpdateQGlobal(qgu->id, temp); - zone->UpdateQGlobal(qgu->id, temp); + worldserver.SendPacket(pack); + safe_delete(pack); - worldserver.SendPacket(pack); - safe_delete(pack); - } + /* Create new qglobal data and update zone processes */ + pack = new ServerPacket(ServerOP_QGlobalUpdate, sizeof(ServerQGlobalUpdate_Struct)); + ServerQGlobalUpdate_Struct *qgu = (ServerQGlobalUpdate_Struct*)pack->pBuffer; + qgu->npc_id = npcid; + qgu->char_id = charid; + qgu->zone_id = zoneid; + + qgu->expdate = (duration == INT_MAX)? 0xFFFFFFFF: Timer::GetTimeSeconds() + duration; + + strcpy((char*)qgu->name, varname); + strn0cpy((char*)qgu->value, varvalue, 128); + qgu->id = results.LastInsertedID(); + qgu->from_zone_id = zone->GetZoneID(); + qgu->from_instance_id = zone->GetInstanceID(); + + QGlobal temp; + temp.npc_id = npcid; + temp.char_id = charid; + temp.zone_id = zoneid; + temp.expdate = qgu->expdate; + temp.name.assign(qgu->name); + temp.value.assign(qgu->value); + entity_list.UpdateQGlobal(qgu->id, temp); + zone->UpdateQGlobal(qgu->id, temp); + + worldserver.SendPacket(pack); + safe_delete(pack); return 0; } @@ -1420,22 +1410,14 @@ void QuestManager::targlobal(const char *varname, const char *value, const char void QuestManager::delglobal(const char *varname) { QuestManagerCurrentQuestVars(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; int qgZoneid=zone->GetZoneID(); int qgCharid=0; int qgNpcid=owner->GetNPCTypeID(); - - - - if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved - { - qgCharid=initiator->CharacterID(); - } - else { + if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved + qgCharid=initiator->CharacterID(); + else qgCharid=-qgNpcid; // make char id negative npc id as a fudge - } /* QS: PlayerLogQGlobalUpdate */ if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){ @@ -1443,31 +1425,32 @@ void QuestManager::delglobal(const char *varname) { QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc); } - if (!database.RunQuery(query, - MakeAnyLenString(&query, - "DELETE FROM quest_globals WHERE name='%s'" - " && (npcid=0 || npcid=%i) && (charid=0 || charid=%i) && (zoneid=%i || zoneid=0)", - varname,qgNpcid,qgCharid,qgZoneid),errbuf)) - { - std::cerr << "delglobal error deleting " << varname << " : " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM quest_globals " + "WHERE name = '%s' " + "&& (npcid=0 || npcid=%i) " + "&& (charid=0 || charid=%i) " + "&& (zoneid=%i || zoneid=0)", + varname, qgNpcid, qgCharid, qgZoneid); + auto results = database.QueryDatabase(query); + if (!results.Success()) + std::cerr << "delglobal error deleting " << varname << " : " << results.ErrorMessage() << std::endl; - if(zone) { - ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); - ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; + if(!zone) + return; - qgu->npc_id = qgNpcid; - qgu->char_id = qgCharid; - qgu->zone_id = qgZoneid; - strcpy(qgu->name, varname); + ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); + ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; - entity_list.DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); - zone->DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); + qgu->npc_id = qgNpcid; + qgu->char_id = qgCharid; + qgu->zone_id = qgZoneid; + strcpy(qgu->name, varname); - worldserver.SendPacket(pack); - safe_delete(pack); - } + entity_list.DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); + zone->DeleteQGlobal(std::string((char*)qgu->name), qgu->npc_id, qgu->char_id, qgu->zone_id); + + worldserver.SendPacket(pack); + safe_delete(pack); } // Converts duration string to duration value (in seconds) @@ -1688,11 +1671,6 @@ void QuestManager::showgrid(int grid) { if(initiator == nullptr) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - FindPerson_Point pt; std::vector pts; @@ -1702,22 +1680,25 @@ void QuestManager::showgrid(int grid) { pts.push_back(pt); // Retrieve all waypoints for this grid - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result)) { - while((row = mysql_fetch_row(result))) { - pt.x = atof(row[0]); - pt.y = atof(row[1]); - pt.z = atof(row[2]); - pts.push_back(pt); - } - mysql_free_result(result); - initiator->SendPathPacket(pts); - } - else // DB query error! - { - LogFile->write(EQEMuLog::Quest, "Error loading grid %d for showgrid(): %s", grid, errbuf); + std::string query = StringFormat("SELECT `x`,`y`,`z` FROM grid_entries " + "WHERE `gridid` = %i AND `zoneid` = %i " + "ORDER BY `number`", grid, zone->GetZoneID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Quest, "Error loading grid %d for showgrid(): %s", grid, results.ErrorMessage().c_str()); return; - } - safe_delete_array(query); + } + + for(auto row = results.begin(); row != results.end(); ++row) { + pt.x = atof(row[0]); + pt.y = atof(row[1]); + pt.z = atof(row[2]); + + pts.push_back(pt); + } + + initiator->SendPathPacket(pts); + } //change the value of a spawn condition @@ -2293,19 +2274,19 @@ bool QuestManager::istaskappropriate(int task) { } void QuestManager::clearspawntimers() { - if(zone) { - //TODO: Dec 19, 2008, replace with code updated for current spawn timers. - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - while (iterator.MoreElements()) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE id=%lu AND " - "instance_id=%lu",(unsigned long)iterator.GetData()->GetID(), (unsigned long)zone->GetInstanceID()), errbuf); - safe_delete_array(query); - iterator.Advance(); - } + if(!zone) + return; + + //TODO: Dec 19, 2008, replace with code updated for current spawn timers. + LinkedListIterator iterator(zone->spawn2_list); + iterator.Reset(); + while (iterator.MoreElements()) { + std::string query = StringFormat("DELETE FROM respawn_times " + "WHERE id = %lu AND instance_id = %lu", + (unsigned long)iterator.GetData()->GetID(), + (unsigned long)zone->GetInstanceID()); + auto results = database.QueryDatabase(query); + iterator.Advance(); } } @@ -2682,11 +2663,6 @@ void QuestManager::FlagInstanceByRaidLeader(uint32 zone, int16 version) const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkName) { QuestManagerCurrentQuestVars(); - const char *ERR_MYSQLERROR = "Error in saylink phrase queries"; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; int sayid = 0; int sz = strlen(Phrase); @@ -2694,70 +2670,50 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam database.DoEscapeString(escaped_string, Phrase, sz); // Query for an existing phrase and id in the saylink table - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string),errbuf,&result)) - { - if (mysql_num_rows(result) >= 1) - { - while((row = mysql_fetch_row(result))) - { + std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string); + auto results = database.QueryDatabase(query); + if (results.Success()) { + if (results.RowCount() >= 1) { + for (auto row = results.begin();row != results.end(); ++row) sayid = atoi(row[0]); - } - mysql_free_result(result); - } - else // Add a new saylink entry to the database and query it again for the new sayid number - { - safe_delete_array(query); - - database.RunQuery(query,MakeAnyLenString(&query,"INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string),errbuf); - safe_delete_array(query); - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `id` FROM saylink WHERE `phrase` = '%s'", escaped_string),errbuf,&result)) - { - if (mysql_num_rows(result) >= 1) - { - while((row = mysql_fetch_row(result))) - { - sayid = atoi(row[0]); - } - mysql_free_result(result); + } else { // Add a new saylink entry to the database and query it again for the new sayid number + std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string); + results = database.QueryDatabase(insert_query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); + } else { + results = database.QueryDatabase(query); + if (results.Success()) { + if (results.RowCount() >= 1) + for(auto row = results.begin(); row != results.end(); ++row) + sayid = atoi(row[0]); + } else { + LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str()); } } - else - { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - safe_delete_array(query); } } - safe_delete_array(query); safe_delete_array(escaped_string); - if(silent) + if (silent) sayid = sayid + 750000; else sayid = sayid + 500000; - //Create the say link as an item link hash - char linktext[250]; + //Create the say link as an item link hash + char linktext[250]; - if(initiator) - { + if (initiator) { if (initiator->GetClientVersion() >= EQClientRoF) - { sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12); - } else if (initiator->GetClientVersion() >= EQClientSoF) - { sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"00000000000000000000000000000000000000000000",LinkName,0x12); - } else - { sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"000000000000000000000000000000000000000",LinkName,0x12); - } - } - else { // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones. + } else { // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones. sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12); } + strcpy(Phrase,linktext); return Phrase; @@ -2949,6 +2905,15 @@ const char* QuestManager::GetZoneLongName(const char *zone) { return ln.c_str(); } +void QuestManager::CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 data){ + ServerPacket* pack = new ServerPacket(ServerOP_CZSignalNPC, sizeof(CZNPCSignal_Struct)); + CZNPCSignal_Struct* CZSN = (CZNPCSignal_Struct*)pack->pBuffer; + CZSN->npctype_id = npctype_id; + CZSN->data = data; + worldserver.SendPacket(pack); + safe_delete(pack); +} + void QuestManager::CrossZoneSignalPlayerByCharID(int charid, uint32 data){ ServerPacket* pack = new ServerPacket(ServerOP_CZSignalClient, sizeof(CZClientSignal_Struct)); CZClientSignal_Struct* CZSC = (CZClientSignal_Struct*) pack->pBuffer; @@ -2966,7 +2931,7 @@ void QuestManager::CrossZoneSignalPlayerByName(const char *CharName, uint32 data CZSC->data = data; worldserver.SendPacket(pack); safe_delete(pack); -} +} void QuestManager::CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message){ uint32 message_len = strlen(CharName) + 1; @@ -2976,6 +2941,18 @@ void QuestManager::CrossZoneMessagePlayerByName(uint32 Type, const char *CharNam CZSC->Type = Type; strn0cpy(CZSC->CharName, CharName, 64); strn0cpy(CZSC->Message, Message, 512); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QuestManager::CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var){ + uint32 message_len = strlen(id) + 1; + uint32 message_len2 = strlen(m_var) + 1; + ServerPacket* pack = new ServerPacket(ServerOP_CZSetEntityVariableByNPCTypeID, sizeof(CZSetEntVarByNPCTypeID_Struct) + message_len + message_len2); + CZSetEntVarByNPCTypeID_Struct* CZSNBYNID = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer; + CZSNBYNID->npctype_id = npctype_id; + strn0cpy(CZSNBYNID->id, id, 256); + strn0cpy(CZSNBYNID->m_var, m_var, 256); worldserver.SendPacket(pack); safe_delete(pack); } diff --git a/zone/questmgr.h b/zone/questmgr.h index 89a0eca78..2d1184942 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -239,8 +239,10 @@ public: uint16 CreateDoor( const char* model, float x, float y, float z, float heading, uint8 opentype, uint16 size); int32 GetZoneID(const char *zone); const char *GetZoneLongName(const char *zone); - void CrossZoneSignalPlayerByCharID(int charid, uint32 data); + void CrossZoneSignalPlayerByCharID(int charid, uint32 data); + void CrossZoneSignalNPCByNPCTypeID(uint32 npctype_id, uint32 data); void CrossZoneSignalPlayerByName(const char *CharName, uint32 data); + void CrossZoneSetEntityVariableByNPCTypeID(uint32 npctype_id, const char *id, const char *m_var); void CrossZoneMessagePlayerByName(uint32 Type, const char *CharName, const char *Message); bool EnableRecipe(uint32 recipe_id); bool DisableRecipe(uint32 recipe_id); diff --git a/zone/raids.cpp b/zone/raids.cpp index 84ebcee7d..699bc6b1d 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -29,6 +29,12 @@ Raid::Raid(uint32 raidID) : GroupIDConsumer(raidID) { memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); + memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); + memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS); + for (int i = 0; i < MAX_RAID_GROUPS; i++) { + group_mentor[i].mentor_percent = 0; + group_mentor[i].mentoree = nullptr; + } leader = nullptr; memset(leadername, 0, 64); locked = false; @@ -39,6 +45,12 @@ Raid::Raid(Client* nLeader) : GroupIDConsumer() { memset(members ,0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); + memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); + memset(group_aa, 0, sizeof(GroupLeadershipAA_Struct) * MAX_RAID_GROUPS); + for (int i = 0; i < MAX_RAID_GROUPS; i++) { + group_mentor[i].mentor_percent = 0; + group_mentor[i].mentoree = nullptr; + } leader = nLeader; memset(leadername, 0, 64); strn0cpy(leadername, nLeader->GetName(), 64); @@ -74,21 +86,32 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo if(!c) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO raid_members SET raidid=%lu, charid=%lu, groupid=%lu, _class=%d, level=%d, name='%s', isgroupleader=%d, israidleader=%d, islooter=%d", (unsigned long)GetID(), (unsigned long)c->CharacterID(), (unsigned long)group, c->GetClass(), c->GetLevel(), c->GetName(), groupleader, rleader, looter ),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("INSERT INTO raid_members SET raidid = %lu, charid = %lu, " + "groupid = %lu, _class = %d, level = %d, name = '%s', " + "isgroupleader = %d, israidleader = %d, islooter = %d", + (unsigned long)GetID(), (unsigned long)c->CharacterID(), + (unsigned long)group, c->GetClass(), c->GetLevel(), + c->GetName(), groupleader, rleader, looter); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); + if (rleader) { + database.SetRaidGroupLeaderInfo(RAID_GROUPLESS, GetID()); + UpdateRaidAAs(); + } + if (group != RAID_GROUPLESS && groupleader) { + database.SetRaidGroupLeaderInfo(group, GetID()); + UpdateGroupAAs(group); + } if(group < 12) GroupUpdate(group); + else // get raid AAs, GroupUpdate will handles it otherwise + SendGroupLeadershipAA(c, RAID_GROUPLESS); SendRaidAddAll(c->GetName()); c->SetRaidGrouped(true); + SendRaidMOTD(c); ServerPacket *pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; @@ -100,32 +123,26 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo safe_delete(pack); } -void Raid::RemoveMember(const char *c) +void Raid::RemoveMember(const char *characterName) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "DELETE FROM raid_members where name='%s'", c ),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("DELETE FROM raid_members where name='%s'", characterName); + auto results = database.QueryDatabase(query); - Client *m = entity_list.GetClientByName(c); - safe_delete_array(query); + Client *client = entity_list.GetClientByName(characterName); disbandCheck = true; - SendRaidRemoveAll(c); - SendRaidDisband(m); + SendRaidRemoveAll(characterName); + SendRaidDisband(client); LearnMembers(); VerifyRaid(); - if(m){ - m->SetRaidGrouped(false); - } + if(client) + client->SetRaidGrouped(false); ServerPacket *pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); rga->instance_id = zone->GetInstanceID(); - strn0cpy(rga->playername, c, 64); + strn0cpy(rga->playername, characterName, 64); rga->zoneid = zone->GetZoneID(); worldserver.SendPacket(pack); safe_delete(pack); @@ -133,14 +150,9 @@ void Raid::RemoveMember(const char *c) void Raid::DisbandRaid() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "DELETE FROM raid_members WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("DELETE FROM raid_members WHERE raidid = %lu", (unsigned long)GetID()); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); SendRaidDisbandAll(); @@ -159,14 +171,10 @@ void Raid::DisbandRaid() void Raid::MoveMember(const char *name, uint32 newGroup) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET groupid=%lu WHERE name='%s'", (unsigned long)newGroup, name),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_members SET groupid = %lu WHERE name = '%s'", + (unsigned long)newGroup, name); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); SendRaidMoveAll(name); @@ -183,21 +191,13 @@ void Raid::MoveMember(const char *name, uint32 newGroup) void Raid::SetGroupLeader(const char *who, bool glFlag) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET isgroupleader=%lu WHERE name='%s'", (unsigned long)glFlag, who),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_members SET isgroupleader = %lu WHERE name = '%s'", + (unsigned long)glFlag, who); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); - //if(glFlag == true){ //we're setting the flag - //this->SendMakeGroupLeaderPacket(who); - //} - ServerPacket *pack = new ServerPacket(ServerOP_RaidGroupLeader, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); @@ -208,27 +208,29 @@ void Raid::SetGroupLeader(const char *who, bool glFlag) safe_delete(pack); } +Client *Raid::GetGroupLeader(uint32 group_id) +{ + if (group_id == RAID_GROUPLESS) + return nullptr; + + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member && members[i].IsGroupLeader && members[i].GroupNumber == group_id) + return members[i].member; + + return nullptr; +} + void Raid::SetRaidLeader(const char *wasLead, const char *name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (!database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET israidleader=0 WHERE name='%s'", wasLead),errbuf,&result)){ - printf("Set Raid Leader error: %s\n", errbuf); - } - else - mysql_free_result(result); + std::string query = StringFormat("UPDATE raid_members SET israidleader = 0 WHERE name = '%s'", wasLead); + auto results = database.QueryDatabase(query); + if (!results.Success()) + printf("Set Raid Leader error: %s\n", results.ErrorMessage().c_str()); - safe_delete_array(query); - query = 0; - - if (!database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET israidleader=1 WHERE name='%s'", name),errbuf,&result)){ - printf("Set Raid Leader error: %s\n", errbuf); - } - else - mysql_free_result(result); - - safe_delete_array(query); + query = StringFormat("UPDATE raid_members SET israidleader = 1 WHERE name = '%s'", name); + results = database.QueryDatabase(query); + if (!results.Success()) + printf("Set Raid Leader error: %s\n", results.ErrorMessage().c_str()); strn0cpy(leadername, name, 64); @@ -250,6 +252,58 @@ void Raid::SetRaidLeader(const char *wasLead, const char *name) safe_delete(pack); } +void Raid::SaveGroupLeaderAA(uint32 gid) +{ + char *queryBuffer = new char[sizeof(GroupLeadershipAA_Struct) * 2 + 1]; + database.DoEscapeString(queryBuffer, (char*)&group_aa[gid], sizeof(GroupLeadershipAA_Struct)); + + std::string query = "UPDATE raid_leaders SET leadershipaa = '"; + query += queryBuffer; + query += StringFormat("' WHERE gid = %lu AND rid = %lu LIMIT 1", gid, GetID()); + safe_delete_array(queryBuffer); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); +} + +void Raid::SaveRaidLeaderAA() +{ + char *queryBuffer = new char[sizeof(RaidLeadershipAA_Struct) * 2 + 1]; + database.DoEscapeString(queryBuffer, (char*)&raid_aa, sizeof(RaidLeadershipAA_Struct)); + + std::string query = "UPDATE raid_leaders SET leadershipaa = '"; + query += queryBuffer; + query += StringFormat("' WHERE gid = %lu AND rid = %lu LIMIT 1", RAID_GROUPLESS, GetID()); + safe_delete_array(queryBuffer); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", results.ErrorMessage().c_str()); +} + +void Raid::UpdateGroupAAs(uint32 gid) +{ + Client *gl = GetGroupLeader(gid); + + if (gl) + gl->GetGroupAAs(&group_aa[gid]); + else + memset(&group_aa[gid], 0, sizeof(GroupLeadershipAA_Struct)); + + SaveGroupLeaderAA(gid); +} + +void Raid::UpdateRaidAAs() +{ + Client *rl = GetLeader(); + + if (rl) + rl->GetRaidAAs(&raid_aa); + else + memset(&raid_aa, 0, sizeof(RaidLeadershipAA_Struct)); + + SaveRaidLeaderAA(); +} + bool Raid::IsGroupLeader(const char *who) { for(int x = 0; x < MAX_RAID_MEMBERS; x++) @@ -264,14 +318,10 @@ bool Raid::IsGroupLeader(const char *who) void Raid::UpdateLevel(const char *name, int newLevel) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET level=%lu WHERE name='%s'", (unsigned long)newLevel, name),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_members SET level = %lu WHERE name = '%s'", + (unsigned long)newLevel, name); + auto results = database.QueryDatabase(query); - safe_delete_array(query); LearnMembers(); VerifyRaid(); } @@ -514,7 +564,7 @@ void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster, int32 int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; int gi = 0; - + float distance; float range2 = range*range; @@ -567,7 +617,7 @@ void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster, int3 if (!range) range = 200; - + float distance; float range2 = range*range; @@ -775,27 +825,16 @@ void Raid::TeleportRaid(Mob* sender, uint32 zoneID, uint16 instance_id, float x, void Raid::ChangeLootType(uint32 type) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_details SET loottype=%lu WHERE raidid=%lu", (unsigned long)type, (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("UPDATE raid_details SET loottype = %lu WHERE raidid = %lu", + (unsigned long)type, (unsigned long)GetID()); + auto results = database.QueryDatabase(query); LootType = type; } void Raid::AddRaidLooter(const char* looter) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET islooter=1 WHERE name='%s'", looter),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("UPDATE raid_members SET islooter = 1 WHERE name = '%s'", looter); + auto results = database.QueryDatabase(query); for(int x = 0; x < MAX_RAID_MEMBERS; x++) { @@ -812,36 +851,19 @@ void Raid::AddRaidLooter(const char* looter) rga->instance_id = zone->GetInstanceID(); worldserver.SendPacket(pack); safe_delete(pack); - - /* For reference only at this time. This code adds a looter to the Raid Options Window. - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rgs = (RaidGeneral_Struct*)outapp->pBuffer; - rgs->action = 33; - strcpy(rgs->leader_name, looter); - QueuePacket(outapp); - safe_delete(outapp); */ } void Raid::RemoveRaidLooter(const char* looter) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_members SET islooter=0 WHERE name='%s'", looter),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("UPDATE raid_members SET islooter = 0 WHERE name = '%s'", looter); + auto results = database.QueryDatabase(query); for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - if(strcmp(looter, members[x].membername) == 0) - { + if(strcmp(looter, members[x].membername) == 0) { members[x].IsLooter = 0; break; } - } + ServerPacket *pack = new ServerPacket(ServerOP_DetailsChange, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); @@ -849,15 +871,6 @@ void Raid::RemoveRaidLooter(const char* looter) rga->instance_id = zone->GetInstanceID(); worldserver.SendPacket(pack); safe_delete(pack); - - /* For reference only at this time. This code removes a looter from the Raid Options Window. - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rgs = (RaidGeneral_Struct*)outapp->pBuffer; - rgs->action = 34; - strcpy(rgs->leader_name, looter); - QueuePacket(outapp); - safe_delete(outapp); */ } bool Raid::IsRaidMember(const char *name){ @@ -1089,12 +1102,12 @@ void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req) void Raid::SendMakeLeaderPacket(const char *who) //30 { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct)); + RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer; rg->action = raidMakeLeader; strn0cpy(rg->leader_name, who, 64); strn0cpy(rg->player_name, who, 64); - rg->parameter = 0; + memcpy(&rg->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct)); QueuePacket(outapp); safe_delete(outapp); } @@ -1104,12 +1117,12 @@ void Raid::SendMakeLeaderPacketTo(const char *who, Client *to) if(!to) return; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); - RaidGeneral_Struct *rg = (RaidGeneral_Struct*)outapp->pBuffer; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct)); + RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer; rg->action = raidMakeLeader; strn0cpy(rg->leader_name, who, 64); strn0cpy(rg->player_name, who, 64); - rg->parameter = 0; + memcpy(&rg->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct)); to->QueuePacket(outapp); safe_delete(outapp); } @@ -1167,6 +1180,7 @@ void Raid::SendGroupUpdate(Client *to) strn0cpy(gu->leadersname, to->GetName(), 64); } strn0cpy(gu->yourname, to->GetName(), 64); + memcpy(&gu->leader_aas, &group_aa[grp], sizeof(GroupLeadershipAA_Struct)); to->FastQueuePacket(&outapp); } @@ -1179,8 +1193,10 @@ void Raid::GroupUpdate(uint32 gid, bool initial) { if(strlen(members[x].membername) > 0){ if(members[x].GroupNumber == gid){ - if(members[x].member) + if(members[x].member) { SendGroupUpdate(members[x].member); + SendGroupLeadershipAA(members[x].member, gid); + } } } } @@ -1279,16 +1295,78 @@ void Raid::SendRaidGroupRemove(const char *who, uint32 gid) safe_delete(pack); } +void Raid::SendRaidMOTD(Client *c) +{ + if (!c || motd.empty()) + return; + + size_t size = motd.size() + 1; + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidMOTD_Struct) + size); + RaidMOTD_Struct *rmotd = (RaidMOTD_Struct *)outapp->pBuffer; + rmotd->general.action = raidSetMotd; + strn0cpy(rmotd->general.player_name, c->GetName(), 64); + strn0cpy(rmotd->motd, motd.c_str(), size); + c->FastQueuePacket(&outapp); +} + +void Raid::SendRaidMOTD() +{ + if (motd.empty()) + return; + + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member) + SendRaidMOTD(members[i].member); +} + +void Raid::SendRaidMOTDToWorld() +{ + if (motd.empty()) + return; + + size_t size = motd.size() + 1; + ServerPacket *pack = new ServerPacket(ServerOP_RaidMOTD, sizeof(ServerRaidMOTD_Struct) + size); + ServerRaidMOTD_Struct *smotd = (ServerRaidMOTD_Struct *)pack->pBuffer; + smotd->rid = GetID(); + strn0cpy(smotd->motd, motd.c_str(), size); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void Raid::SendGroupLeadershipAA(Client *c, uint32 gid) +{ + EQApplicationPacket *outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct)); + RaidLeadershipUpdate_Struct *rlaa = (RaidLeadershipUpdate_Struct *)outapp->pBuffer; + rlaa->action = raidSetLeaderAbilities; + strn0cpy(rlaa->leader_name, c->GetName(), 64); + strn0cpy(rlaa->player_name, c->GetName(), 64); + if (gid != RAID_GROUPLESS) + memcpy(&rlaa->group, &group_aa[gid], sizeof(GroupLeadershipAA_Struct)); + memcpy(&rlaa->raid, &raid_aa, sizeof(RaidLeadershipAA_Struct)); + c->QueuePacket(outapp); + safe_delete(outapp); +} + +void Raid::SendGroupLeadershipAA(uint32 gid) +{ + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member && members[i].GroupNumber == gid) + SendGroupLeadershipAA(members[i].member, gid); +} + +void Raid::SendAllRaidLeadershipAA() +{ + for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) + if (members[i].member) + SendGroupLeadershipAA(members[i].member, members[i].GroupNumber); +} + void Raid::LockRaid(bool lockFlag) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "UPDATE raid_details SET locked=%d WHERE raidid=%lu", lockFlag, (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } + std::string query = StringFormat("UPDATE raid_details SET locked = %d WHERE raidid = %lu", + lockFlag, (unsigned long)GetID()); + auto results = database.QueryDatabase(query); - safe_delete_array(query); locked = lockFlag; if(lockFlag) SendRaidLock(); @@ -1307,74 +1385,79 @@ void Raid::LockRaid(bool lockFlag) void Raid::SetRaidDetails() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - if (database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO raid_details SET raidid=%lu, loottype=4, locked=0", (unsigned long)GetID()),errbuf,&result)){ - mysql_free_result(result); - } - - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO raid_details SET raidid = %lu, loottype = 4, locked = 0, motd = ''", + (unsigned long)GetID()); + auto results = database.QueryDatabase(query); } void Raid::GetRaidDetails() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT locked, loottype FROM raid_details WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){ - safe_delete_array(query); - if(mysql_num_rows(result) < 1) { - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "Error getting raid details for raid %lu: %s", (unsigned long)GetID(), errbuf); - return; - } - row = mysql_fetch_row(result); - if(row){ - locked = atoi(row[0]); - LootType = atoi(row[1]); - } - mysql_free_result(result); - } + std::string query = StringFormat("SELECT locked, loottype, motd FROM raid_details WHERE raidid = %lu", + (unsigned long)GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error getting raid details for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); + return; + } + + auto row = results.begin(); + + locked = atoi(row[0]); + LootType = atoi(row[1]); + motd = std::string(row[2]); +} + +void Raid::SaveRaidMOTD() +{ + std::string query = StringFormat("UPDATE raid_details SET motd = '%s' WHERE raidid = %lu", + EscapeString(motd).c_str(), (unsigned long)GetID()); + + auto results = database.QueryDatabase(query); } bool Raid::LearnMembers() { memset(members, 0, (sizeof(RaidMember)*MAX_RAID_MEMBERS)); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT name, groupid, _class, level, isgroupleader, israidleader, islooter FROM raid_members WHERE raidid=%lu", (unsigned long)GetID()),errbuf,&result)){ - safe_delete_array(query); - if(mysql_num_rows(result) < 1) { - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "Error getting raid members for raid %lu: %s", (unsigned long)GetID(), errbuf); - disbandCheck = true; - return(false); - } - int i = 0; - while((row = mysql_fetch_row(result))) { - if(!row[0]) - continue; - members[i].member = nullptr; - strn0cpy(members[i].membername, row[0], 64); - int GroupNum = atoi(row[1]); - if(GroupNum > 11) - members[i].GroupNumber = 0xFFFFFFFF; - else - members[i].GroupNumber = GroupNum; - members[i]._class = atoi(row[2]); - members[i].level = atoi(row[3]); - members[i].IsGroupLeader = atoi(row[4]); - members[i].IsRaidLeader = atoi(row[5]); - members[i].IsLooter = atoi(row[6]); - i++; - } - mysql_free_result(result); - } - return(true); + + std::string query = StringFormat("SELECT name, groupid, _class, level, " + "isgroupleader, israidleader, islooter " + "FROM raid_members WHERE raidid = %lu", + (unsigned long)GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return false; + + if(results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error getting raid members for raid %lu: %s", (unsigned long)GetID(), results.ErrorMessage().c_str()); + disbandCheck = true; + return false; + } + + int index = 0; + for(auto row = results.begin(); row != results.end(); ++row) { + if(!row[0]) + continue; + + members[index].member = nullptr; + strn0cpy(members[index].membername, row[0], 64); + int groupNum = atoi(row[1]); + if(groupNum > 11) + members[index].GroupNumber = 0xFFFFFFFF; + else + members[index].GroupNumber = groupNum; + + members[index]._class = atoi(row[2]); + members[index].level = atoi(row[3]); + members[index].IsGroupLeader = atoi(row[4]); + members[index].IsRaidLeader = atoi(row[5]); + members[index].IsLooter = atoi(row[6]); + ++index; + } + + return true; } void Raid::VerifyRaid() @@ -1407,13 +1490,19 @@ void Raid::MemberZoned(Client *c) if(!c) return; + // Raid::GetGroup() goes over the members as well, this way we go over once + uint32 gid = RAID_GROUPLESS; for(int x = 0; x < MAX_RAID_MEMBERS; x++) { if(members[x].member == c) { members[x].member = nullptr; + gid = members[x].GroupNumber; } } + + if (gid < 12 && group_mentor[gid].mentoree == c) + group_mentor[gid].mentoree = nullptr; } void Raid::SendHPPacketsTo(Client *c) @@ -1518,3 +1607,60 @@ void Raid::RaidMessage_StringID(Mob* sender, uint32 type, uint32 string_id, cons } } +void Raid::LoadLeadership() +{ + database.GetRaidLeadershipInfo(GetID(), nullptr, nullptr, nullptr, nullptr, &raid_aa); + + char mentor_name[64]; + for (uint32 group_id = 0; group_id < MAX_RAID_GROUPS; group_id++) { + database.GetGroupLeadershipInfo(group_id, GetID(), nullptr, nullptr, nullptr, nullptr, + mentor_name, &group_mentor[group_id].mentor_percent, &group_aa[group_id]); + if (strlen(mentor_name)) { + group_mentor[group_id].name = mentor_name; + mentor_name[0] = '\0'; + } + } +} + +void Raid::SetGroupMentor(uint32 group_id, int percent, char *name) +{ + if (group_id > 11) + return; + group_mentor[group_id].name = name; + group_mentor[group_id].mentor_percent = percent; + Client *client = entity_list.GetClientByName(name); + group_mentor[group_id].mentoree = client ? client : nullptr; + + std::string query = StringFormat("UPDATE raid_leaders SET mentoree = '%s', mentor_percent = %i WHERE gid = %i AND rid = %i LIMIT 1", + name, percent, group_id, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to set raid group mentor: %s\n", results.ErrorMessage().c_str()); +} + +void Raid::ClearGroupMentor(uint32 group_id) +{ + if (group_id > 11) + return; + group_mentor[group_id].name.clear(); + group_mentor[group_id].mentor_percent = 0; + group_mentor[group_id].mentoree = nullptr; + + std::string query = StringFormat("UPDATE raid_leaders SET mentoree = '', mentor_percent = 0 WHERE gid = %i AND rid = %i LIMIT 1", + group_id, GetID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to clear raid group mentor: %s\n", results.ErrorMessage().c_str()); +} + +// there isn't a nice place to add this in another function, unlike groups +// so we do it here instead +void Raid::CheckGroupMentor(uint32 group_id, Client *c) +{ + if (!c || group_id > 11) + return; + + if (group_mentor[group_id].name == c->GetName()) + group_mentor[group_id].mentoree = c; +} + diff --git a/zone/raids.h b/zone/raids.h index be12788e6..eb963ad32 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -38,13 +38,13 @@ enum { //raid packet types: raidMembers = 6, //len 395+, details + members list raidNoAssignLeadership = 7, raidCreate = 8, //len 72 - raidUnknown = 9, + raidUnknown = 9, // unused? raidNoRaid = 10, //parameter=0 raidChangeLootType = 11, raidStringID = 12, raidChangeGroupLeader = 13, //136 raid leader, new group leader, group_id? - raidBecomeGroupLeader = 14, //472 - raidUnknown2 = 15, + raidSetLeaderAbilities = 14, //472 + raidSetLeaderData = 15, // 14,15 SoE names, not sure on difference, 14 packet has 0x100 bytes 15 0x214 in addition to raid general raidChangeGroup = 16, //?? len 136 old leader, new leader, 0 (preceeded with a remove2) raidLock = 17, //len 136 leader?, leader, 0 raidUnlock = 18, //len 136 leader?, leader, 0 @@ -79,6 +79,7 @@ enum { //raid command types #define MAX_RAID_GROUPS 12 #define MAX_RAID_MEMBERS 72 +const uint32 RAID_GROUPLESS = 0xFFFFFFFF; struct RaidMember{ char membername[64]; @@ -91,6 +92,12 @@ struct RaidMember{ bool IsLooter; }; +struct GroupMentor { + std::string name; + Client *mentoree; + int mentor_percent; +}; + class Raid : public GroupIDConsumer { public: Raid(Client *nLeader); @@ -111,6 +118,7 @@ public: void DisbandRaid(); void MoveMember(const char *name, uint32 newGroup); void SetGroupLeader(const char *who, bool glFlag = true); + Client *GetGroupLeader(uint32 group_id); void RemoveGroupLeader(const char *who); bool IsGroupLeader(const char *who); bool IsRaidMember(const char *name); @@ -130,6 +138,8 @@ public: void AddRaidLooter(const char* looter); void RemoveRaidLooter(const char* looter); + inline void SetRaidMOTD(std::string in_motd) { motd = in_motd; }; + //util func //keeps me from having to keep iterating through the list //when I want lots of data from the same entry @@ -160,6 +170,7 @@ public: //also learns raid structure based on db. void SetRaidDetails(); void GetRaidDetails(); + void SaveRaidMOTD(); bool LearnMembers(); void VerifyRaid(); void MemberZoned(Client *c); @@ -194,9 +205,34 @@ public: void SendMakeGroupLeaderPacketAll(); void SendMakeGroupLeaderPacket(const char *who); //13 void SendMakeGroupLeaderPacketTo(const char *who, Client *to); + void SendRaidMOTD(Client *c); + void SendRaidMOTD(); + void SendRaidMOTDToWorld(); void QueuePacket(const EQApplicationPacket *app, bool ack_req = true); + // Leadership + void UpdateGroupAAs(uint32 gid); + void SaveGroupLeaderAA(uint32 gid); + void UpdateRaidAAs(); + void SaveRaidLeaderAA(); + void SendGroupLeadershipAA(Client *c, uint32 gid); + void SendGroupLeadershipAA(uint32 gid); + void SendAllRaidLeadershipAA(); + void LoadLeadership(); + inline int GetLeadershipAA(int AAID, uint32 gid = 0) + { if (AAID >= 16) return raid_aa.ranks[AAID - 16]; else return group_aa[gid].ranks[AAID]; } + inline void SetGroupAAs(uint32 gid, GroupLeadershipAA_Struct *glaa) + { memcpy(&group_aa[gid], glaa, sizeof(GroupLeadershipAA_Struct)); } + inline void SetRaidAAs(RaidLeadershipAA_Struct *rlaa) + { memcpy(&raid_aa, rlaa, sizeof(RaidLeadershipAA_Struct)); } + + void SetGroupMentor(uint32 group_id, int percent, char *name); + void ClearGroupMentor(uint32 group_id); + void CheckGroupMentor(uint32 group_id, Client *c); // this just checks if we should be fixing the pointer in group mentor struct on zone + inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; } + inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; } + RaidMember members[MAX_RAID_MEMBERS]; char leadername[64]; protected: @@ -206,6 +242,11 @@ protected: uint32 LootType; bool disbandCheck; bool forceDisband; + std::string motd; + RaidLeadershipAA_Struct raid_aa; + GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]; + + GroupMentor group_mentor[MAX_RAID_GROUPS]; }; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 904dc41b3..f3527b997 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -26,6 +26,7 @@ #include "string_ids.h" #include "../common/misc_functions.h" #include "../common/rulesys.h" +#include "../common/string_util.h" int Mob::GetKickDamage() { @@ -346,15 +347,23 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { ReuseTime = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction; //Live AA - Technique of Master Wu - uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if (bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0, 99))) { - - int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; - MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); - - // always 1/4 of the double attack chance, 25% at rank 5 (100/4) - if ((bDoubleSpecialAttack / 4) > MakeRandomInt(0, 99)) - MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { + if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) { + int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; + int extra = 1; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + if (wuchance / 4 > MakeRandomInt(0, 99)) + extra++; + // They didn't add a string ID for this. + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + // live uses 400 here -- not sure if it's the best for all clients though + SendColoredText(400, msg); + while (extra) { + MonkSpecialAttack(GetTarget(), MonkSPA[MakeRandomInt(0, 4)]); + extra--; + } + } } if(ReuseTime < 100) { @@ -1412,11 +1421,7 @@ void NPC::DoClassAttacks(Mob *target) { if(!ca_time) return; - float HasteModifier = 0; - if (GetHaste()) - HasteModifier = 10000 / (100 + GetHaste()); - else - HasteModifier = 100; + float HasteModifier = GetHaste() * 0.01f; int level = GetLevel(); int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will @@ -1568,7 +1573,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - classattack_timer.Start(reuse*HasteModifier/100); + classattack_timer.Start(reuse / HasteModifier); } void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) @@ -1592,20 +1597,13 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } int ReuseTime = 0; - int ClientHaste = GetHaste(); - int HasteMod = 0; + float HasteMod = GetHaste() * 0.01f; - if(ClientHaste >= 0){ - HasteMod = (10000/(100+ClientHaste)); //+100% haste = 2x as many attacks - } - else{ - HasteMod = (100-ClientHaste); //-100% haste = 1/2 as many attacks - } int32 dmg = 0; uint16 skill_to_use = -1; - if (skill == -1){ + if (skill == -1){ switch(GetClass()){ case WARRIOR: case RANGER: @@ -1677,8 +1675,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } } - ReuseTime = BashReuseTime-1; - ReuseTime = (ReuseTime*HasteMod)/100; + ReuseTime = (BashReuseTime - 1) / HasteMod; DoSpecialAttackDamage(ca_target, SkillBash, dmg, 1,-1,ReuseTime); @@ -1705,8 +1702,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) if (min_dmg > max_dmg) max_dmg = min_dmg; - ReuseTime = FrenzyReuseTime-1; - ReuseTime = (ReuseTime*HasteMod)/100; + ReuseTime = (FrenzyReuseTime - 1) / HasteMod; //Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit. while(AtkRounds > 0) { @@ -1756,18 +1752,21 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) return; //Live AA - Technique of Master Wu - uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - if( bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0,100))) { - - int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; - MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]); - - int TripleChance = 25; - if (bDoubleSpecialAttack > 100) - TripleChance += TripleChance*(100-bDoubleSpecialAttack)/100; - - if(TripleChance > MakeRandomInt(0,100)) { - MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0,4)]); + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + if (wuchance) { + if (wuchance >= 100 || wuchance > MakeRandomInt(0, 99)) { + int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; + int extra = 1; + if (wuchance / 4 > MakeRandomInt(0, 99)) + extra++; + // They didn't add a string ID for this. + std::string msg = StringFormat("The spirit of Master Wu fills you! You gain %d additional attack(s).", extra); + // live uses 400 here -- not sure if it's the best for all clients though + SendColoredText(400, msg); + while (extra) { + MonkSpecialAttack(ca_target, MonkSPA[MakeRandomInt(0, 4)]); + extra--; + } } } } @@ -1781,7 +1780,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) TryBackstab(ca_target,ReuseTime); } - ReuseTime = (ReuseTime*HasteMod)/100; + ReuseTime = ReuseTime / HasteMod; if(ReuseTime > 0 && !IsRiposte){ p_timers.Start(pTimerCombatAbility, ReuseTime); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index f0bc8498b..41e8f1e50 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -188,6 +188,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (!IsPowerDistModSpell(spell_id)) SetSpellPowerDistanceMod(0); + + bool SE_SpellTrigger_HasCast = false; // iterate through the effects in the spell for (i = 0; i < EFFECT_COUNT; i++) @@ -1265,10 +1267,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value); #endif - if (spells[spell_id].base[i] == 1) + // this should catch the cures + if (BeneficialSpell(spell_id) && spells[spell_id].buffduration == 0) BuffFadeByEffect(SE_Blind); - // handled by client - // TODO: blind flag? + else if (!IsClient()) + CalculateNewFearpoint(); break; } @@ -2729,6 +2732,25 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) Message(10, "The power of your next illusion spell will flow to your grouped target in your place."); break; } + + case SE_ApplyEffect: { + + if (caster && IsValidSpell(spells[spell_id].base2[i])){ + + if(MakeRandomInt(0, 100) <= spells[spell_id].base[i]) + caster->SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff); + } + break; + } + + case SE_SpellTrigger: { + + if (!SE_SpellTrigger_HasCast) { + if (caster && caster->TrySpellTrigger(this, spell_id, i)) + SE_SpellTrigger_HasCast = true; + } + break; + } // Handled Elsewhere case SE_ImmuneFleeing: @@ -2839,8 +2861,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_HealRate: case SE_SkillDamageTaken: case SE_FcSpellVulnerability: - case SE_SpellTrigger: - case SE_ApplyEffect: case SE_FcTwincast: case SE_DelayDeath: case SE_CastOnFadeEffect: @@ -3185,55 +3205,55 @@ snare has both of them negative, yet their range should work the same: case 124: // check sign result = ubase; if (caster_level > 50) - result += caster_level - 50; + result += updownsign * (caster_level - 50); break; case 125: // check sign result = ubase; if (caster_level > 50) - result += 2 * (caster_level - 50); + result += updownsign * 2 * (caster_level - 50); break; case 126: // check sign result = ubase; if (caster_level > 50) - result += 3 * (caster_level - 50); + result += updownsign * 3 * (caster_level - 50); break; case 127: // check sign result = ubase; if (caster_level > 50) - result += 4 * (caster_level - 50); + result += updownsign * 4 * (caster_level - 50); break; case 128: // check sign result = ubase; if (caster_level > 50) - result += 5 * (caster_level - 50); + result += updownsign * 5 * (caster_level - 50); break; case 129: // check sign result = ubase; if (caster_level > 50) - result += 10 * (caster_level - 50); + result += updownsign * 10 * (caster_level - 50); break; case 130: // check sign result = ubase; if (caster_level > 50) - result += 15 * (caster_level - 50); + result += updownsign * 15 * (caster_level - 50); break; case 131: // check sign result = ubase; if (caster_level > 50) - result += 20 * (caster_level - 50); + result += updownsign * 20 * (caster_level - 50); break; case 132: // check sign result = ubase; if (caster_level > 50) - result += 25 * (caster_level - 50); + result += updownsign * 25 * (caster_level - 50); break; case 137: // used in berserker AA desperation @@ -3375,7 +3395,10 @@ void Mob::BuffProcess() { if(buffs[buffs_i].UpdateClient == true) { - CastToClient()->SendBuffDurationPacket(buffs[buffs_i].spellid, buffs[buffs_i].ticsremaining, buffs[buffs_i].casterlevel); + CastToClient()->SendBuffDurationPacket(buffs[buffs_i]); + // Hack to get UF to play nicer, RoF seems fine without it + if (CastToClient()->GetClientVersion() == EQClientUnderfoot && buffs[buffs_i].numhits > 0) + CastToClient()->SendBuffNumHitPacket(buffs[buffs_i], buffs_i); buffs[buffs_i].UpdateClient = false; } } @@ -3976,6 +3999,11 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) break; } + case SE_Blind: + if (curfp && !FindType(SE_Fear)) + curfp = false; + break; + case SE_Fear: { if(RuleB(Combat, EnableFearPathing)){ @@ -4140,6 +4168,12 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) safe_delete(outapp); } + if (IsNPC()) { + EQApplicationPacket *outapp = MakeBuffsPacket(); + entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater, true); + safe_delete(outapp); + } + if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { EQApplicationPacket *outapp = MakeBuffsPacket(false); @@ -5534,92 +5568,85 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id) uint32 buff_max = GetMaxTotalSlots(); //Spell specific procs [Type 7,10,11] - if (IsValidSpell(spell_id)){ - - for(uint32 d = 0; d < buff_max; d++) { - - if((buffs[d].spellid == spell_id) && (buffs[d].numhits > 0) && (spells[buffs[d].spellid].numhitstype == type)){ - - if(--buffs[d].numhits == 0) { + if (IsValidSpell(spell_id)) { + for (uint32 d = 0; d < buff_max; d++) { + if (buffs[d].spellid == spell_id && buffs[d].numhits > 0 && + spells[buffs[d].spellid].numhitstype == type) { + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); - if(!TryFadeEffect(d)) + if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[d], d); } } } - } - - else if (type == 7){ - if (buff_slot > 0){ - - if(--buffs[buff_slot].numhits == 0) { + } else if (type == 7) { + if (buff_slot > 0) { + if (--buffs[buff_slot].numhits == 0) { CastOnNumHitFade(buffs[buff_slot].spellid); - if(!TryFadeEffect(buff_slot)) + if (!TryFadeEffect(buff_slot)) BuffFadeBySlot(buff_slot , true); - } + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[buff_slot], buff_slot); } - - else { - - for(int d = 0; d < buff_max; d++) { - - if(!m_spellHitsLeft[d]) + } else { + for (int d = 0; d < buff_max; d++) { + if (!m_spellHitsLeft[d]) continue; - if ((IsValidSpell(buffs[d].spellid)) && (m_spellHitsLeft[d] == buffs[d].spellid)) { - if(--buffs[d].numhits == 0) { + if (IsValidSpell(buffs[d].spellid) && m_spellHitsLeft[d] == buffs[d].spellid) { + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); m_spellHitsLeft[d] = 0; - if(!TryFadeEffect(d)) + if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[d], d); } } } } - } - - - else{ - - for(uint32 d = 0; d < buff_max; d++) { - - if((IsValidSpell(buffs[d].spellid)) && (buffs[d].numhits > 0) && (spells[buffs[d].spellid].numhitstype == type)){ - - if(--buffs[d].numhits == 0) { + } else { + for (uint32 d = 0; d < buff_max; d++) { + if (IsValidSpell(buffs[d].spellid) && buffs[d].numhits > 0 && + spells[buffs[d].spellid].numhitstype == type) { + if (--buffs[d].numhits == 0) { CastOnNumHitFade(buffs[d].spellid); - if(!TryFadeEffect(d)){ + if (!TryFadeEffect(d)) BuffFadeBySlot(d, true); - } + } else if (IsClient()) { // still have numhits and client, update + CastToClient()->SendBuffNumHitPacket(buffs[d], d); } - } } } } //for some stupid reason SK procs return theirs one base off... -uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) { +uint16 Mob::GetProcID(uint16 spell_id, uint8 effect_index) +{ + if (!RuleB(Spells, SHDProcIDOffByOne)) // UF+ spell files + return spells[spell_id].base[effect_index]; + + // We should actually just be checking if the mob is SHD, but to not force + // custom servers to create new spells, we will still do this bool sk = false; bool other = false; - for(int x = 0; x < 16; x++) - { - if(x == 4){ - if(spells[spell_id].classes[4] < 255) + for (int x = 0; x < 16; x++) { + if (x == 4) { + if (spells[spell_id].classes[4] < 255) sk = true; - } - else{ - if(spells[spell_id].classes[x] < 255) + } else { + if (spells[spell_id].classes[x] < 255) other = true; } } - if(sk && !other) - { - return(spells[spell_id].base[effect_index] + 1); - } - else{ - return(spells[spell_id].base[effect_index]); - } + if (sk && !other) + return spells[spell_id].base[effect_index] + 1; + else + return spells[spell_id].base[effect_index]; } bool Mob::TryDivineSave() @@ -6453,8 +6480,12 @@ void Mob::ResourceTap(int32 damage, uint16 spellid){ if (spells[spellid].max[i] && (damage > spells[spellid].max[i])) damage = spells[spellid].max[i]; - if (spells[spellid].base2[i] == 0) //HP Tap - SetHP((GetHP()+ damage)); + if (spells[spellid].base2[i] == 0){ //HP Tap + if (damage > 0) + HealDamage(damage); + else + Damage(this, -damage,0, SkillEvocation,false); + } if (spells[spellid].base2[i] == 1) //Mana Tap SetMana(GetMana() + damage); diff --git a/zone/spells.cpp b/zone/spells.cpp index b54b0b66c..50eb9479f 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2215,6 +2215,12 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot); if(itm && itm->GetItem()->RecastDelay > 0){ CastToClient()->GetPTimers().Start((pTimerItemStart + itm->GetItem()->RecastType), itm->GetItem()->RecastDelay); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); + ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; + ird->recast_delay = itm->GetItem()->RecastDelay; + ird->recast_type = itm->GetItem()->RecastType; + CastToClient()->QueuePacket(outapp); + safe_delete(outapp); } } @@ -3122,6 +3128,12 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid safe_delete(outapp); } + if (IsNPC()) { + EQApplicationPacket *outapp = MakeBuffsPacket(); + entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater, true); + safe_delete(outapp); + } + // recalculate bonuses since we stripped/added buffs CalcBonuses(); @@ -3606,108 +3618,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r spell_effectiveness = 100; } - // Recourse means there is a spell linked to that spell in that the recourse spell will - // be automatically casted on the casters group or the caster only depending on Targettype - // this is for things like dark empathy, shadow vortex - int recourse_spell=0; - recourse_spell = spells[spell_id].RecourseLink; - if(recourse_spell) - { - if(spells[recourse_spell].targettype == ST_Group || spells[recourse_spell].targettype == ST_GroupTeleport) - { - if(IsGrouped()) - { - Group *g = entity_list.GetGroupByMob(this); - if(g) - g->CastGroupSpell(this, recourse_spell); - else{ - SpellOnTarget(recourse_spell, this); -#ifdef GROUP_BUFF_PETS - if (GetPet()) - SpellOnTarget(recourse_spell, GetPet()); -#endif - } - } - else if(IsRaidGrouped() && IsClient()) - { - Raid *r = entity_list.GetRaidByClient(CastToClient()); - uint32 gid = 0xFFFFFFFF; - if(r) - gid = r->GetGroup(GetName()); - else - gid = 13; // Forces ungrouped spell casting - - if(gid < 12) - { - r->CastGroupSpell(this, recourse_spell, gid); - } - else{ - SpellOnTarget(recourse_spell, this); -#ifdef GROUP_BUFF_PETS - if (GetPet()) - SpellOnTarget(recourse_spell, GetPet()); -#endif - } - } - else if(HasOwner()) - { - if(GetOwner()->IsGrouped()) - { - Group *g = entity_list.GetGroupByMob(GetOwner()); - if(g) - g->CastGroupSpell(this, recourse_spell); - else{ - SpellOnTarget(recourse_spell, GetOwner()); - SpellOnTarget(recourse_spell, this); - } - } - else if(GetOwner()->IsRaidGrouped() && GetOwner()->IsClient()) - { - Raid *r = entity_list.GetRaidByClient(GetOwner()->CastToClient()); - uint32 gid = 0xFFFFFFFF; - if(r) - gid = r->GetGroup(GetOwner()->GetName()); - else - gid = 13; // Forces ungrouped spell casting - - if(gid < 12) - { - r->CastGroupSpell(this, recourse_spell, gid); - } - else - { - SpellOnTarget(recourse_spell, GetOwner()); - SpellOnTarget(recourse_spell, this); - } - } - else - { - SpellOnTarget(recourse_spell, GetOwner()); - SpellOnTarget(recourse_spell, this); - } - } - else - { - SpellOnTarget(recourse_spell, this); -#ifdef GROUP_BUFF_PETS - if (GetPet()) - SpellOnTarget(recourse_spell, GetPet()); -#endif - } - - } - else - { - SpellOnTarget(recourse_spell, this); - } - } - if(spelltar->spellbonuses.SpellDamageShield && IsDetrimentalSpell(spell_id)) spelltar->DamageShield(this, true); - TrySpellTrigger(spelltar, spell_id); - TryApplyEffect(spelltar, spell_id); - if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) { int32 aggro_amount = CheckAggroAmount(spell_id, isproc); mlog(SPELLS__CASTING, "Spell %d cast on %s generated %d hate", spell_id, spelltar->GetName(), aggro_amount); @@ -3746,7 +3659,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r return false; } - + if (IsValidSpell(spells[spell_id].RecourseLink)) + SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff); + if (IsDetrimentalSpell(spell_id)) { CheckNumHitsRemaining(NUMHIT_OutgoingSpells); @@ -4817,8 +4732,8 @@ void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message) if(send_message) { const char *fadetext = spells[spell_id].spell_fades; - outapp = new EQApplicationPacket(OP_BuffFadeMsg, sizeof(BuffFadeMsg_Struct) + strlen(fadetext)); - BuffFadeMsg_Struct *bfm = (BuffFadeMsg_Struct *) outapp->pBuffer; + outapp = new EQApplicationPacket(OP_ColoredText, sizeof(ColoredText_Struct) + strlen(fadetext)); + ColoredText_Struct *bfm = (ColoredText_Struct *) outapp->pBuffer; bfm->color = MT_Spells; memcpy(bfm->msg, fadetext, strlen(fadetext)); QueuePacket(outapp); @@ -4841,6 +4756,8 @@ void Client::MemSpell(uint16 spell_id, int slot, bool update_client) m_pp.mem_spells[slot] = spell_id; mlog(CLIENT__SPELLS, "Spell %d memorized into slot %d", spell_id, slot); + database.SaveCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + if(update_client) { MemorizeSpell(slot, spell_id, memSpellMemorize); @@ -4855,6 +4772,8 @@ void Client::UnmemSpell(int slot, bool update_client) mlog(CLIENT__SPELLS, "Spell %d forgotten from slot %d", m_pp.mem_spells[slot], slot); m_pp.mem_spells[slot] = 0xFFFFFFFF; + database.DeleteCharacterMemorizedSpell(this->CharacterID(), m_pp.mem_spells[slot], slot); + if(update_client) { MemorizeSpell(slot, m_pp.mem_spells[slot], memSpellForget); @@ -4882,6 +4801,7 @@ void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client) } m_pp.spell_book[slot] = spell_id; + database.SaveCharacterSpell(this->CharacterID(), spell_id, slot); mlog(CLIENT__SPELLS, "Spell %d scribed into spell book slot %d", spell_id, slot); if(update_client) @@ -4897,7 +4817,8 @@ void Client::UnscribeSpell(int slot, bool update_client) mlog(CLIENT__SPELLS, "Spell %d erased from spell book slot %d", m_pp.spell_book[slot], slot); m_pp.spell_book[slot] = 0xFFFFFFFF; - + + database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot); if(update_client) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct)); @@ -4925,8 +4846,9 @@ void Client::UntrainDisc(int slot, bool update_client) if(slot >= MAX_PP_DISCIPLINES || slot < 0) return; - mlog(CLIENT__SPELLS, "Discipline %d untrained from slot %d", m_pp.disciplines.values[slot], slot); + mlog(CLIENT__SPELLS, "Discipline %d untrained from slot %d", m_pp.disciplines.values[slot], slot); m_pp.disciplines.values[slot] = 0; + database.DeleteCharacterDisc(this->CharacterID(), slot); if(update_client) { @@ -5241,20 +5163,40 @@ void Mob::_StopSong() //Thus I use this in the buff process to update the correct duration once after casting //this allows AAs and focus effects that increase buff duration to work correctly, but could probably //be used for other things as well -void Client::SendBuffDurationPacket(uint16 spell_id, int duration, int inlevel) +void Client::SendBuffDurationPacket(Buffs_Struct &buff) { EQApplicationPacket* outapp; outapp = new EQApplicationPacket(OP_Buff, sizeof(SpellBuffFade_Struct)); SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) outapp->pBuffer; sbf->entityid = GetID(); - sbf->slot=2; - sbf->spellid=spell_id; - sbf->slotid=0; - sbf->effect = inlevel > 0 ? inlevel : GetLevel(); - sbf->level = inlevel > 0 ? inlevel : GetLevel(); + sbf->slot = 2; + sbf->spellid = buff.spellid; + sbf->slotid = 0; + sbf->effect = buff.casterlevel > 0 ? buff.casterlevel : GetLevel(); + sbf->level = buff.casterlevel > 0 ? buff.casterlevel : GetLevel(); sbf->bufffade = 0; - sbf->duration = duration; + sbf->duration = buff.ticsremaining; + sbf->num_hits = buff.numhits; + FastQueuePacket(&outapp); +} + +void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot) +{ + // UF+ use this packet + if (GetClientVersion() < EQClientUnderfoot) + return; + EQApplicationPacket *outapp; + outapp = new EQApplicationPacket(OP_BuffCreate, sizeof(BuffIcon_Struct) + sizeof(BuffIconEntry_Struct)); + BuffIcon_Struct *bi = (BuffIcon_Struct *)outapp->pBuffer; + bi->entity_id = GetID(); + bi->count = 1; + bi->all_buffs = 0; + + bi->entries[0].buff_slot = slot; + bi->entries[0].spell_id = buff.spellid; + bi->entries[0].tics_remaining = buff.ticsremaining; + bi->entries[0].num_hits = buff.numhits; FastQueuePacket(&outapp); } @@ -5329,6 +5271,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) BuffIcon_Struct *buff = (BuffIcon_Struct*)outapp->pBuffer; buff->entity_id = GetID(); buff->count = count; + buff->all_buffs = 1; uint32 index = 0; for(unsigned int i = 0; i < buff_count; ++i) @@ -5338,6 +5281,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) buff->entries[index].buff_slot = i; buff->entries[index].spell_id = buffs[i].spellid; buff->entries[index].tics_remaining = buffs[i].ticsremaining; + buff->entries[index].num_hits = buffs[i].numhits; ++index; } } @@ -5355,7 +5299,7 @@ void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration) buffs[i].ticsremaining = newDuration; if(IsClient()) { - CastToClient()->SendBuffDurationPacket(buffs[i].spellid, buffs[i].ticsremaining, buffs[i].casterlevel); + CastToClient()->SendBuffDurationPacket(buffs[i]); } } } @@ -5406,3 +5350,19 @@ void NPC::UninitializeBuffSlots() safe_delete_array(buffs); } +void Client::SendSpellAnim(uint16 targetid, uint16 spell_id) +{ + if (!targetid || !IsValidSpell(spell_id)) + return; + + EQApplicationPacket app(OP_Action, sizeof(Action_Struct)); + Action_Struct* a = (Action_Struct*)app.pBuffer; + a->target = targetid; + a->source = this->GetID(); + a->type = 231; + a->spell = spell_id; + a->sequence = 231; + + app.priority = 1; + entity_list.QueueCloseClients(this, &app); +} diff --git a/zone/string_ids.h b/zone/string_ids.h index 056513874..41e0960b0 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -163,6 +163,8 @@ #define CANNOT_WAKE 555 //%1 tells you, 'I am unable to wake %2, master.' #define GUILD_NAME_IN_USE 711 //You cannot create a guild with that name, that guild already exists on this server. #define GM_GAINXP 1002 //[GM] You have gained %1 AXP and %2 EXP (%3). +#define MALE_SLAYUNDEAD 1007 //%1's holy blade cleanses his target!(%2) +#define FEMALE_SLAYUNDEAD 1008 //%1's holy blade cleanses her target!(%2) #define FINISHING_BLOW 1009 //%1 scores a Finishing Blow!! #define ASSASSINATES 1016 //%1 ASSASSINATES their victim!! #define CRIPPLING_BLOW 1021 //%1 lands a Crippling Blow!(%2) @@ -249,6 +251,8 @@ #define PLAYER_CHARMED 1461 //You lose control of yourself! #define TRADER_BUSY 1468 //That Trader is currently with a customer. Please wait until their transaction is finished. #define SENSE_CORPSE_DIRECTION 1563 //You sense a corpse in this direction. +#define QUEUED_TELL 2458 //[queued] +#define QUEUE_TELL_FULL 2459 //[zoing and queue is full] #define SUSPEND_MINION_UNSUSPEND 3267 //%1 tells you, 'I live again...' #define SUSPEND_MINION_SUSPEND 3268 //%1 tells you, 'By your command, master.' #define ONLY_SUMMONED_PETS 3269 //3269 This effect only works with summoned pets. @@ -269,8 +273,11 @@ #define CORPSEDRAG_STOP 4066 //You stop dragging the corpse. #define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away. #define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters. +#define TELL_QUEUED_MESSAGE 5045 //You told %1 '%T2. %3' +#define TOLD_NOT_ONLINE 5046 //%1 is not online at this time. #define PETITION_NO_DELETE 5053 //You do not have a petition in the queue. #define PETITION_DELETED 5054 //Your petition was successfully deleted. +#define ALREADY_IN_RAID 5060 //%1 is already in a raid. #define GAIN_RAIDEXP 5085 //You gained raid experience! #define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there. #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 3da32c03c..924d3770b 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -60,41 +60,32 @@ TaskManager::~TaskManager() { } bool TaskManager::LoadTaskSets() { - const char *TaskSetQuery = "SELECT `id`, `taskid` from `tasksets` WHERE `id` > 0 AND `id` < %i " - "AND `taskid` >= 0 AND `taskid` < %i ORDER BY `id`, `taskid` ASC"; - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTaskSets: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; // Clear all task sets in memory. Done so we can reload them on the fly if required by just calling // this method again. - for(int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `id`, `taskid` from `tasksets` " + "WHERE `id` > 0 AND `id` < %i " + "AND `taskid` >= 0 AND `taskid` < %i " + "ORDER BY `id`, `taskid` ASC", + MAXTASKSETS, MAXTASKS); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadTaskSets: %s", results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + int taskSet = atoi(row[0]); + int taskID = atoi(row[1]); + + TaskSets[taskSet].push_back(taskID); + _log(TASKS__GLOBALLOAD, "Adding TaskID %4i to TaskSet %4i", taskID, taskSet); + } return true; - } bool TaskManager::LoadSingleTask(int TaskID) { @@ -126,203 +117,177 @@ void TaskManager::ReloadGoalLists() { _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); } -bool TaskManager::LoadTasks(int SingleTask) { +bool TaskManager::LoadTasks(int singleTask) { // If TaskID !=0, then just load the task specified. - - const char *AllTaskQuery = "SELECT `id`, `duration`, `title`, `description`, `reward`, `rewardid`," - "`cashreward`, `xpreward`, `rewardmethod`, `startzone`, `minlevel`, `maxlevel`, `repeatable` " - "from `tasks` WHERE `id` < %i"; - - const char *SingleTaskQuery = "SELECT `id`, `duration`, `title`, `description`, `reward`, `rewardid`," - "`cashreward`, `xpreward`, `rewardmethod`, `startzone`, `minlevel`, `maxlevel`, `repeatable` " - "from `tasks` WHERE `id` = %i"; - - const char *AllActivityQuery = "SELECT `taskid`, `step`, `activityid`, `activitytype`, `text1`, `text2`," - "`text3`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, " - "`zoneid`, `optional` from `activities` WHERE " - "`taskid` < %i AND `activityid` < %i ORDER BY taskid, activityid ASC"; - - const char *SingleTaskActivityQuery = "SELECT `taskid`, `step`, `activityid`, `activitytype`, `text1`, `text2`," - "`text3`, `goalid`, `goalmethod`, `goalcount`, `delivertonpc`, " - "`zoneid`, `optional` from `activities` WHERE " - "`taskid` = %i AND `activityid` < %i ORDER BY taskid, activityid ASC"; - - const char *ERR_TASK_OOR = "[TASKS]Task ID %i out of range while loading tasks from database"; - - const char *ERR_TASK_OR_ACTIVITY_OOR = "[TASKS]Task or Activity ID (%i, %i) out of range while loading" - "activities from database"; - - const char *ERR_NOTASK = "[TASKS]Activity for non-existent task (%i, %i) while loading activities from database"; - - const char *ERR_SEQERR = "[TASKS]Activities for Task %i are not sequential starting at 0. Not loading task."; - - const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int QueryLength = 0; - MYSQL_RES *result; - MYSQL_ROW row; - _log(TASKS__GLOBALLOAD, "TaskManager::LoadTasks Called"); - if(SingleTask == 0) { + std::string query; + if(singleTask == 0) { if(!GoalListManager.LoadLists()) _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadLists failed"); if(!LoadTaskSets()) _log(TASKS__GLOBALLOAD,"TaskManager::LoadTasks LoadTaskSets failed"); - QueryLength = MakeAnyLenString(&query,AllTaskQuery,MAXTASKS); + query = StringFormat("SELECT `id`, `duration`, `title`, `description`, `reward`, " + "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, " + "`startzone`, `minlevel`, `maxlevel`, `repeatable` " + "FROM `tasks` WHERE `id` < %i", MAXTASKS); } else - QueryLength = MakeAnyLenString(&query,SingleTaskQuery,SingleTask); + query = StringFormat("SELECT `id`, `duration`, `title`, `description`, `reward`, " + "`rewardid`, `cashreward`, `xpreward`, `rewardmethod`, " + "`startzone`, `minlevel`, `maxlevel`, `repeatable` " + "FROM `tasks` WHERE `id` = %i",singleTask); - if(database.RunQuery(query,QueryLength,errbuf,&result)) { + const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::LoadTasks: %s"; - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - if((TaskID <= 0) || (TaskID >= MAXTASKS)) { - // This shouldn't happen, as the SELECT is bounded by MAXTASKS - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR, TaskID); - continue; - } - Tasks[TaskID] = new TaskInformation; - Tasks[TaskID]->Duration = atoi(row[1]); - Tasks[TaskID]->Title = new char[strlen(row[2]) + 1]; - strcpy(Tasks[TaskID]->Title, row[2]); - Tasks[TaskID]->Description = new char[strlen(row[3]) + 1]; - strcpy(Tasks[TaskID]->Description, row[3]); - Tasks[TaskID]->Reward = new char[strlen(row[4]) + 1]; - strcpy(Tasks[TaskID]->Reward, row[4]); - Tasks[TaskID]->RewardID = atoi(row[5]); - Tasks[TaskID]->CashReward = atoi(row[6]); - Tasks[TaskID]->XPReward = atoi(row[7]); - Tasks[TaskID]->RewardMethod = (TaskMethodType)atoi(row[8]); - Tasks[TaskID]->StartZone = atoi(row[9]); - Tasks[TaskID]->MinLevel = atoi(row[10]); - Tasks[TaskID]->MaxLevel = atoi(row[11]); - Tasks[TaskID]->Repeatable = atoi(row[12]); - Tasks[TaskID]->ActivityCount = 0; - Tasks[TaskID]->SequenceMode = ActivitiesSequential; - Tasks[TaskID]->LastStep = 0; - - _log(TASKS__GLOBALLOAD,"TaskID: %5i, Duration: %8i, StartZone: %3i Reward: %s MinLevel %i MaxLevel %i Repeatable: %s", - TaskID, Tasks[TaskID]->Duration, Tasks[TaskID]->StartZone, Tasks[TaskID]->Reward, - Tasks[TaskID]->MinLevel, Tasks[TaskID]->MaxLevel, - Tasks[TaskID]->Repeatable ? "Yes" : "No"); - _log(TASKS__GLOBALLOAD,"Title: %s ", Tasks[TaskID]->Title); - //_log(TASKS__GLOBALLOAD,"Description: %s ", Tasks[TaskID]->Description); - - } - mysql_free_result(result); - safe_delete_array(query); - - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; - } + } - if(SingleTask==0) - QueryLength = MakeAnyLenString(&query,AllActivityQuery,MAXTASKS, MAXACTIVITIESPERTASK); + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + + if((taskID <= 0) || (taskID >= MAXTASKS)) { + // This shouldn't happen, as the SELECT is bounded by MAXTASKS + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading tasks from database", taskID); + continue; + } + + Tasks[taskID] = new TaskInformation; + Tasks[taskID]->Duration = atoi(row[1]); + Tasks[taskID]->Title = new char[strlen(row[2]) + 1]; + strcpy(Tasks[taskID]->Title, row[2]); + Tasks[taskID]->Description = new char[strlen(row[3]) + 1]; + strcpy(Tasks[taskID]->Description, row[3]); + Tasks[taskID]->Reward = new char[strlen(row[4]) + 1]; + strcpy(Tasks[taskID]->Reward, row[4]); + Tasks[taskID]->RewardID = atoi(row[5]); + Tasks[taskID]->CashReward = atoi(row[6]); + Tasks[taskID]->XPReward = atoi(row[7]); + Tasks[taskID]->RewardMethod = (TaskMethodType)atoi(row[8]); + Tasks[taskID]->StartZone = atoi(row[9]); + Tasks[taskID]->MinLevel = atoi(row[10]); + Tasks[taskID]->MaxLevel = atoi(row[11]); + Tasks[taskID]->Repeatable = atoi(row[12]); + Tasks[taskID]->ActivityCount = 0; + Tasks[taskID]->SequenceMode = ActivitiesSequential; + Tasks[taskID]->LastStep = 0; + + _log(TASKS__GLOBALLOAD,"TaskID: %5i, Duration: %8i, StartZone: %3i Reward: %s MinLevel %i MaxLevel %i Repeatable: %s", + taskID, Tasks[taskID]->Duration, Tasks[taskID]->StartZone, Tasks[taskID]->Reward, + Tasks[taskID]->MinLevel, Tasks[taskID]->MaxLevel, + Tasks[taskID]->Repeatable ? "Yes" : "No"); + _log(TASKS__GLOBALLOAD,"Title: %s ", Tasks[taskID]->Title); + } + + + if(singleTask==0) + query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " + "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zoneid`, `optional` " + "FROM `activities` " + "WHERE `taskid` < %i AND `activityid` < %i " + "ORDER BY taskid, activityid ASC", MAXTASKS, MAXACTIVITIESPERTASK); else - QueryLength = MakeAnyLenString(&query,SingleTaskActivityQuery, SingleTask, MAXACTIVITIESPERTASK); - - if(database.RunQuery(query,QueryLength, errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - int Step = atoi(row[1]); - - int ActivityID = atoi(row[2]); - - if((TaskID <= 0) || (TaskID >= MAXTASKS) || (ActivityID < 0) || (ActivityID >= MAXACTIVITIESPERTASK)) { - // This shouldn't happen, as the SELECT is bounded by MAXTASKS - LogFile->write(EQEMuLog::Error, ERR_TASK_OR_ACTIVITY_OOR, TaskID, ActivityID); - continue; - } - if(Tasks[TaskID]==nullptr) { - LogFile->write(EQEMuLog::Error, ERR_NOTASK, TaskID, ActivityID); - continue; - } - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].StepNumber = Step; - - if(Step != 0) - Tasks[TaskID]->SequenceMode = ActivitiesStepped; - - if(Step >Tasks[TaskID]->LastStep) Tasks[TaskID]->LastStep = Step; - - // Task Activities MUST be numbered sequentially from 0. If not, log an error - // and set the task to nullptr. Subsequent activities for this task will raise - // ERR_NOTASK errors. - // Change to (ActivityID != (Tasks[TaskID]->ActivityCount + 1)) to index from 1 - if(ActivityID != Tasks[TaskID]->ActivityCount) { - LogFile->write(EQEMuLog::Error, ERR_SEQERR, TaskID, ActivityID); - Tasks[TaskID] = nullptr; - continue; - } - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Type = atoi(row[3]); - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1 = new char[strlen(row[4]) + 1]; - - if(strlen(row[4])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1, row[4]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2 = new char[strlen(row[5]) + 1]; - - if(strlen(row[5])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2, row[5]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3 = new char[strlen(row[6]) + 1]; - - if(strlen(row[6])>0) - strcpy(Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3, row[6]); - else - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3[0]=0; - - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalID = atoi(row[7]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalCount = atoi(row[9]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].DeliverToNPC = atoi(row[10]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].ZoneID = atoi(row[11]); - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Optional = atoi(row[12]); - - _log(TASKS__GLOBALLOAD, "Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, " - "GoalMethod: %i, GoalCount: %3i, ZoneID:%3i", - Tasks[TaskID]->ActivityCount, ActivityID, TaskID, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Type, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalID, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalMethod, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].GoalCount, - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].ZoneID); - - _log(TASKS__GLOBALLOAD, " Text1: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text1); - _log(TASKS__GLOBALLOAD, " Text2: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text2); - _log(TASKS__GLOBALLOAD, " Text3: %s", - Tasks[TaskID]->Activity[Tasks[TaskID]->ActivityCount].Text3); - - Tasks[TaskID]->ActivityCount++; - - } - mysql_free_result(result); - safe_delete_array(query); - - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `step`, `activityid`, `activitytype`, " + "`text1`, `text2`, `text3`, `goalid`, `goalmethod`, " + "`goalcount`, `delivertonpc`, `zoneid`, `optional` " + "FROM `activities` " + "WHERE `taskid` = %i AND `activityid` < %i " + "ORDER BY taskid, activityid ASC", singleTask, MAXACTIVITIESPERTASK); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); return false; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + int step = atoi(row[1]); + + int activityID = atoi(row[2]); + + if((taskID <= 0) || (taskID >= MAXTASKS) || (activityID < 0) || (activityID >= MAXACTIVITIESPERTASK)) { + // This shouldn't happen, as the SELECT is bounded by MAXTASKS + LogFile->write(EQEMuLog::Error, "[TASKS]Task or Activity ID (%i, %i) out of range while loading " + "activities from database", taskID, activityID); + continue; + } + + if(Tasks[taskID]==nullptr) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity for non-existent task (%i, %i) while loading activities from database", taskID, activityID); + continue; + } + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].StepNumber = step; + + if(step != 0) + Tasks[taskID]->SequenceMode = ActivitiesStepped; + + if(step >Tasks[taskID]->LastStep) + Tasks[taskID]->LastStep = step; + + // Task Activities MUST be numbered sequentially from 0. If not, log an error + // and set the task to nullptr. Subsequent activities for this task will raise + // ERR_NOTASK errors. + // Change to (activityID != (Tasks[taskID]->ActivityCount + 1)) to index from 1 + if(activityID != Tasks[taskID]->ActivityCount) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activities for Task %i are not sequential starting at 0. Not loading task.", taskID, activityID); + Tasks[taskID] = nullptr; + continue; + } + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type = atoi(row[3]); + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1 = new char[strlen(row[4]) + 1]; + + if(strlen(row[4])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1, row[4]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2 = new char[strlen(row[5]) + 1]; + + if(strlen(row[5])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2, row[5]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3 = new char[strlen(row[6]) + 1]; + + if(strlen(row[6])>0) + strcpy(Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3, row[6]); + else + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3[0]=0; + + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID = atoi(row[7]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod = (TaskMethodType)atoi(row[8]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount = atoi(row[9]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].DeliverToNPC = atoi(row[10]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID = atoi(row[11]); + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Optional = atoi(row[12]); + + _log(TASKS__GLOBALLOAD, "Activity Slot %2i: ID %i for Task %5i. Type: %3i, GoalID: %8i, " + "GoalMethod: %i, GoalCount: %3i, ZoneID:%3i", + Tasks[taskID]->ActivityCount, activityID, taskID, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Type, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalID, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalMethod, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].GoalCount, + Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].ZoneID); + + _log(TASKS__GLOBALLOAD, " Text1: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text1); + _log(TASKS__GLOBALLOAD, " Text2: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text2); + _log(TASKS__GLOBALLOAD, " Text3: %s", Tasks[taskID]->Activity[Tasks[taskID]->ActivityCount].Text3); + + Tasks[taskID]->ActivityCount++; } + return true; } @@ -333,146 +298,127 @@ bool TaskManager::SaveClientState(Client *c, ClientTaskState *state) { // in that slot had more activities than the one now occupying it. Hopefully retaining the slot number for the // duration of a session will overcome this. // - const char *TaskQuery="REPLACE INTO character_tasks (charid, taskid, slot, acceptedtime) " - "VALUES (%i, %i, %i, %i)"; - - const char *ActivityQuery="REPLACE INTO character_activities (charid, taskid, activityid, donecount, completed) " - "VALUES "; - - const char *CompletedTaskQuery="REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) " - "VALUES (%i, %i, %i, %i)"; + if(!c || !state) + return false; const char *ERR_MYSQLERROR = "[TASKS]Error in TaskManager::SaveClientState %s"; - if(!c || !state) return false; + int characterID = c->CharacterID(); - int CharacterID = c->CharacterID(); - - _log(TASKS__CLIENTSAVE,"TaskManager::SaveClientState for character ID %d", CharacterID); - - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + _log(TASKS__CLIENTSAVE,"TaskManager::SaveClientState for character ID %d", characterID); if(state->ActiveTaskCount > 0) { - for(int Task=0; TaskActiveTasks[Task].TaskID; - if(TaskID==TASKSLOTEMPTY) continue; - if(state->ActiveTasks[Task].Updated) { + for(int task=0; taskActiveTasks[task].TaskID; + if(taskID==TASKSLOTEMPTY) + continue; - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", - CharacterID, Task, TaskID); + if(state->ActiveTasks[task].Updated) { - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, - CharacterID, - TaskID, - Task, - state->ActiveTasks[Task].AcceptedTime), errbuf)) { + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState for character ID %d, Updating TaskIndex %i TaskID %i", characterID, task, taskID); - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } + std::string query = StringFormat("REPLACE INTO character_tasks (charid, taskid, slot, acceptedtime) " + "VALUES (%i, %i, %i, %i)", + characterID, taskID, task, state->ActiveTasks[task].AcceptedTime); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); else - state->ActiveTasks[Task].Updated = false; + state->ActiveTasks[task].Updated = false; - safe_delete_array(query); } - int UpdatedActivityCount = 0; - std::string UpdateActivityQuery = ActivityQuery; - char *buf = 0; + std::string query = "REPLACE INTO character_activities (charid, taskid, activityid, donecount, completed) " + "VALUES "; - for(int Activity=0; ActivityActivityCount; Activity++) { + int updatedActivityCount = 0; + for(int activityIndex = 0; activityIndexActivityCount; ++activityIndex) { - if(state->ActiveTasks[Task].Activity[Activity].Updated) { + if(!state->ActiveTasks[task].Activity[activityIndex].Updated) + continue; - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientSate for character ID %d, " - "Updating Activity %i, %i", - CharacterID, Task, Activity); + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientSate for character ID %d, Updating Activity %i, %i", + characterID, task, activityIndex); - if(UpdatedActivityCount==0) { - MakeAnyLenString(&buf, "(%i, %i, %i, %i, %i)", CharacterID, TaskID, - Activity, - state->ActiveTasks[Task].Activity[Activity].DoneCount, - state->ActiveTasks[Task].Activity[Activity].State == - ActivityCompleted); - } - else { - MakeAnyLenString(&buf, ", (%i, %i, %i, %i, %i)", CharacterID, TaskID, - Activity, - state->ActiveTasks[Task].Activity[Activity].DoneCount, - state->ActiveTasks[Task].Activity[Activity].State == - ActivityCompleted); - } - UpdateActivityQuery = UpdateActivityQuery + buf; - safe_delete_array(buf); - UpdatedActivityCount++; - } + if(updatedActivityCount==0) + query += StringFormat("(%i, %i, %i, %i, %i)", + characterID, taskID, activityIndex, + state->ActiveTasks[task].Activity[activityIndex].DoneCount, + state->ActiveTasks[task].Activity[activityIndex].State == ActivityCompleted); + else + query += StringFormat(", (%i, %i, %i, %i, %i)", + characterID, taskID, activityIndex, + state->ActiveTasks[task].Activity[activityIndex].DoneCount, + state->ActiveTasks[task].Activity[activityIndex].State == ActivityCompleted); + + updatedActivityCount++; } - if(UpdatedActivityCount > 0) { - _log(TASKS__CLIENTSAVE, "Executing query %s", UpdateActivityQuery.c_str()); - if(!database.RunQuery(query,MakeAnyLenString(&query, UpdateActivityQuery.c_str()), - errbuf)) { + if(updatedActivityCount == 0) + continue; - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - else { - state->ActiveTasks[Task].Updated=false; - for(int Activity=0; ActivityActivityCount; Activity++) - state->ActiveTasks[Task].Activity[Activity].Updated=false; + _log(TASKS__CLIENTSAVE, "Executing query %s", query.c_str()); + auto results = database.QueryDatabase(query); - } + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + continue; + } + + state->ActiveTasks[task].Updated=false; + for(int activityIndex=0; activityIndexActivityCount; ++activityIndex) + state->ActiveTasks[task].Activity[activityIndex].Updated=false; - safe_delete_array(query); - } } - - } - if(RuleB(TaskSystem, RecordCompletedTasks) && - (state->CompletedTasks.size() > (unsigned int)state->LastCompletedTaskLoaded)) { - - for(unsigned int i=state->LastCompletedTaskLoaded; iCompletedTasks.size(); i++) { - - _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState Saving Completed Task at slot %i", i); - int TaskID = state->CompletedTasks[i].TaskID; - if((TaskID<=0) || (TaskID>=MAXTASKS) || (Tasks[TaskID]==nullptr)) continue; - - // First we save a record with an ActivityID of -1. - // This indicates this task was completed at the given time. We infer that all - // none optional activities were completed. - // - if(!database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID, - state->CompletedTasks[i].CompletedTime, - TaskID, -1), errbuf)) { - - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - continue; - } - safe_delete_array(query); - - // If the Rule to record non-optional task completion is not enabled, don't save it - if(!RuleB(TaskSystem, RecordCompletedOptionalActivities)) continue; - - // Insert one record for each completed optional task. - - for(int j=0; jActivityCount; j++) { - if(Tasks[TaskID]->Activity[j].Optional && state->CompletedTasks[i].ActivityDone[j]) { - - if(!database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID, - state->CompletedTasks[i].CompletedTime, - TaskID, j), errbuf)) { - - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, errbuf); - } - safe_delete_array(query); - } - } - } - state->LastCompletedTaskLoaded = state->CompletedTasks.size(); } + if(!RuleB(TaskSystem, RecordCompletedTasks) || (state->CompletedTasks.size() <= (unsigned int)state->LastCompletedTaskLoaded)) { + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); + return true; + } + + const char* completedTaskQuery = "REPLACE INTO completed_tasks (charid, completedtime, taskid, activityid) " + "VALUES (%i, %i, %i, %i)"; + + for(unsigned int i=state->LastCompletedTaskLoaded; iCompletedTasks.size(); i++) { + + _log(TASKS__CLIENTSAVE, "TaskManager::SaveClientState Saving Completed Task at slot %i", i); + int taskID = state->CompletedTasks[i].TaskID; + + if((taskID <= 0) || (taskID >= MAXTASKS) || (Tasks[taskID] == nullptr)) + continue; + + // First we save a record with an ActivityID of -1. + // This indicates this task was completed at the given time. We infer that all + // none optional activities were completed. + // + std::string query = StringFormat(completedTaskQuery, characterID, state->CompletedTasks[i].CompletedTime, taskID, -1); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + continue; + } + + // If the Rule to record non-optional task completion is not enabled, don't save it + if(!RuleB(TaskSystem, RecordCompletedOptionalActivities)) + continue; + + // Insert one record for each completed optional task. + + for(int j=0; jActivityCount; j++) { + if(!Tasks[taskID]->Activity[j].Optional || !state->CompletedTasks[i].ActivityDone[j]) + continue; + + query = StringFormat(completedTaskQuery, characterID, state->CompletedTasks[i].CompletedTime, taskID, j); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, results.ErrorMessage().c_str()); + + } + + } + + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); return true; } @@ -505,329 +451,261 @@ void Client::RemoveClientTaskState() { bool TaskManager::LoadClientState(Client *c, ClientTaskState *state) { - const char *TaskQuery = "SELECT `taskid`, `slot`, `acceptedtime` from `character_tasks` " - "WHERE `charid` = %i ORDER BY acceptedtime"; + if(!c || !state) + return false; - const char *ERR_TASK_OOR1 = "[TASKS]Task ID %i out of range while loading character tasks from database"; - - const char *ERR_SLOT_OOR = "[TASKS] Slot %i out of range while loading character tasks from database"; - - const char *ERR_DUP_SLOT = "[TASKS] Slot %i for Task %is is already occupied."; - - const char *ERR_MYSQLERROR1 = "[TASKS]Error in TaskManager::LoadClientState load Tasks: %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(!c || !state) return false; - - int CharacterID = c->CharacterID(); + int characterID = c->CharacterID(); state->ActiveTaskCount = 0; - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientSate for character ID %d", CharacterID); + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState for character ID %d", characterID); - if(database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, CharacterID), errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - - int TaskID = atoi(row[0]); - int Slot = atoi(row[1]); - - if((TaskID<0) || (TaskID>=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR1, TaskID); - continue; - } - - if((Slot<0) || (Slot>=MAXACTIVETASKS)) { - LogFile->write(EQEMuLog::Error, ERR_SLOT_OOR, Slot); - continue; - } - - if(state->ActiveTasks[Slot].TaskID != TASKSLOTEMPTY) { - LogFile->write(EQEMuLog::Error, ERR_DUP_SLOT, Slot, TaskID); - continue; - } - - int acceptedtime = atoi(row[2]); - - state->ActiveTasks[Slot].TaskID = TaskID; - - state->ActiveTasks[Slot].CurrentStep = -1; - - state->ActiveTasks[Slot].AcceptedTime = acceptedtime; - - state->ActiveTasks[Slot].Updated = false; - - for(int i=0; iActiveTasks[Slot].Activity[i].ActivityID = -1; - } - - //LoadClientActivitiesForTask(CharacterID, &state->ActiveTasks[state->ActiveTaskCount]); - // Calculate which activities are active based on those completed. - //state->UnlockActivities(state->ActiveTaskCount); - - state->ActiveTaskCount++; - - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, " - "Accepted Time: %8X", - CharacterID, TaskID,acceptedtime); - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR1, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `taskid`, `slot`, `acceptedtime` " + "FROM `character_tasks` " + "WHERE `charid` = %i ORDER BY acceptedtime", characterID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load Tasks: %s", results.ErrorMessage().c_str()); return false; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + int slot = atoi(row[1]); + + if((taskID<0) || (taskID>=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading character tasks from database", taskID); + continue; + } + + if((slot<0) || (slot>=MAXACTIVETASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS] Slot %i out of range while loading character tasks from database", slot); + continue; + } + + if(state->ActiveTasks[slot].TaskID != TASKSLOTEMPTY) { + LogFile->write(EQEMuLog::Error, "[TASKS] Slot %i for Task %is is already occupied.", slot, taskID); + continue; + } + + int acceptedtime = atoi(row[2]); + + state->ActiveTasks[slot].TaskID = taskID; + state->ActiveTasks[slot].CurrentStep = -1; + state->ActiveTasks[slot].AcceptedTime = acceptedtime; + state->ActiveTasks[slot].Updated = false; + + for(int i=0; iActiveTasks[slot].Activity[i].ActivityID = -1; + + ++state->ActiveTaskCount; + + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, Accepted Time: %8X", characterID, taskID, acceptedtime); } // Load Activities + _log(TASKS__CLIENTLOAD, "LoadClientState. Loading activities for character ID %d", characterID); - const char *ActivityQuery = "SELECT `taskid`, `activityid`, `donecount`, `completed` " - " from `character_activities` WHERE `charid` = %i " - "ORDER BY `taskid` ASC, `activityid` ASC"; - - const char *ERR_TASK_OOR2 = "[TASKS]Task ID %i out of range while loading character activities from database"; - - const char *ERR_ACTIVITY_OOR = "[TASKS]Activity ID %i out of range while loading character activities from database"; - - const char *ERR_NOTASK = "[TASKS]Activity %i found for task %i which client does not have."; - - const char *ERR_MYSQLERROR2 = "[TASKS]Error in TaskManager::LoadClientState load Activities: %s"; - - _log(TASKS__CLIENTLOAD, "LoadClientState. Loading activities for character ID %d", CharacterID); - - - - if(database.RunQuery(query,MakeAnyLenString(&query, ActivityQuery, - CharacterID), errbuf, &result)) { - - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - if((TaskID<0) || (TaskID>=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR2, TaskID); - continue; - } - int ActivityID = atoi(row[1]); - if((ActivityID<0) || (ActivityID>=MAXACTIVITIESPERTASK)) { - LogFile->write(EQEMuLog::Error, ERR_ACTIVITY_OOR, ActivityID); - continue; - } - - // Find Active Task Slot - int ActiveTaskIndex = -1; - - for(int i=0; iActiveTasks[i].TaskID == TaskID) { - ActiveTaskIndex = i; - break; - } - } - - if(ActiveTaskIndex == -1) { - LogFile->write(EQEMuLog::Error, ERR_NOTASK, ActivityID, TaskID); - continue; - } - - int DoneCount = atoi(row[2]); - bool Completed = atoi(row[3]); - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].ActivityID = ActivityID; - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount = DoneCount; - if(Completed) state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State = ActivityCompleted; - else - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].State = ActivityHidden; - - state->ActiveTasks[ActiveTaskIndex].Activity[ActivityID].Updated = false; - - _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, ActivityID: %i, " - "DoneCount: %i, Completed: %i", - CharacterID, TaskID, ActivityID, DoneCount, Completed); - - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR2, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `activityid`, `donecount`, `completed` " + "FROM `character_activities` " + "WHERE `charid` = %i " + "ORDER BY `taskid` ASC, `activityid` ASC", characterID); + results = database.QueryDatabase(query); + if (!results.Success()){ + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load Activities: %s", results.ErrorMessage().c_str()); return false; } - const char *CompletedTaskQuery = "SELECT `taskid`, `activityid`, `completedtime` from `completed_tasks` " - "WHERE `charid` = %i ORDER BY completedtime, taskid, activityid"; + for (auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + if((taskID<0) || (taskID>=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading character activities from database", taskID); + continue; + } - const char *ERR_TASK_OOR3 = "[TASKS]Task ID %i out of range while loading completed tasks from database"; + int activityID = atoi(row[1]); + if((activityID<0) || (activityID>=MAXACTIVITIESPERTASK)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity ID %i out of range while loading character activities from database", activityID); + continue; + } - const char *ERR_ACTIVITY_OOR2 = "[TASKS]Activity ID %i out of range while loading completed tasks from database"; + // Find Active Task Slot + int activeTaskIndex = -1; - const char *ERR_MYSQLERROR3 = "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s"; + for(int i=0; iActiveTasks[i].TaskID == taskID) { + activeTaskIndex = i; + break; + } + if(activeTaskIndex == -1) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity %i found for task %i which client does not have.", activityID, taskID); + continue; + } + int doneCount = atoi(row[2]); + bool completed = atoi(row[3]); + state->ActiveTasks[activeTaskIndex].Activity[activityID].ActivityID = activityID; + state->ActiveTasks[activeTaskIndex].Activity[activityID].DoneCount = doneCount; + if(completed) + state->ActiveTasks[activeTaskIndex].Activity[activityID].State = ActivityCompleted; + else + state->ActiveTasks[activeTaskIndex].Activity[activityID].State = ActivityHidden; + + state->ActiveTasks[activeTaskIndex].Activity[activityID].Updated = false; + + _log(TASKS__CLIENTLOAD, "TaskManager::LoadClientState. Char: %i Task ID %i, ActivityID: %i, DoneCount: %i, Completed: %i", characterID, taskID, activityID, doneCount, completed); + + } if(RuleB(TaskSystem, RecordCompletedTasks)) { - if(database.RunQuery(query,MakeAnyLenString(&query, CompletedTaskQuery, - CharacterID), errbuf, &result)) { - - CompletedTaskInformation cti; - - for(int i=0; i=MAXTASKS)) { - LogFile->write(EQEMuLog::Error, ERR_TASK_OOR3, TaskID); - continue; - } - int ActivityID = atoi(row[1]); - - // An ActivityID of -1 means mark all the none optional activities in the - // task as complete. If the Rule to record optional activities is enabled, - // subsequent records for this task will flag any optional tasks that were - // completed. - if((ActivityID<-1) || (ActivityID>=MAXACTIVITIESPERTASK)) { - LogFile->write(EQEMuLog::Error, ERR_ACTIVITY_OOR2, ActivityID); - continue; - } - int CompletedTime = atoi(row[2]); - - if((PreviousTaskID != -1) && ((TaskID != PreviousTaskID) || - (CompletedTime != PreviousCompletedTime))) { - - state->CompletedTasks.push_back(cti); - for(int i=0; iActivityCount; i++) - if(!Task->Activity[i].Optional) - cti.ActivityDone[i] = true; - } - else - cti.ActivityDone[ActivityID] = true; - - } - if(PreviousTaskID != -1) - state->CompletedTasks.push_back(cti); - - state->LastCompletedTaskLoaded = state->CompletedTasks.size(); - - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR3, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT `taskid`, `activityid`, `completedtime` " + "FROM `completed_tasks` " + "WHERE `charid` = %i ORDER BY completedtime, taskid, activityid", + characterID); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load completed tasks: %s", results.ErrorMessage().c_str()); return false; + } + + CompletedTaskInformation cti; + + for(int i=0; i=MAXTASKS)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Task ID %i out of range while loading completed tasks from database", taskID); + continue; + } + + // An ActivityID of -1 means mark all the none optional activities in the + // task as complete. If the Rule to record optional activities is enabled, + // subsequent records for this task will flag any optional tasks that were + // completed. + int activityID = atoi(row[1]); + if((activityID<-1) || (activityID>=MAXACTIVITIESPERTASK)) { + LogFile->write(EQEMuLog::Error, "[TASKS]Activity ID %i out of range while loading completed tasks from database", activityID); + continue; + } + + int completedTime = atoi(row[2]); + if((previousTaskID != -1) && ((taskID != previousTaskID) || (completedTime != previousCompletedTime))) { + state->CompletedTasks.push_back(cti); + for(int i=0; iActivityCount; i++) + if(!task->Activity[i].Optional) + cti.ActivityDone[i] = true; + } + else + cti.ActivityDone[activityID] = true; + + } + + if(previousTaskID != -1) + state->CompletedTasks.push_back(cti); + + state->LastCompletedTaskLoaded = state->CompletedTasks.size(); + + } + + query = StringFormat("SELECT `taskid` FROM character_enabledtasks " + "WHERE `charid` = %i AND `taskid` >0 AND `taskid` < %i " + "ORDER BY `taskid` ASC", + characterID, MAXTASKS); + results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in TaskManager::LoadClientState load enabled tasks: %s", results.ErrorMessage().c_str()); + else + for (auto row = results.begin(); row != results.end(); ++row) { + int taskID = atoi(row[0]); + state->EnabledTasks.push_back(taskID); + _log(TASKS__CLIENTLOAD, "Adding TaskID %i to enabled tasks", taskID); } - } - - const char *EnabledTaskQuery = "SELECT `taskid` FROM character_enabledtasks WHERE `charid` = %i " - "AND `taskid` >0 AND `taskid` < %i ORDER BY `taskid` ASC"; - - const char *ERR_MYSQLERROR4 = "[TASKS]Error in TaskManager::LoadClientState load enabled tasks: %s"; - - if(database.RunQuery(query,MakeAnyLenString(&query, EnabledTaskQuery, - CharacterID, MAXTASKS), errbuf, &result)) { - - while((row = mysql_fetch_row(result))) { - int TaskID = atoi(row[0]); - state->EnabledTasks.push_back(TaskID); - _log(TASKS__CLIENTLOAD, "Adding TaskID %i to enabled tasks", TaskID); - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR4, errbuf); - safe_delete_array(query); - } // Check that there is an entry in the client task state for every activity in each task // This should only break if a ServerOP adds or deletes activites for a task that players already // have active, or due to a bug. - - - const char *ERR_NOTASK2 = "[TASKS]Character %i has task %i which does not exist."; - - const char *ERR_INCONSISTENT = "[TASKS]Fatal error in character %i task state. Activity %i for " - "Task %i either missing from client state or from task."; - for(int i=0; iActiveTasks[i].TaskID; - if(TaskID==TASKSLOTEMPTY) continue; - if(!Tasks[TaskID]) { + int taskID = state->ActiveTasks[i].TaskID; + if(taskID==TASKSLOTEMPTY) continue; + if(!Tasks[taskID]) { c->Message(13, "Active Task Slot %i, references a task (%i), that does not exist. " - "Removing from memory. Contact a GM to resolve this.",i, TaskID); + "Removing from memory. Contact a GM to resolve this.",i, taskID); - LogFile->write(EQEMuLog::Error, ERR_NOTASK2, CharacterID, TaskID); + LogFile->write(EQEMuLog::Error, "[TASKS]Character %i has task %i which does not exist.", characterID, taskID); state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; continue; } - for(int j=0; jActivityCount; j++) { + for(int j=0; jActivityCount; j++) { if(state->ActiveTasks[i].Activity[j].ActivityID != j) { c->Message(13, "Active Task %i, %s. Activity count does not match expected value." "Removing from memory. Contact a GM to resolve this.", - TaskID, Tasks[TaskID]->Title); + taskID, Tasks[taskID]->Title); - LogFile->write(EQEMuLog::Error, ERR_INCONSISTENT, CharacterID, j, TaskID); + LogFile->write(EQEMuLog::Error, "[TASKS]Fatal error in character %i task state. Activity %i for " + "Task %i either missing from client state or from task.", characterID, j, taskID); state->ActiveTasks[i].TaskID=TASKSLOTEMPTY; break; } } } - for(int i=0; iActiveTasks[i].TaskID != TASKSLOTEMPTY) - state->UnlockActivities(CharacterID, i); + state->UnlockActivities(characterID, i); - _log(TASKS__CLIENTLOAD, "LoadClientState for Character ID %d DONE!", CharacterID); + _log(TASKS__CLIENTLOAD, "LoadClientState for Character ID %d DONE!", characterID); return true; } -void ClientTaskState::EnableTask(int CharID, int TaskCount, int *TaskList) { +void ClientTaskState::EnableTask(int characterID, int taskCount, int *tasks) { // Check if the Task is already enabled for this client // - std::vector TasksEnabled; - std::vector::iterator Iterator; + std::vector tasksEnabled; - for(int i=0; i TaskList[i]) break; - ++Iterator; + if((*iterator) > tasks[i]) + break; + ++iterator; } - if(AddTask) { - EnabledTasks.insert(Iterator, TaskList[i]); + + if(addTask) { + EnabledTasks.insert(iterator, tasks[i]); // Make a note of the task we enabled, for later SQL generation - TasksEnabled.push_back(TaskList[i]); + tasksEnabled.push_back(tasks[i]); } } @@ -835,59 +713,46 @@ void ClientTaskState::EnableTask(int CharID, int TaskCount, int *TaskList) { for(unsigned int i=0; iwrite(EQEMuLog::Error, "[TASKS]Error in ClientTaskState::EnableTask %s %s", query.c_str(), results.ErrorMessage().c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - char *buf = 0; - - for(unsigned int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - } - - safe_delete_array(query); } -void ClientTaskState::DisableTask(int CharID, int TaskCount, int *TaskList) { +void ClientTaskState::DisableTask(int charID, int taskCount, int *taskList) { // Check if the Task is enabled for this client // - std::vector TasksDisabled; - std::vector::iterator Iterator; + std::vector tasksDisabled; - for(int i=0; i TaskList[i]) break; - ++Iterator; + + if((*iterator) > taskList[i]) + break; + + ++iterator; } - if(RemoveTask) { - EnabledTasks.erase(Iterator); - TasksDisabled.push_back(TaskList[i]); + + if(removeTask) { + EnabledTasks.erase(iterator); + tasksDisabled.push_back(taskList[i]); } } @@ -895,40 +760,20 @@ void ClientTaskState::DisableTask(int CharID, int TaskCount, int *TaskList) { for(unsigned int i=0; iwrite(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - } - - safe_delete_array(query); + queryStream << ")"; + std::string query = queryStream.str(); + _log(TASKS__UPDATE, "Executing query %s", query.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in ClientTaskState::DisableTask %s %s", query.c_str(), results.ErrorMessage().c_str()); } bool ClientTaskState::IsTaskEnabled(int TaskID) { @@ -1152,7 +997,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task if(Tasks[TaskList[i]] != nullptr) break; } - // FIXME: The 10 and 5 values in this calculation are to account for the string "ABCD" we are putting in 3 times. + // FIXME: The 10 and 5 values in this calculation are to account for the string "ABCD" we are putting in 3 times. // // Calculate how big the packet needs to be pased on the number of tasks and the // size of the variable length strings. @@ -1230,9 +1075,9 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task // FIXME: In live packets, these two strings appear to be the same as the Text1 and Text2 // strings from the first activity in the task, however the task chooser/selector // does not appear to make use of them. - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; AvailableTaskTrailer = (AvailableTaskTrailer_Struct*)Ptr; @@ -1247,7 +1092,7 @@ void TaskManager::SendTaskSelector(Client *c, Mob *mob, int TaskCount, int *Task // In some packets, this next string looks like a short task summary, however it doesn't // appear anywhere in the client window. - sprintf(Ptr, "ABCD"); + sprintf(Ptr, "ABCD"); Ptr = Ptr + strlen(Ptr) + 1; } @@ -1427,24 +1272,18 @@ int ClientTaskState::GetActiveTaskID(int index) { return ActiveTasks[index].TaskID; } -static void DeleteCompletedTaskFromDatabase(int CharID, int TaskID) { +static void DeleteCompletedTaskFromDatabase(int charID, int taskID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + _log(TASKS__UPDATE, "DeleteCompletedTasksFromDatabase. CharID = %i, TaskID = %i", charID, taskID); - const char *TaskQuery="DELETE FROM completed_tasks WHERE charid=%i AND taskid = %i"; - - _log(TASKS__UPDATE, "DeleteCompletedTasksFromDatabase. CharID = %i, TaskID = %i", - CharID, TaskID); - - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, CharID, TaskID), errbuf)) { - - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s, %s", query, errbuf); - safe_delete_array(query); + const std::string query = StringFormat("DELETE FROM completed_tasks WHERE charid=%i AND taskid = %i", charID, taskID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s, %s", query.c_str(), results.ErrorMessage().c_str()); return; } - _log(TASKS__UPDATE, "Delete query %s", query); - safe_delete_array(query); + + _log(TASKS__UPDATE, "Delete query %s", query.c_str()); } bool ClientTaskState::UnlockActivities(int CharID, int TaskIndex) { @@ -1979,7 +1818,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T taskmanager->SendSingleActiveTaskToClient(c, TaskIndex, TaskComplete, false); // Inform the client the task has been updated, both by a chat message c->Message(0, "Your task '%s' has been updated.", Task->Title); - + if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) { char buf[24]; snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID); @@ -1992,7 +1831,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc); } } - + // If this task is now complete, the Completed tasks will have been // updated in UnlockActivities. Send the completed task list to the // client. This is the same sequence the packets are sent on live. @@ -3142,40 +2981,29 @@ void ClientTaskState::CancelTask(Client *c, int SequenceNumber, bool RemoveFromD RemoveTask(c, SequenceNumber); } -void ClientTaskState::RemoveTask(Client *c, int SequenceNumber) { +void ClientTaskState::RemoveTask(Client *c, int sequenceNumber) { - int CharacterID = c->CharacterID(); + int characterID = c->CharacterID(); + _log(TASKS__UPDATE, "ClientTaskState Cancel Task %i ", sequenceNumber); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - const char *TaskQuery="DELETE FROM character_tasks WHERE charid=%i AND taskid = %i"; - - const char *ActivityQuery="DELETE FROM character_activities WHERE charid=%i AND taskid = %i"; - - _log(TASKS__UPDATE, "ClientTaskState Cancel Task %i ", SequenceNumber); - - if(!database.RunQuery(query,MakeAnyLenString(&query, ActivityQuery, - CharacterID, - ActiveTasks[SequenceNumber].TaskID), errbuf)) { - - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM character_activities WHERE charid=%i AND taskid = %i", + characterID, ActiveTasks[sequenceNumber].TaskID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); return; } - _log(TASKS__UPDATE, "CancelTask: %s", query); - safe_delete_array(query); - if(!database.RunQuery(query,MakeAnyLenString(&query, TaskQuery, - CharacterID, - ActiveTasks[SequenceNumber].TaskID), errbuf)) { + _log(TASKS__UPDATE, "CancelTask: %s", query.c_str()); - LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", errbuf); - } + query = StringFormat("DELETE FROM character_tasks WHERE charid=%i AND taskid = %i", + characterID, ActiveTasks[sequenceNumber].TaskID); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "[TASKS]Error in CientTaskState::CancelTask %s", results.ErrorMessage().c_str()); - _log(TASKS__UPDATE, "CancelTask: %s", query); - safe_delete_array(query); + _log(TASKS__UPDATE, "CancelTask: %s", query.c_str()); - ActiveTasks[SequenceNumber].TaskID = TASKSLOTEMPTY; + ActiveTasks[sequenceNumber].TaskID = TASKSLOTEMPTY; ActiveTaskCount--; } @@ -3297,98 +3125,83 @@ TaskGoalListManager::~TaskGoalListManager() { bool TaskGoalListManager::LoadLists() { - - const char *CountQuery = "SELECT `listid`, COUNT(`entry`) FROM `goallists` GROUP by `listid` " - "ORDER BY `listid`"; - - const char *ListQuery = "SELECT `entry` from `goallists` WHERE `listid`=%i " - "ORDER BY `entry` ASC LIMIT %i"; - - const char *ERR_MYSQLERROR = "Error in TaskGoalListManager::LoadLists: %s %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - _log(TASKS__GLOBALLOAD, "TaskGoalListManager::LoadLists Called"); - for(int i=0; i< NumberOfLists; i++) { - + for(int i=0; i< NumberOfLists; i++) safe_delete_array(TaskGoalLists[i].GoalItemEntries); - - } safe_delete_array(TaskGoalLists); + const char *ERR_MYSQLERROR = "Error in TaskGoalListManager::LoadLists: %s %s"; + NumberOfLists = 0; - if(database.RunQuery(query,MakeAnyLenString(&query,CountQuery),errbuf,&result)) { - - NumberOfLists = mysql_num_rows(result); - _log(TASKS__GLOBALLOAD, "Database returned a count of %i lists", NumberOfLists); - - TaskGoalLists = new TaskGoalList_Struct[NumberOfLists]; - - int ListIndex = 0; - - while((row = mysql_fetch_row(result))) { - int ListID = atoi(row[0]); - int ListSize = atoi(row[1]); - - TaskGoalLists[ListIndex].ListID = ListID; - TaskGoalLists[ListIndex].Size = ListSize; - TaskGoalLists[ListIndex].Min = 0; - TaskGoalLists[ListIndex].Max = 0; - TaskGoalLists[ListIndex].GoalItemEntries = new int[ListSize]; - - ListIndex++; - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); + std::string query = "SELECT `listid`, COUNT(`entry`) " + "FROM `goallists` GROUP by `listid` " + "ORDER BY `listid`"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query.c_str(), results.ErrorMessage().c_str()); return false; + } + + NumberOfLists = results.RowCount(); + _log(TASKS__GLOBALLOAD, "Database returned a count of %i lists", NumberOfLists); + + TaskGoalLists = new TaskGoalList_Struct[NumberOfLists]; + + int listIndex = 0; + + for(auto row = results.begin(); row != results.end(); ++row) { + int listID = atoi(row[0]); + int listSize = atoi(row[1]); + + TaskGoalLists[listIndex].ListID = listID; + TaskGoalLists[listIndex].Size = listSize; + TaskGoalLists[listIndex].Min = 0; + TaskGoalLists[listIndex].Max = 0; + TaskGoalLists[listIndex].GoalItemEntries = new int[listSize]; + + listIndex++; + } + + for(int listIndex = 0; listIndex < NumberOfLists; listIndex++) { + + int listID = TaskGoalLists[listIndex].ListID; + unsigned int size = TaskGoalLists[listIndex].Size; + query = StringFormat("SELECT `entry` from `goallists` " + "WHERE `listid` = %i " + "ORDER BY `entry` ASC LIMIT %i", + listID, size); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query.c_str(), results.ErrorMessage().c_str()); + TaskGoalLists[listIndex].Size = 0; + continue; + } + + // This should only happen if a row is deleted in between us retrieving the counts + // at the start of this method and getting to here. It should not be possible for + // an INSERT to cause a problem, as the SELECT is used with a LIMIT + if(results.RowCount() < size) + TaskGoalLists[listIndex].Size = results.RowCount(); + + int entryIndex = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++entryIndex) { + + int entry = atoi(row[0]); + + if(entry < TaskGoalLists[listIndex].Min) + TaskGoalLists[listIndex].Min = entry; + + if(entry > TaskGoalLists[listIndex].Max) + TaskGoalLists[listIndex].Max = entry; + + TaskGoalLists[listIndex].GoalItemEntries[entryIndex] = entry; + + } + } - for(int ListIndex = 0; ListIndex < NumberOfLists; ListIndex++) { - - int ListID = TaskGoalLists[ListIndex].ListID; - unsigned int Size = TaskGoalLists[ListIndex].Size; - - if(database.RunQuery(query,MakeAnyLenString(&query,ListQuery,ListID,Size),errbuf,&result)) { - // This should only happen if a row is deleted in between us retrieving the counts - // at the start of this method and getting to here. It should not be possible for - // an INSERT to cause a problem, as the SELECT is used with a LIMIT - if(mysql_num_rows(result) < Size) - TaskGoalLists[ListIndex].Size = mysql_num_rows(result); - - int EntryIndex = 0; - - while((row = mysql_fetch_row(result))) { - - int Entry = atoi(row[0]); - - if(Entry < TaskGoalLists[ListIndex].Min) - TaskGoalLists[ListIndex].Min = Entry; - - if(Entry > TaskGoalLists[ListIndex].Max) - TaskGoalLists[ListIndex].Max = Entry; - - TaskGoalLists[ListIndex].GoalItemEntries[EntryIndex++] = Entry; - - } - - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - TaskGoalLists[ListIndex].Size = 0; - safe_delete_array(query); - } - } return true; } @@ -3486,47 +3299,33 @@ TaskProximityManager::~TaskProximityManager() { } -bool TaskProximityManager::LoadProximities(int ZoneID) { +bool TaskProximityManager::LoadProximities(int zoneID) { + TaskProximity proximity; - const char *ProximityQuery = "SELECT `exploreid`, `minx`, `maxx`, `miny`, `maxy`, " - "`minz`, `maxz` from `proximities` WHERE `zoneid`=%i " - "ORDER BY `zoneid` ASC"; - - const char *ERR_MYSQLERROR = "Error in TaskProximityManager::LoadProximities %s %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - - TaskProximity Proximity; - - _log(TASKS__GLOBALLOAD, "TaskProximityManager::LoadProximities Called for zone %i", ZoneID); + _log(TASKS__GLOBALLOAD, "TaskProximityManager::LoadProximities Called for zone %i", zoneID); TaskProximities.clear(); - if(database.RunQuery(query,MakeAnyLenString(&query,ProximityQuery, ZoneID),errbuf,&result)) { - - while((row = mysql_fetch_row(result))) { - Proximity.ExploreID = atoi(row[0]); - Proximity.MinX = atof(row[1]); - Proximity.MaxX = atof(row[2]); - Proximity.MinY = atof(row[3]); - Proximity.MaxY = atof(row[4]); - Proximity.MinZ = atof(row[5]); - Proximity.MaxZ = atof(row[6]); - - TaskProximities.push_back(Proximity); - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `exploreid`, `minx`, `maxx`, " + "`miny`, `maxy`, `minz`, `maxz` " + "FROM `proximities` WHERE `zoneid` = %i " + "ORDER BY `zoneid` ASC", zoneID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in TaskProximityManager::LoadProximities %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } - safe_delete_array(query); + } + + for( auto row = results.begin(); row != results.end(); ++row) { + proximity.ExploreID = atoi(row[0]); + proximity.MinX = atof(row[1]); + proximity.MaxX = atof(row[2]); + proximity.MinY = atof(row[3]); + proximity.MaxY = atof(row[4]); + proximity.MinZ = atof(row[5]); + proximity.MaxZ = atof(row[6]); + + TaskProximities.push_back(proximity); + } return true; diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index 91cb458a0..0fd130f89 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -142,7 +142,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme // Adding augment if (in_augment->augment_slot == -1) { - if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && + if (((slot=tobe_auged->AvailableAugmentSlot(auged_with->GetAugmentType()))!=-1) && (tobe_auged->AvailableWearSlot(auged_with->GetItem()->Slots))) { tobe_auged->PutAugment(slot, *auged_with); @@ -424,38 +424,28 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac return; } - - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - - uint32 qlen = 0; - uint8 qcount = 0; - - //pull the list of components - qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount " - " FROM tradeskill_recipe_entries AS tre " - " WHERE tre.componentcount > 0 AND tre.recipe_id=%u", rac->recipe_id); - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query, errbuf); - safe_delete_array(query); + //pull the list of components + std::string query = StringFormat("SELECT tre.item_id, tre.componentcount " + "FROM tradeskill_recipe_entries AS tre " + "WHERE tre.componentcount > 0 AND tre.recipe_id = %u", + rac->recipe_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); user->QueuePacket(outapp); safe_delete(outapp); return; } - safe_delete_array(query); - qcount = mysql_num_rows(result); - if(qcount < 1) { + if(results.RowCount() < 1) { LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: no components returned"); user->QueuePacket(outapp); safe_delete(outapp); return; } - if(qcount > 10) { - LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", qcount); + + if(results.RowCount() > 10) { + LogFile->write(EQEMuLog::Error, "Error in HandleAutoCombine: too many components returned (%u)", results.RowCount()); user->QueuePacket(outapp); safe_delete(outapp); return; @@ -466,17 +456,15 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac uint8 counts[10]; memset(counts, 0, sizeof(counts)); - //search for all the items in their inventory Inventory& user_inv = user->GetInv(); uint8 count = 0; uint8 needcount = 0; - uint8 r,k; std::list MissingItems; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + uint8 needItemIndex = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++needItemIndex) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); @@ -491,10 +479,9 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac MissingItems.push_back(item); //dont start deleting anything until we have found it all. - items[r] = item; - counts[r] = num; + items[needItemIndex] = item; + counts[needItemIndex] = num; } - mysql_free_result(result); //make sure we found it all... if(count != needcount) @@ -520,12 +507,12 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac //remove all the items from the players inventory, with updates... int16 slot; - for(r = 0; r < qcount; r++) { + for(uint8 r = 0; r < results.RowCount(); r++) { if(items[r] == 0 || counts[r] == 0) continue; //skip empties, could prolly break here //we have to loop here to delete 1 at a time in case its in multiple stacks. - for(k = 0; k < counts[r]; k++) { + for(uint8 k = 0; k < counts[r]; k++) { slot = user_inv.HasItem(items[r], 1, invWherePersonal); if (slot == INVALID_INDEX) { //WTF... I just checked this above, but just to be sure... @@ -539,19 +526,14 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac const ItemInst* inst = user_inv.GetItem(slot); if (inst && !inst->IsStackable()) - { user->DeleteItemInInventory(slot, 0, true); - } else - { user->DeleteItemInInventory(slot, 1, true); - } } } //otherwise, we found it all... outp->reply_code = 0x00000000; //success for finding it... - user->QueuePacket(outapp); safe_delete(outapp); @@ -654,34 +636,26 @@ SkillUseTypes Object::TypeToSkill(uint32 type) return TradeskillUnknown; } -void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsigned long objtype, unsigned long someid) { +void Client::TradeskillSearchResults(const std::string query, unsigned long objtype, unsigned long someid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query, errbuf); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - uint8 qcount = 0; + if(results.RowCount() < 1) + return; //search gave no results... not an error - qcount = mysql_num_rows(result); - if(qcount < 1) { - //search gave no results... not an error - return; - } - if(mysql_num_fields(result) != 6) { - LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query); + if(results.ColumnCount() != 6) { + LogFile->write(EQEMuLog::Error, "Error in TradeskillSearchResults query '%s': Invalid column count in result", query.c_str()); return; } - uint8 r; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + for(auto row = results.begin(); row != results.end(); ++row) { if(row == nullptr || row[0] == nullptr || row[1] == nullptr || row[2] == nullptr || row[3] == nullptr || row[5] == nullptr) continue; + uint32 recipe = (uint32)atoi(row[0]); const char *name = row[1]; uint32 trivial = (uint32) atoi(row[2]); @@ -691,14 +665,10 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi // Skip the recipes that exceed the threshold in skill difference // Recipes that have either been made before or were // explicitly learned are excempt from that limit - if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff)) { - if (((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff) - && row[4] == nullptr) - { + if (RuleB(Skills, UseLimitTradeskillSearchSkillDiff) + && ((int32)trivial - (int32)GetSkill((SkillUseTypes)tradeskill)) > RuleI(Skills, MaxTradeskillSearchSkillDiff) + && row[4] == nullptr) continue; - } - } - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RecipeReply, sizeof(RecipeReply_Struct)); RecipeReply_Struct *reply = (RecipeReply_Struct *) outapp->pBuffer; @@ -711,39 +681,30 @@ void Client::TradeskillSearchResults(const char *query, unsigned long qlen, unsi strn0cpy(reply->recipe_name, name, sizeof(reply->recipe_name)); FastQueuePacket(&outapp); } - mysql_free_result(result); + } void Client::SendTradeskillDetails(uint32 recipe_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - - uint32 qlen = 0; - uint8 qcount = 0; - - //pull the list of components - qlen = MakeAnyLenString(&query, "SELECT tre.item_id,tre.componentcount,i.icon,i.Name " - " FROM tradeskill_recipe_entries AS tre " - " LEFT JOIN items AS i ON tre.item_id = i.id " - " WHERE tre.componentcount > 0 AND tre.recipe_id=%u", recipe_id); - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query, errbuf); - safe_delete_array(query); + //pull the list of components + std::string query = StringFormat("SELECT tre.item_id,tre.componentcount,i.icon,i.Name " + "FROM tradeskill_recipe_entries AS tre " + "LEFT JOIN items AS i ON tre.item_id = i.id " + "WHERE tre.componentcount > 0 AND tre.recipe_id = %u", + recipe_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - safe_delete_array(query); - qcount = mysql_num_rows(result); - if(qcount < 1) { + if(results.RowCount() < 1) { LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: no components returned"); return; } - if(qcount > 10) { - LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", qcount); + + if(results.RowCount() > 10) { + LogFile->write(EQEMuLog::Error, "Error in SendTradeskillDetails: too many components returned (%u)", results.RowCount()); return; } @@ -773,20 +734,18 @@ void Client::SendTradeskillDetails(uint32 recipe_id) { uint32 len; uint32 datalen = 0; uint8 count = 0; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + + for(auto row = results.begin(); row != results.end(); ++row) { //watch for references to items which are not in the //items table, which the left join will make nullptr... - if(row[2] == nullptr || row[3] == nullptr) { + if(row[2] == nullptr || row[3] == nullptr) continue; - } uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); - - uint32 icon = (uint32) atoi(row[2]); + const char *name = row[3]; len = strlen(name); if(len > 63) @@ -816,7 +775,6 @@ void Client::SendTradeskillDetails(uint32 recipe_id) { } } - mysql_free_result(result); //now move the item data over top of the FFFFs uint8 dist = sizeof(uint32) * (10 - count); @@ -1094,7 +1052,7 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { ++itr; } return(true); - } + } /* Tradeskill Fail */ else { success_modifier = 2; // Halves the chance @@ -1172,7 +1130,7 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float chance_stage2 = 12.5 - (.08 * (current_raw_skill - 175)); } } - + chance_stage2 = mod_tradeskill_skillup(chance_stage2); if (chance_stage2 > MakeRandomFloat(0, 99)) { @@ -1191,249 +1149,212 @@ void Client::CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float bool ZoneDatabase::GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - char buf2[4096]; - uint32 sum = 0; - uint32 count = 0; - uint32 qcount = 0; - uint32 qlen = 0; - - // make where clause segment for container(s) - char containers[30]; - if (some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", c_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", c_type, some_id); - } - - buf2[0] = '\0'; + std::string containers;// make where clause segment for container(s) + if (some_id == 0) + containers = StringFormat("= %u", c_type); // world combiner so no item number + else + containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory //Could prolly watch for stacks in this loop and handle them properly... //just increment sum and count accordingly bool first = true; - uint8 i; - char *pos = buf2; - for (i = 0; i < 10; i++) { // TODO: need to determine if this is bound to world/item container size + std::string buf2; + uint32 count = 0; + uint32 sum = 0; + for (uint8 i = 0; i < 10; i++) { // TODO: need to determine if this is bound to world/item container size const ItemInst* inst = container->GetItem(i); - if (inst) { - const Item_Struct* item = GetItem(inst->GetItem()->ID); - if (item) { - if(first) { - pos += snprintf(pos, 19, "%d", item->ID); - first = false; - } else { - pos += snprintf(pos, 19, ",%d", item->ID); - } - sum += item->ID; - count++; - } - } - } - *pos = '\0'; + if (!inst) + continue; - if(count < 1) { - return(false); //no items == no recipe + const Item_Struct* item = GetItem(inst->GetItem()->ID); + if (!item) + continue; + + if(first) { + buf2 += StringFormat("%d", item->ID); + first = false; + } else + buf2 += StringFormat(",%d", item->ID); + + sum += item->ID; + count++; } - qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id " - " FROM tradeskill_recipe_entries AS tre" - " INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) " - " WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount>0 )" - " OR ( tre.item_id %s AND tre.iscontainer=1 ))" - " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u" - " AND sum(tre.item_id * tre.componentcount) = %u", buf2, containers, count, sum); + if(count == 0) + return false; //no items == no recipe - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - qcount = mysql_num_rows(result); - if(qcount > 1) { - //multiple recipes, partial match... do an extra query to get it exact. - //this happens when combining components for a smaller recipe - //which is completely contained within another recipe - - first = true; - pos = buf2; - for (i = 0; i < qcount; i++) { - row = mysql_fetch_row(result); - uint32 recipeid = (uint32)atoi(row[0]); - if(first) { - pos += snprintf(pos, 19, "%u", recipeid); - first = false; - } else { - pos += snprintf(pos, 19, ",%u", recipeid); - } - //length limit on buf2 - if(i == 214) { //Maximum number of recipe matches (19 * 215 = 4096) - LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", i + 1, qcount); - break; - } - } - - qlen = MakeAnyLenString(&query, "SELECT tre.recipe_id" - " FROM tradeskill_recipe_entries AS tre" - " WHERE tre.recipe_id IN (%s)" - " GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u" - " AND sum(tre.item_id * tre.componentcount) = %u", buf2, count, sum); - - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - qcount = mysql_num_rows(result); + std::string query = StringFormat("SELECT tre.recipe_id " + "FROM tradeskill_recipe_entries AS tre " + "INNER JOIN tradeskill_recipe AS tr ON (tre.recipe_id = tr.id) " + "WHERE tr.enabled AND (( tre.item_id IN(%s) AND tre.componentcount > 0) " + "OR ( tre.item_id %s AND tre.iscontainer=1 ))" + "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u " + "AND sum(tre.item_id * tre.componentcount) = %u", + buf2.c_str(), containers.c_str(), count, sum); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe search, error: %s", results.ErrorMessage().c_str()); + return false; } - if(qcount < 1) - return(false); + if (results.RowCount() > 1) { + //multiple recipes, partial match... do an extra query to get it exact. + //this happens when combining components for a smaller recipe + //which is completely contained within another recipe + first = true; + uint32 index = 0; + buf2 = ""; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + uint32 recipeid = (uint32)atoi(row[0]); + if(first) { + buf2 += StringFormat("%u", recipeid); + first = false; + } else + buf2 += StringFormat(",%u", recipeid); - if(qcount > 1) - { + //length limit on buf2 + if(index == 214) { //Maximum number of recipe matches (19 * 215 = 4096) + LogFile->write(EQEMuLog::Error, "GetTradeRecipe warning: Too many matches. Unable to search all recipe entries. Searched %u of %u possible entries.", index + 1, results.RowCount()); + break; + } + } + + query = StringFormat("SELECT tre.recipe_id " + "FROM tradeskill_recipe_entries AS tre " + "WHERE tre.recipe_id IN (%s) " + "GROUP BY tre.recipe_id HAVING sum(tre.componentcount) = %u " + "AND sum(tre.item_id * tre.componentcount) = %u", buf2.c_str(), count, sum); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + return false; + } + } + + if (results.RowCount() < 1) + return false; + + if(results.RowCount() > 1) { //The recipe is not unique, so we need to compare the container were using. - uint32 containerId = 0; - if(some_id) { //Standard container + if(some_id) //Standard container containerId = some_id; - } - else if(c_type) { //World container + else if(c_type)//World container containerId = c_type; - } - else { //Invalid container - return(false); + else //Invalid container + return false; + + query = StringFormat("SELECT tre.recipe_id " + "FROM tradeskill_recipe_entries AS tre " + "WHERE tre.recipe_id IN (%s) " + "AND tre.item_id = %u;", buf2.c_str(), containerId); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + return false; } - qlen = MakeAnyLenString(&query,"SELECT tre.recipe_id FROM tradeskill_recipe_entries as tre WHERE tre.recipe_id IN (%s)" - " AND tre.item_id = %u;",buf2,containerId); - - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, re-query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - uint32 resultRowTotal = mysql_num_rows(result); - - if(resultRowTotal == 0) { //Recipe contents matched more than 1 recipe, but not in this container + if(results.RowCount() == 0) { //Recipe contents matched more than 1 recipe, but not in this container LogFile->write(EQEMuLog::Error, "Combine error: Incorrect container is being used!"); - return(false); - } - if(resultRowTotal > 1) { //Recipe contents matched more than 1 recipe in this container - LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", resultRowTotal, containerId); + return false; } + + if (results.RowCount() > 1) //Recipe contents matched more than 1 recipe in this container + LogFile->write(EQEMuLog::Error, "Combine error: Recipe is not unique! %u matches found for container %u. Continuing with first recipe match.", results.RowCount(), containerId); + } - row = mysql_fetch_row(result); + auto row = results.begin(); uint32 recipe_id = (uint32)atoi(row[0]); - mysql_free_result(result); //Right here we verify that we actually have ALL of the tradeskill components.. //instead of part which is possible with experimentation. //This is here because something's up with the query above.. it needs to be rethought out bool has_components = true; - char TSerrbuf[MYSQL_ERRMSG_SIZE]; - char *TSquery = 0; - MYSQL_RES *TSresult; - MYSQL_ROW TSrow; - if (RunQuery(TSquery, MakeAnyLenString(&TSquery, "SELECT item_id, componentcount from tradeskill_recipe_entries where recipe_id=%i AND componentcount > 0", recipe_id), TSerrbuf, &TSresult)) { - while((TSrow = mysql_fetch_row(TSresult))!=nullptr) { - int ccnt = 0; - for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) { - const ItemInst* inst = container->GetItem(x); - if(inst){ - const Item_Struct* item = GetItem(inst->GetItem()->ID); - if (item) { - if(item->ID == atoi(TSrow[0])){ - ccnt++; - } - } - } - } - if(ccnt != atoi(TSrow[1])) - has_components = false; - } - mysql_free_result(TSresult); - } else { - LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", TSquery, TSerrbuf); - } - safe_delete_array(TSquery); - if(has_components == false){ + query = StringFormat("SELECT item_id, componentcount " + "FROM tradeskill_recipe_entries " + "WHERE recipe_id = %i AND componentcount > 0", + recipe_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in tradeskill verify query: '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec); + } - return false; - } + if (results.RowCount() == 0) + return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec); - return(GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec)); + for (auto row = results.begin(); row != results.end(); ++row) { + int ccnt = 0; + + for(int x = MAIN_BEGIN; x < EmuConstants::MAP_WORLD_SIZE; x++) { + const ItemInst* inst = container->GetItem(x); + if(!inst) + continue; + + const Item_Struct* item = GetItem(inst->GetItem()->ID); + if (!item) + continue; + + if(item->ID == atoi(row[0])) + ccnt++; + } + + if(ccnt != atoi(row[1])) + return false; + } + + return GetTradeRecipe(recipe_id, c_type, some_id, char_id, spec); } bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - - uint32 qcount = 0; - uint32 qlen; // make where clause segment for container(s) - char containers[30]; - if (some_id == 0) { - // world combiner so no item number - snprintf(containers,29, "= %u", c_type); - } else { - // container in inventory - snprintf(containers,29, "in (%u,%u)", c_type, some_id); + std::string containers; + if (some_id == 0) + containers = StringFormat("= %u", c_type); // world combiner so no item number + else + containers = StringFormat("IN (%u,%u)", c_type, some_id); // container in inventory + + std::string query = StringFormat("SELECT tr.id, tr.tradeskill, tr.skillneeded, " + "tr.trivial, tr.nofail, tr.replace_container, " + "tr.name, tr.must_learn, tr.quest, crl.madecount " + "FROM tradeskill_recipe AS tr " + "INNER JOIN tradeskill_recipe_entries AS tre " + "ON tr.id = tre.recipe_id " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list WHERE char_id = %u) AS crl " + "ON tr.id = crl.recipe_id " + "WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled " + "GROUP BY tr.id", + char_id, (unsigned long)recipe_id, containers.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query.c_str()); + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", results.ErrorMessage().c_str()); + return false; } - qlen = MakeAnyLenString(&query, "SELECT tr.id, tr.tradeskill, tr.skillneeded," - " tr.trivial, tr.nofail, tr.replace_container, tr.name, tr.must_learn, tr.quest, crl.madecount" - " FROM tradeskill_recipe AS tr inner join tradeskill_recipe_entries as tre" - " ON tr.id = tre.recipe_id" - " LEFT JOIN (SELECT recipe_id, madecount from char_recipe_list WHERE char_id = %u) AS crl " - " ON tr.id = crl.recipe_id " - " WHERE tr.id = %lu AND tre.item_id %s AND tr.enabled " - " GROUP BY tr.id", char_id, (unsigned long)recipe_id, containers); + if(results.RowCount() != 1) + return false;//just not found i guess.. - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, query: %s", query); - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecipe, error: %s", errbuf); - return(false); - } - safe_delete_array(query); - - qcount = mysql_num_rows(result); - if(qcount != 1) { - //just not found i guess.. - return(false); - } - - row = mysql_fetch_row(result); - spec->tradeskill = (SkillUseTypes)atoi(row[1]); - spec->skill_needed = (int16)atoi(row[2]); - spec->trivial = (uint16)atoi(row[3]); - spec->nofail = atoi(row[4]) ? true : false; + auto row = results.begin(); + spec->tradeskill = (SkillUseTypes)atoi(row[1]); + spec->skill_needed = (int16)atoi(row[2]); + spec->trivial = (uint16)atoi(row[3]); + spec->nofail = atoi(row[4]) ? true : false; spec->replace_container = atoi(row[5]) ? true : false; spec->name = row[6]; spec->must_learn = (uint8)atoi(row[7]); spec->quest = atoi(row[8]) ? true : false; + if (row[9] == nullptr) { spec->has_learnt = false; spec->madecount = 0; @@ -1442,141 +1363,109 @@ bool ZoneDatabase::GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id spec->madecount = (uint32)atoul(row[9]); } spec->recipe_id = recipe_id; - mysql_free_result(result); //Pull the on-success items... - qlen = MakeAnyLenString(&query, "SELECT item_id,successcount FROM tradeskill_recipe_entries" - " WHERE successcount>0 AND recipe_id=%u", recipe_id); - - if (!RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); + query = StringFormat("SELECT item_id,successcount FROM tradeskill_recipe_entries " + "WHERE successcount > 0 AND recipe_id = %u", recipe_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - qcount = mysql_num_rows(result); - if(qcount < 1) { + if(results.RowCount() < 1) { LogFile->write(EQEMuLog::Error, "Error in GetTradeRecept success: no success items returned"); - return(false); + return false; } - uint8 r; + spec->onsuccess.clear(); - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + for(auto row = results.begin(); row != results.end(); ++row) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); spec->onsuccess.push_back(std::pair(item, num)); } - mysql_free_result(result); + spec->onfail.clear(); //Pull the on-fail items... - qlen = MakeAnyLenString(&query, "SELECT item_id,failcount FROM tradeskill_recipe_entries" - " WHERE failcount>0 AND recipe_id=%u", recipe_id); - - spec->onfail.clear(); - if (RunQuery(query, qlen, errbuf, &result)) { - - qcount = mysql_num_rows(result); - uint8 r; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + query = StringFormat("SELECT item_id, failcount FROM tradeskill_recipe_entries " + "WHERE failcount > 0 AND recipe_id = %u", recipe_id); + results = QueryDatabase(query); + if (results.Success()) + for(auto row = results.begin(); row != results.end(); ++row) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8) atoi(row[1]); spec->onfail.push_back(std::pair(item, num)); } - mysql_free_result(result); - } + + spec->salvage.clear(); + + // Don't bother with the query if TS is nofail + if (spec->nofail) + return true; // Pull the salvage list - qlen = MakeAnyLenString(&query, "SELECT item_id,salvagecount FROM tradeskill_recipe_entries WHERE salvagecount>0 AND recipe_id=%u", recipe_id); - - spec->salvage.clear(); - // Don't bother with the query if TS is nofail - if (!spec->nofail && RunQuery(query, qlen, errbuf, &result)) { - qcount = mysql_num_rows(result); - uint8 r; - for(r = 0; r < qcount; r++) { - row = mysql_fetch_row(result); + query = StringFormat("SELECT item_id, salvagecount " + "FROM tradeskill_recipe_entries " + "WHERE salvagecount > 0 AND recipe_id = %u", recipe_id); + results = QueryDatabase(query); + if (results.Success()) + for(auto row = results.begin(); row != results.begin(); ++row) { uint32 item = (uint32)atoi(row[0]); uint8 num = (uint8)atoi(row[1]); spec->salvage.push_back(std::pair(item, num)); } - mysql_free_result(result); - } - safe_delete_array(query); - - return(true); + return true; } -void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount) +void ZoneDatabase::UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madeCount) { - char *query = 0; - uint32 qlen; - char errbuf[MYSQL_ERRMSG_SIZE]; - - qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list " - " SET recipe_id = %u, char_id = %u, madecount = %u " - " ON DUPLICATE KEY UPDATE madecount = %u;" - , recipe_id, char_id, madecount, madecount); - - if (!RunQuery(query, qlen, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query, errbuf); - } - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO char_recipe_list " + "SET recipe_id = %u, char_id = %u, madecount = %u " + "ON DUPLICATE KEY UPDATE madecount = %u;", + recipe_id, char_id, madeCount, madeCount); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateRecipeMadecount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); } void Client::LearnRecipe(uint32 recipeID) { - char *query = 0; - uint32 qlen; - uint32 qcount = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - qlen = MakeAnyLenString(&query, "SELECT tr.name, crl.madecount " - " FROM tradeskill_recipe as tr " - " LEFT JOIN (SELECT recipe_id, madecount FROM char_recipe_list WHERE char_id = %u) AS crl " - " ON tr.id = crl.recipe_id " - " WHERE tr.id = %u ;", CharacterID(), recipeID); - - if (!database.RunQuery(query, qlen, errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT tr.name, crl.madecount " + "FROM tradeskill_recipe AS tr " + "LEFT JOIN (SELECT recipe_id, madecount " + "FROM char_recipe_list WHERE char_id = %u) AS crl " + "ON tr.id = crl.recipe_id " + "WHERE tr.id = %u ;", CharacterID(), recipeID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Client::LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - qcount = mysql_num_rows(result); - if (qcount != 1) { - LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, qcount); - mysql_free_result(result); - safe_delete_array(query); + if (results.RowCount() != 1) { + LogFile->write(EQEMuLog::Normal, "Client::LearnRecipe - RecipeID: %d had %d occurences.", recipeID, results.RowCount()); return; } - safe_delete_array(query); - row = mysql_fetch_row(result); + auto row = results.begin(); - if (row != nullptr && row[0] != nullptr) { - // Only give Learn message if character doesn't know the recipe - if (row[1] == nullptr) { - Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]); - // Actually learn the recipe now - qlen = MakeAnyLenString(&query, "INSERT INTO char_recipe_list " - " SET recipe_id = %u, char_id = %u, madecount = 0 " - " ON DUPLICATE KEY UPDATE madecount = madecount;" - , recipeID, CharacterID()); + if (row[0] == nullptr) + return; - if (!database.RunQuery(query, qlen, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query, errbuf); - } - safe_delete_array(query); - } - } + // Only give Learn message if character doesn't know the recipe + if (row[1] != nullptr) + return; - mysql_free_result(result); + Message_StringID(4, TRADESKILL_LEARN_RECIPE, row[0]); + // Actually learn the recipe now + query = StringFormat("INSERT INTO char_recipe_list " + "SET recipe_id = %u, char_id = %u, madecount = 0 " + "ON DUPLICATE KEY UPDATE madecount = madecount;", + recipeID, CharacterID()); + results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in LearnRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); } @@ -1622,33 +1511,22 @@ bool Client::CanIncreaseTradeskill(SkillUseTypes tradeskill) { bool ZoneDatabase::EnableRecipe(uint32 recipe_id) { - char *query = 0; - uint32 qlen; - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 affected_rows = 0; + std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 1 " + "WHERE id = %u;", recipe_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 1 WHERE id = %u;", recipe_id); - - if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) { - LogFile->write(EQEMuLog::Error, "Error in EnableRecipe query '%s': %s", query, errbuf); - } - safe_delete_array(query); - - return (affected_rows > 0); + return results.RowsAffected() > 0; } bool ZoneDatabase::DisableRecipe(uint32 recipe_id) { - char *query = 0; - uint32 qlen; - char errbuf[MYSQL_ERRMSG_SIZE]; - uint32 affected_rows = 0; + std::string query = StringFormat("UPDATE tradeskill_recipe SET enabled = 0 " + "WHERE id = %u;", recipe_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - qlen = MakeAnyLenString(&query, "UPDATE tradeskill_recipe SET enabled = 0 WHERE id = %u;", recipe_id); - - if (!RunQuery(query, qlen, errbuf, 0, &affected_rows)) { - LogFile->write(EQEMuLog::Error, "Error in DisableRecipe query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return (affected_rows > 0); + return results.RowsAffected() > 0; } diff --git a/zone/trading.cpp b/zone/trading.cpp index c89af4010..738e763ed 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -133,7 +133,7 @@ void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { client->Kick(); return; } - + SendItemData(inst, trade_slot_id); _log(TRADING__HOLDER, "%s added item '%s' to trade slot %i", owner->GetName(), inst->GetItem()->Name, trade_slot_id); @@ -451,13 +451,13 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st mlog(TRADING__CLIENT, "Finishing trade with client %s", other->GetName()); this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true); - + // step 0: pre-processing // QS code if (RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) { qs_audit = (QSPlayerLogTrade_Struct*)event_entry; qs_log = true; - + if (finalizer) { qs_audit->char2_id = this->character_id; @@ -506,7 +506,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st detail->aug_5 = inst->GetAugmentItemID(5); event_details->push_back(detail); - + if (finalizer) qs_audit->char2_count += detail->charges; else @@ -886,7 +886,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (baginst) { const Item_Struct* bagitem = baginst->GetItem(); if (bagitem && (GetGM() || (bagitem->NoDrop != 0 && baginst->IsInstNoDrop() == false))) { - tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist, + tradingWith->CastToNPC()->AddLootDrop(bagitem, &tradingWith->CastToNPC()->itemlist, baginst->GetCharges(), 1, 127, true, true); } else if (RuleB(NPC, ReturnNonQuestNoDropItems)) { @@ -895,8 +895,8 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st } } } - - tradingWith->CastToNPC()->AddLootDrop(item, &tradingWith->CastToNPC()->itemlist, + + tradingWith->CastToNPC()->AddLootDrop(item, &tradingWith->CastToNPC()->itemlist, inst->GetCharges(), 1, 127, true, true); } // Return NO DROP and Attuned items being handed into a non-quest NPC if the rule is true @@ -913,14 +913,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if(!tradingWith->IsMoving()) tradingWith->FaceTarget(this); - /* Set a timestamp in an entity variable for plugin check_handin.pl in return_items - This will stopgap players from items being returned if global_npc.pl has a catch all return_items - */ - struct timeval read_time; - char buffer[50]; - gettimeofday(&read_time, 0); - sprintf(buffer, "%li.%li \n", read_time.tv_sec, read_time.tv_usec); - this->SetEntityVariable("Stop_Return", buffer); + this->EVENT_ITEM_ScriptStopReturn(); } } @@ -938,12 +931,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st parse->AddVar(temp1, temp2); snprintf(temp1, 100, "platinum.%d", tradingWith->GetNPCTypeID()); snprintf(temp2, 100, "%u", trade->pp); - parse->AddVar(temp1, temp2); + parse->AddVar(temp1, temp2); if(tradingWith->GetAppearance() != eaDead) { tradingWith->FaceTarget(this); } - + ItemInst *insts[4] = { 0 }; for(int i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_NPC_END; ++i) { insts[i - EmuConstants::TRADE_BEGIN] = m_inv.PopItem(i); @@ -1475,18 +1468,16 @@ void Client::TradeRequestFailed(const EQApplicationPacket* app) { } -static void BazaarAuditTrail(const char *Seller, const char *Buyer, const char *ItemName, int Quantity, int TotalCost, int TranType) { +static void BazaarAuditTrail(const char *seller, const char *buyer, const char *itemName, int quantity, int totalCost, int tranType) { - const char *AuditQuery="INSERT INTO `trader_audit` (`time`, `seller`, `buyer`, `itemname`, `quantity`, `totalcost`, `trantype`) " - "VALUES (NOW(), '%s', '%s', '%s', %i, %i, %i)"; + std::string query = StringFormat("INSERT INTO `trader_audit` " + "(`time`, `seller`, `buyer`, `itemname`, `quantity`, `totalcost`, `trantype`) " + "VALUES (NOW(), '%s', '%s', '%s', %i, %i, %i)", + seller, buyer, itemName, quantity, totalCost, tranType); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(TRADING__CLIENT, "Audit write error: %s : %s", query.c_str(), results.ErrorMessage().c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - if(!database.RunQuery(query, MakeAnyLenString(&query, AuditQuery, Seller, Buyer, ItemName, Quantity, TotalCost, TranType), errbuf)) - _log(TRADING__CLIENT, "Audit write error: %s : %s", query, errbuf); - - safe_delete_array(query); } @@ -1624,365 +1615,326 @@ void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* Trader,const EQApplicat } -void Client::SendBazaarWelcome(){ +void Client::SendBazaarWelcome() +{ + const std::string query = "SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader"; + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() == 1){ + auto row = results.begin(); - char errbuf[MYSQL_ERRMSG_SIZE]; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); - char* query = 0; + memset(outapp->pBuffer,0,outapp->size); - MYSQL_RES *result; + BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; - MYSQL_ROW row; + bws->Beginning.Action = BazaarWelcome; - if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct char_id),count(char_id) from trader"),errbuf,&result)){ - if(mysql_num_rows(result)==1){ + bws->Traders = atoi(row[0]); + bws->Items = atoi(row[1]); - row = mysql_fetch_row(result); + QueuePacket(outapp); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarWelcome_Struct)); - - memset(outapp->pBuffer,0,outapp->size); - - BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer; - - bws->Beginning.Action = BazaarWelcome; - - bws->Items = atoi(row[1]); - - bws->Traders = atoi(row[0]); - - QueuePacket(outapp); - - safe_delete(outapp); - } - mysql_free_result(result); + safe_delete(outapp); } - safe_delete_array(query); - if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct charid) from buyer"),errbuf,&result)){ - if(mysql_num_rows(result)==1) { + const std::string buyerCountQuery = "SELECT COUNT(DISTINCT charid) FROM buyer"; + results = database.QueryDatabase(buyerCountQuery); + if (!results.Success() || results.RowCount() != 1) + return; - row = mysql_fetch_row(result); - - Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them," - " or use /buyer to set up your own Buy Lines.", atoi(row[0])); - } - mysql_free_result(result); - } - safe_delete_array(query); + auto row = results.begin(); + Message(10, "There are %i Buyers waiting to purchase your loot. Type /barter to search for them, " + "or use /buyer to set up your own Buy Lines.", atoi(row[0])); } void Client::SendBazaarResults(uint32 TraderID, uint32 Class_, uint32 Race, uint32 ItemStat, uint32 Slot, uint32 Type, char Name[64], uint32 MinPrice, uint32 MaxPrice) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - std::string Search, Values; - MYSQL_RES *Result; - MYSQL_ROW Row; - char Tmp[100] = {0}; + std::string searchValues = " COUNT(item_id), trader.*, items.name "; + std::string searchCriteria = " WHERE trader.item_id = items.id "; - Values.append("count(item_id),trader.*,items.name"); + if(TraderID > 0) { + Client* trader = entity_list.GetClientByID(TraderID); - Search.append("where trader.item_id=items.id"); + if(trader) + searchCriteria.append(StringFormat(" AND trader.char_id = %i", trader->CharacterID())); + } - if(TraderID > 0){ - Client* Trader = entity_list.GetClientByID(TraderID); + if(MinPrice != 0) + searchCriteria.append(StringFormat(" AND trader.item_cost >= %i", MinPrice)); - if(Trader){ - sprintf(Tmp," and trader.char_id=%i",Trader->CharacterID()); - Search.append(Tmp); - } + if(MaxPrice != 0) + searchCriteria.append(StringFormat(" AND trader.item_cost <= %i", MaxPrice)); + if(strlen(Name) > 0) { + char *safeName = RemoveApostrophes(Name); + searchCriteria.append(StringFormat(" AND items.name LIKE '%%%s%%'", safeName)); + safe_delete_array(safeName); } - std::string SearchrResults; - if(MinPrice != 0){ - sprintf(Tmp, " and trader.item_cost>=%i", MinPrice); - Search.append(Tmp); - } - if(MaxPrice != 0){ - sprintf(Tmp, " and trader.item_cost<=%i", MaxPrice); - Search.append(Tmp); - } - if(strlen(Name) > 0){ - char *SafeName = RemoveApostrophes(Name); - sprintf(Tmp, " and items.name like '%%%s%%'", SafeName); - safe_delete_array(SafeName); - Search.append(Tmp); - } - if(Class_ != 0xFFFFFFFF){ - sprintf(Tmp, " and mid(reverse(bin(items.classes)),%i,1)=1", Class_); - Search.append(Tmp); - } - if(Race!=0xFFFFFFFF){ - sprintf(Tmp, " and mid(reverse(bin(items.races)),%i,1)=1", Race); - Search.append(Tmp); - } - if(Slot!=0xFFFFFFFF){ - sprintf(Tmp, " and mid(reverse(bin(items.slots)),%i,1)=1", Slot + 1); - Search.append(Tmp); - } - if(Type!=0xFFFFFFFF){ + if(Class_ != 0xFFFFFFFF) + searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.classes)), %i, 1) = 1", Class_)); - switch(Type){ + if(Race != 0xFFFFFFFF) + searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.races)), %i, 1) = 1", Race)); - case 0: - // 1H Slashing - Search.append(" and items.itemtype=0 and damage>0"); - break; - case 31: - Search.append(" and items.itemclass=2"); - break; - case 46: - Search.append(" and items.spellid>0 and items.spellid<65000"); - break; - case 47: - Search.append(" and items.spellid=998"); - break; - case 48: - Search.append(" and items.spellid>=1298 and items.spellid<=1307"); - break; - case 49: - Search.append(" and items.focuseffect>0"); - break; - default: - sprintf(Tmp, " and items.itemtype=%i", Type); - Search.append(Tmp); - } - } + if(Slot != 0xFFFFFFFF) + searchCriteria.append(StringFormat(" AND MID(REVERSE(BIN(items.slots)), %i, 1) = 1", Slot + 1)); + + switch(Type){ + case 0xFFFFFFFF: + break; + case 0: + // 1H Slashing + searchCriteria.append(" AND items.itemtype = 0 AND damage > 0"); + break; + case 31: + searchCriteria.append(" AND items.itemclass = 2"); + break; + case 46: + searchCriteria.append(" AND items.spellid > 0 AND items.spellid < 65000"); + break; + case 47: + searchCriteria.append(" AND items.spellid = 998"); + break; + case 48: + searchCriteria.append(" AND items.spellid >= 1298 AND items.spellid <= 1307"); + break; + case 49: + searchCriteria.append(" AND items.focuseffect > 0"); + break; + + default: + searchCriteria.append(StringFormat(" AND items.itemtype = %i", Type)); + } switch(ItemStat) { case STAT_AC: - Search.append(" and items.ac>0"); - Values.append(",items.ac"); + searchCriteria.append(" AND items.ac > 0"); + searchValues.append(", items.ac"); break; case STAT_AGI: - Search.append(" and items.aagi>0"); - Values.append(",items.aagi"); + searchCriteria.append(" AND items.aagi > 0"); + searchValues.append(", items.aagi"); break; case STAT_CHA: - Search.append(" and items.acha>0"); - Values.append(",items.acha"); + searchCriteria.append(" AND items.acha > 0"); + searchValues.append(", items.acha"); break; case STAT_DEX: - Search.append(" and items.adex>0"); - Values.append(",items.adex"); + searchCriteria.append(" AND items.adex > 0"); + searchValues.append(", items.adex"); break; case STAT_INT: - Search.append(" and items.aint>0"); - Values.append(",items.aint"); + searchCriteria.append(" AND items.aint > 0"); + searchValues.append(", items.aint"); break; case STAT_STA: - Search.append(" and items.asta>0"); - Values.append(",items.asta"); + searchCriteria.append(" AND items.asta > 0"); + searchValues.append(", items.asta"); break; case STAT_STR: - Search.append(" and items.astr>0"); - Values.append(",items.astr"); + searchCriteria.append(" AND items.astr > 0"); + searchValues.append(", items.astr"); break; case STAT_WIS: - Search.append(" and items.awis>0"); - Values.append(",items.awis"); + searchCriteria.append(" AND items.awis > 0"); + searchValues.append(", items.awis"); break; case STAT_COLD: - Search.append(" and items.cr>0"); - Values.append(",items.cr"); + searchCriteria.append(" AND items.cr > 0"); + searchValues.append(", items.cr"); break; case STAT_DISEASE: - Search.append(" and items.dr>0"); - Values.append(",items.dr"); + searchCriteria.append(" AND items.dr > 0"); + searchValues.append(", items.dr"); break; case STAT_FIRE: - Search.append(" and items.fr>0"); - Values.append(",items.fr"); + searchCriteria.append(" AND items.fr > 0"); + searchValues.append(", items.fr"); break; case STAT_MAGIC: - Values.append(",items.mr"); - Search.append(" and items.mr>0"); + searchCriteria.append(" AND items.mr > 0"); + searchValues.append(", items.mr"); break; case STAT_POISON: - Search.append(" and items.pr>0"); - Values.append(",items.pr"); + searchCriteria.append(" AND items.pr > 0"); + searchValues.append(", items.pr"); break; case STAT_HP: - Search.append(" and items.hp>0"); - Values.append(",items.hp"); + searchCriteria.append(" AND items.hp > 0"); + searchValues.append(", items.hp"); break; case STAT_MANA: - Search.append(" and items.mana>0"); - Values.append(",items.mana"); + searchCriteria.append(" AND items.mana > 0"); + searchValues.append(", items.mana"); break; case STAT_ENDURANCE: - Search.append(" and items.endur>0"); - Values.append(",items.endur"); + searchCriteria.append(" AND items.endur > 0"); + searchValues.append(", items.endur"); break; case STAT_ATTACK: - Search.append(" and items.attack>0"); - Values.append(",items.attack"); + searchCriteria.append(" AND items.attack > 0"); + searchValues.append(", items.attack"); break; case STAT_HP_REGEN: - Search.append(" and items.regen>0"); - Values.append(",items.regen"); + searchCriteria.append(" AND items.regen > 0"); + searchValues.append(", items.regen"); break; case STAT_MANA_REGEN: - Search.append(" and items.manaregen>0"); - Values.append(",items.manaregen"); + searchCriteria.append(" AND items.manaregen > 0"); + searchValues.append(", items.manaregen"); break; case STAT_HASTE: - Search.append(" and items.haste>0"); - Values.append(",items.haste"); + searchCriteria.append(" AND items.haste > 0"); + searchValues.append(", items.haste"); break; case STAT_DAMAGE_SHIELD: - Search.append(" and items.damageshield>00"); - Values.append(",items.damageshield"); + searchCriteria.append(" AND items.damageshield > 0"); + searchValues.append(", items.damageshield"); break; default: - Values.append(",0"); + searchValues.append(", 0"); break; } - Values.append(",sum(charges), items.stackable "); + std::string query = StringFormat("SELECT %s, SUM(charges), items.stackable " + "FROM trader, items %s GROUP BY items.id, charges, char_id LIMIT %i", + searchValues.c_str(), searchCriteria.c_str(), RuleI(Bazaar, MaxSearchResults)); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + _log(TRADING__CLIENT, "Failed to retrieve Bazaar Search!! %s %s\n", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if (database.RunQuery(Query,MakeAnyLenString(&Query, "select %s from trader,items %s group by items.id,charges,char_id limit %i", - Values.c_str(),Search.c_str(), RuleI(Bazaar, MaxSearchResults)),errbuf,&Result)){ + _log(TRADING__CLIENT, "SRCH: %s", query.c_str()); - _log(TRADING__CLIENT, "SRCH: %s", Query); - safe_delete_array(Query); + int Size = 0; + uint32 ID = 0; - int Size = 0; - uint32 ID = 0; - - if(mysql_num_rows(Result) == static_cast(RuleI(Bazaar, MaxSearchResults))) + if (results.RowCount() == static_cast(RuleI(Bazaar, MaxSearchResults))) Message(15, "Your search reached the limit of %i results. Please narrow your search down by selecting more options.", RuleI(Bazaar, MaxSearchResults)); - if(mysql_num_rows(Result) == 0){ - EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct)); - BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer; - brds->TraderID = ID; - brds->Type = BazaarSearchDone; - brds->Unknown008 = 0xFFFFFFFF; - brds->Unknown012 = 0xFFFFFFFF; - brds->Unknown016 = 0xFFFFFFFF; - this->QueuePacket(outapp2); - _pkt(TRADING__PACKETS,outapp2); - safe_delete(outapp2); - mysql_free_result(Result); - return; - } - Size = mysql_num_rows(Result) * sizeof(BazaarSearchResults_Struct); - uchar *buffer = new uchar[Size]; - uchar *bufptr = buffer; - memset(buffer, 0, Size); - - int Action = BazaarSearchResults; - uint32 Cost = 0; - int32 SerialNumber = 0; - char Name[64] = {0}; - int Count = 0; - uint32 StatValue=0; - - while ((Row = mysql_fetch_row(Result))) { - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action); - Count = atoi(Row[0]); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count); - SerialNumber = atoi(Row[3]); - VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber); - Client* Trader2=entity_list.GetClientByCharID(atoi(Row[1])); - if(Trader2){ - ID = Trader2->GetID(); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID); - } - else{ - _log(TRADING__CLIENT, "Unable to find trader: %i\n",atoi(Row[1])); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0); - } - Cost = atoi(Row[5]); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost); - StatValue = atoi(Row[8]); - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue); - bool Stackable = atoi(Row[10]); - if(Stackable) { - int Charges = atoi(Row[9]); - sprintf(Name, "%s(%i)", Row[7], Charges); - } - else - sprintf(Name,"%s(%i)",Row[7], Count); - - memcpy(bufptr,&Name, strlen(Name)); - - bufptr += 64; - - // Extra fields for SoD+ - // - if(Trader2) - sprintf(Name, "%s", Trader2->GetName()); - else - sprintf(Name, "Unknown"); - - memcpy(bufptr,&Name, strlen(Name)); - - bufptr += 64; - - VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(Row[1])); // ItemID - } - mysql_free_result(Result); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, Size); - - memcpy(outapp->pBuffer, buffer, Size); - - this->QueuePacket(outapp); - - _pkt(TRADING__PACKETS,outapp); - - safe_delete(outapp); - safe_delete_array(buffer); - + if(results.RowCount() == 0) { EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct)); BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer; - brds->TraderID = ID; brds->Type = BazaarSearchDone; - brds->Unknown008 = 0xFFFFFFFF; brds->Unknown012 = 0xFFFFFFFF; brds->Unknown016 = 0xFFFFFFFF; - this->QueuePacket(outapp2); - _pkt(TRADING__PACKETS,outapp2); safe_delete(outapp2); - - } - else{ - _log(TRADING__CLIENT, "Failed to retrieve Bazaar Search!! %s %s\n", Query, errbuf); - safe_delete_array(Query); return; } + + Size = results.RowCount() * sizeof(BazaarSearchResults_Struct); + uchar *buffer = new uchar[Size]; + uchar *bufptr = buffer; + memset(buffer, 0, Size); + + int Action = BazaarSearchResults; + uint32 Cost = 0; + int32 SerialNumber = 0; + char temp_buffer[64] = {0}; + int Count = 0; + uint32 StatValue=0; + + for (auto row = results.begin(); row != results.end(); ++row) { + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Action); + Count = atoi(row[0]); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Count); + SerialNumber = atoi(row[3]); + VARSTRUCT_ENCODE_TYPE(int32, bufptr, SerialNumber); + Client* Trader2=entity_list.GetClientByCharID(atoi(row[1])); + if(Trader2){ + ID = Trader2->GetID(); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, ID); + } + else{ + _log(TRADING__CLIENT, "Unable to find trader: %i\n",atoi(row[1])); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0); + } + Cost = atoi(row[5]); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, Cost); + StatValue = atoi(row[8]); + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, StatValue); + bool Stackable = atoi(row[10]); + if(Stackable) { + int Charges = atoi(row[9]); + sprintf(temp_buffer, "%s(%i)", row[7], Charges); + } + else + sprintf(temp_buffer,"%s(%i)",row[7], Count); + + memcpy(bufptr,&temp_buffer, strlen(temp_buffer)); + + bufptr += 64; + + // Extra fields for SoD+ + // + if(Trader2) + sprintf(temp_buffer, "%s", Trader2->GetName()); + else + sprintf(temp_buffer, "Unknown"); + + memcpy(bufptr,&temp_buffer, strlen(temp_buffer)); + + bufptr += 64; + + VARSTRUCT_ENCODE_TYPE(uint32, bufptr, atoi(row[1])); // ItemID + } + + EQApplicationPacket* outapp = new EQApplicationPacket(OP_BazaarSearch, Size); + + memcpy(outapp->pBuffer, buffer, Size); + + this->QueuePacket(outapp); + + _pkt(TRADING__PACKETS,outapp); + + safe_delete(outapp); + safe_delete_array(buffer); + + EQApplicationPacket* outapp2 = new EQApplicationPacket(OP_BazaarSearch, sizeof(BazaarReturnDone_Struct)); + BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer; + + brds->TraderID = ID; + brds->Type = BazaarSearchDone; + + brds->Unknown008 = 0xFFFFFFFF; + brds->Unknown012 = 0xFFFFFFFF; + brds->Unknown016 = 0xFFFFFFFF; + + this->QueuePacket(outapp2); + + _pkt(TRADING__PACKETS,outapp2); + safe_delete(outapp2); } static void UpdateTraderCustomerItemsAdded(uint32 CustomerID, TraderCharges_Struct* gis, uint32 ItemID) { @@ -2304,103 +2256,92 @@ void Client::HandleTraderPriceUpdate(const EQApplicationPacket *app) { } -void Client::SendBuyerResults(char* SearchString, uint32 SearchID) { +void Client::SendBuyerResults(char* searchString, uint32 searchID) { // This method is called when a potential seller in the /barter window searches for matching buyers // - _log(TRADING__BARTER, "Client::SendBuyerResults %s\n", SearchString); + _log(TRADING__BARTER, "Client::SendBuyerResults %s\n", searchString); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - char ItemName[64]; - std::string Search, Values; - MYSQL_RES *Result; - MYSQL_ROW Row; + char* escSearchString = new char[strlen(searchString) * 2 + 1]; + database.DoEscapeString(escSearchString, searchString, strlen(searchString)); - char*EscSearchString = new char[strlen(SearchString) * 2 + 1]; - database.DoEscapeString(EscSearchString, SearchString, strlen(SearchString)); + std::string query = StringFormat("SELECT * FROM buyer WHERE itemname LIKE '%%%s%%' ORDER BY charid LIMIT %i", + escSearchString, RuleI(Bazaar, MaxBarterSearchResults)); + safe_delete_array(escSearchString); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + _log(TRADING__CLIENT, "Failed to retrieve Barter Search!! %s %s\n", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if (database.RunQuery(Query,MakeAnyLenString(&Query, "select * from buyer where itemname like '%%%s%%' order by charid limit %i", - EscSearchString, RuleI(Bazaar, MaxBarterSearchResults)), errbuf, &Result)) { + int numberOfRows = results.RowCount(); - int NumberOfRows = mysql_num_rows(Result); + if(numberOfRows == RuleI(Bazaar, MaxBarterSearchResults)) + Message(15, "Your search found too many results; some are not displayed."); + else if(strlen(searchString) == 0) + Message(10, "There are %i Buy Lines.", numberOfRows); + else + Message(10, "There are %i Buy Lines that match the search string '%s'.", numberOfRows, searchString); - if(NumberOfRows == RuleI(Bazaar, MaxBarterSearchResults)) - Message(15, "Your search found too many results; some are not displayed."); - else { - if(strlen(SearchString) == 0) - Message(10, "There are %i Buy Lines.", NumberOfRows); - else - Message(10, "There are %i Buy Lines that match the search string '%s'.", - NumberOfRows, SearchString); + if(numberOfRows == 0) + return; + + uint32 lastCharID = 0; + Client *buyer = nullptr; + + for (auto row = results.begin(); row != results.end(); ++row) { + char itemName[64]; + + uint32 charID = atoi(row[0]); + uint32 buySlot = atoi(row[1]); + uint32 itemID = atoi(row[2]); + strcpy(itemName, row[3]); + uint32 quantity = atoi(row[4]); + uint32 price = atoi(row[5]); + + // Each item in the search results is sent as a single fixed length packet, although the position of + // the fields varies due to the use of variable length strings. The reason the packet is so big, is + // to allow item compensation, e.g. a buyer could offer to buy a Blade Of Carnage for 10000pp plus + // other items in exchange. Item compensation is not currently supported in EQEmu. + // + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 940); + + char *buf = (char *)outapp->pBuffer; + + const Item_Struct* item = database.GetItem(itemID); + + if(!item) + continue; + + // Save having to scan the client list when dealing with multiple buylines for the same Character. + if(charID != lastCharID) { + buyer = entity_list.GetClientByCharID(charID); + lastCharID = charID; } - if(NumberOfRows == 0) { - mysql_free_result(Result); - safe_delete_array(Query); - return; - } + if(!buyer) + continue; - uint32 LastCharID = 0; - Client *Buyer = nullptr; + VARSTRUCT_ENCODE_TYPE(uint32, buf, Barter_BuyerSearchResults); // Command + VARSTRUCT_ENCODE_TYPE(uint32, buf, searchID); // Match up results with the request + VARSTRUCT_ENCODE_TYPE(uint32, buf, buySlot); // Slot in this Buyer's list + VARSTRUCT_ENCODE_TYPE(uint8, buf, 0x01); // Unknown - probably a flag field + VARSTRUCT_ENCODE_TYPE(uint32, buf, itemID); // ItemID + VARSTRUCT_ENCODE_STRING(buf, itemName); // Itemname + VARSTRUCT_ENCODE_TYPE(uint32, buf, item->Icon); // Icon + VARSTRUCT_ENCODE_TYPE(uint32, buf, quantity); // Quantity + VARSTRUCT_ENCODE_TYPE(uint8, buf, 0x01); // Unknown - probably a flag field + VARSTRUCT_ENCODE_TYPE(uint32, buf, price); // Price + VARSTRUCT_ENCODE_TYPE(uint32, buf, buyer->GetID()); // Entity ID + VARSTRUCT_ENCODE_TYPE(uint32, buf, 0); // Flag for + Items , probably ItemCount + VARSTRUCT_ENCODE_STRING(buf, buyer->GetName()); // Seller Name - while ((Row = mysql_fetch_row(Result))) { + _pkt(TRADING__BARTER, outapp); - uint32 CharID = atoi(Row[0]); - uint32 BuySlot = atoi(Row[1]); - uint32 ItemID = atoi(Row[2]); - strcpy(ItemName, Row[3]); - uint32 Quantity = atoi(Row[4]); - uint32 Price = atoi(Row[5]); + QueuePacket(outapp); + safe_delete(outapp); + } - // Each item in the search results is sent as a single fixed length packet, although the position of - // the fields varies due to the use of variable length strings. The reason the packet is so big, is - // to allow item compensation, e.g. a buyer could offer to buy a Blade Of Carnage for 10000pp plus - // other items in exchange. Item compensation is not currently supported in EQEmu. - // - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 940); - - char *Buf = (char *)outapp->pBuffer; - - const Item_Struct* item = database.GetItem(ItemID); - - if(!item) continue; - - // Save having to scan the client list when dealing with multiple buylines for the same Character. - if(CharID != LastCharID) { - Buyer = entity_list.GetClientByCharID(CharID); - LastCharID = CharID; - } - - if(!Buyer) continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerSearchResults); // Command - VARSTRUCT_ENCODE_TYPE(uint32, Buf, SearchID); // Match up results with the request - VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); // Slot in this Buyer's list - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); // Unknown - probably a flag field - VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); // ItemID - VARSTRUCT_ENCODE_STRING(Buf, ItemName); // Itemname - VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); // Icon - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); // Quantity - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 0x01); // Unknown - probably a flag field - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); // Price - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); // Entity ID - VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); // Flag for + Items , probably ItemCount - VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName()); // Seller Name - - _pkt(TRADING__BARTER, outapp); - - QueuePacket(outapp); - safe_delete(outapp); - } - - mysql_free_result(Result); - } - else{ - _log(TRADING__CLIENT, "Failed to retrieve Barter Search!! %s %s\n", Query, errbuf); - } - safe_delete_array(Query); - safe_delete_array(EscSearchString); } void Client::ShowBuyLines(const EQApplicationPacket *app) { @@ -2443,60 +2384,44 @@ void Client::ShowBuyLines(const EQApplicationPacket *app) { safe_delete(outapp); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - char ItemName[64]; - std::string Search, Values; - MYSQL_RES *Result; - MYSQL_ROW Row; + std::string query = StringFormat("SELECT * FROM buyer WHERE charid = %i", Buyer->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return; - if (database.RunQuery(Query,MakeAnyLenString(&Query, "select * from buyer where charid = %i", - Buyer->CharacterID()),errbuf,&Result)){ + for (auto row = results.begin(); row != results.end(); ++row) { + char ItemName[64]; + uint32 BuySlot = atoi(row[1]); + uint32 ItemID = atoi(row[2]); + strcpy(ItemName, row[3]); + uint32 Quantity = atoi(row[4]); + uint32 Price = atoi(row[5]); - if(mysql_num_rows(Result) == 0) { + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 936); - safe_delete_array(Query); + char *Buf = (char *)outapp->pBuffer; - mysql_free_result(Result); + const Item_Struct* item = database.GetItem(ItemID); - return; - } + if(!item) + continue; - while ((Row = mysql_fetch_row(Result))) { + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerInspectWindow); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); + VARSTRUCT_ENCODE_STRING(Buf, ItemName); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); + VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); + VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); + VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName()); - uint32 BuySlot = atoi(Row[1]); - uint32 ItemID = atoi(Row[2]); - strcpy(ItemName, Row[3]); - uint32 Quantity = atoi(Row[4]); - uint32 Price = atoi(Row[5]); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Barter, 936); - - char *Buf = (char *)outapp->pBuffer; - - const Item_Struct* item = database.GetItem(ItemID); - - if(!item) continue; - - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerInspectWindow); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, BuySlot); - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buf, ItemID); - VARSTRUCT_ENCODE_STRING(Buf, ItemName); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, item->Icon); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Quantity); - VARSTRUCT_ENCODE_TYPE(uint8, Buf, 1); // Flag - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Price); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, Buyer->GetID()); - VARSTRUCT_ENCODE_TYPE(uint32, Buf, 0); - VARSTRUCT_ENCODE_STRING(Buf, Buyer->GetName()); - - _pkt(TRADING__BARTER, outapp); - QueuePacket(outapp); - } - mysql_free_result(Result); - } - safe_delete_array(Query); + _pkt(TRADING__BARTER, outapp); + QueuePacket(outapp); + } } void Client::SellToBuyer(const EQApplicationPacket *app) { diff --git a/zone/tribute.cpp b/zone/tribute.cpp index 95da9b5a3..08bbf4662 100644 --- a/zone/tribute.cpp +++ b/zone/tribute.cpp @@ -254,8 +254,10 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) { return(0); } - //make sure they have enough of them - //and remove it from inventory + /* + Make sure they have enough of them + and remove it from inventory + */ if(inst->IsStackable()) { if(inst->GetCharges() < (int32)quantity) //dont have enough.... return(0); @@ -267,7 +269,7 @@ int32 Client::TributeItem(uint32 slot, uint32 quantity) { pts *= quantity; - //add the tribute value in points + /* Add the tribute value in points */ AddTributePoints(pts); return(pts); } @@ -279,7 +281,7 @@ int32 Client::TributeMoney(uint32 platinum) { return(0); } - //add the tribute value in points + /* Add the tribute value in points */ AddTributePoints(platinum); return(platinum); } diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index a36bcec8b..cfefe73f3 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -1084,14 +1084,14 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) // how much it's allowed to be off by #define _GASSIGN_TOLERANCE 1.0 - if(results.RowCount() == 0) // try a fuzzy match if that didn't find it + if (results.RowCount() == 0) // try a fuzzy match if that didn't find it { query = StringFormat("SELECT id,x,y FROM spawn2 WHERE zone='%s' AND " "ABS( ABS(x) - ABS(%f) ) < %f AND " "ABS( ABS(y) - ABS(%f) ) < %f", zone->GetShortName(), x, _GASSIGN_TOLERANCE, y, _GASSIGN_TOLERANCE); results = QueryDatabase(query); - if(!results.Success()) { + if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error querying fuzzy spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); return; } @@ -1100,12 +1100,13 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) matches = results.RowCount(); } - if (matches == 0) { + if (matches == 0) + { client->Message(0, "ERROR: Unable to assign grid - can't find it in spawn2"); return; } - if(matches == 1) + if(matches > 1) { client->Message(0, "ERROR: Unable to assign grid - multiple spawn2 rows match"); return; @@ -1125,7 +1126,8 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) return; } - if (results.RowsAffected() != 1) { + if (results.RowsAffected() != 1) + { client->Message(0, "ERROR: found spawn2 id %d but the update query failed", spawn2id); return; } @@ -1133,7 +1135,8 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) if(client) client->LogSQL(query.c_str()); - if(!fuzzy) { + if (!fuzzy) + { client->Message(0, "Grid assign: spawn2 id = %d updated - exact match", spawn2id); return; } diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 791a34ee4..be0a1a193 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -168,18 +168,24 @@ void WorldServer::Process() { break; } case ServerOP_ChannelMessage: { - if (!ZoneLoaded) break; + if (!ZoneLoaded) + break; ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer; if (scm->deliverto[0] == 0) { entity_list.ChannelMessageFromWorld(scm->from, scm->to, scm->chan_num, scm->guilddbid, scm->language, scm->message); - } - else { - Client* client; - client = entity_list.GetClientByName(scm->deliverto); - if (client != 0) { + } else { + Client* client = entity_list.GetClientByName(scm->deliverto); + if (client) { if (client->Connected()) { - client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); - if (!scm->noreply && scm->chan_num!=2) { //dont echo on group chat + if (scm->queued == 1) // tell was queued + client->Tell_StringID(QUEUED_TELL, scm->to, scm->message); + else if (scm->queued == 2) // tell queue was full + client->Tell_StringID(QUEUE_TELL_FULL, scm->to, scm->message); + else if (scm->queued == 3) // person was offline + client->Message_StringID(MT_TellEcho, TOLD_NOT_ONLINE, scm->to); + else // normal stuff + client->ChannelMessageSend(scm->from, scm->to, scm->chan_num, scm->language, scm->message); + if (!scm->noreply && scm->chan_num != 2) { //dont echo on group chat // if it's a tell, echo back so it shows up scm->noreply = true; scm->chan_num = 14; @@ -322,7 +328,7 @@ void WorldServer::Process() { entity->CastToMob()->SetZone(ztz->requested_zone_id, ztz->requested_instance_id); - if(ztz->ignorerestrictions == 3) + if(ztz->ignorerestrictions == 3) entity->CastToClient()->GoToSafeCoords(ztz->requested_zone_id, ztz->requested_instance_id); } @@ -995,9 +1001,11 @@ void WorldServer::Process() { char AssistName[64]; char PullerName[64]; char NPCMarkerName[64]; + char mentoree_name[64]; + int mentor_percent; GroupLeadershipAA_Struct GLAA; memset(ln, 0, 64); - strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, &GLAA)); + strcpy(ln, database.GetGroupLeadershipInfo(group->GetID(), ln, MainTankName, AssistName, PullerName, NPCMarkerName, mentoree_name, &mentor_percent, &GLAA)); Client *lc = entity_list.GetClientByName(ln); if(lc) group->SetLeader(lc); @@ -1007,6 +1015,7 @@ void WorldServer::Process() { group->SetPuller(PullerName); group->SetNPCMarker(NPCMarkerName); group->SetGroupAAs(&GLAA); + group->SetGroupMentor(mentor_percent, mentoree_name); } } @@ -1366,6 +1375,18 @@ void WorldServer::Process() { break; } + case ServerOP_RaidMOTD: { + ServerRaidMOTD_Struct *rmotd = (ServerRaidMOTD_Struct *)pack->pBuffer; + if (!zone) + break; + Raid *r = entity_list.GetRaidByID(rmotd->rid); + if (!r) + break; + r->SetRaidMOTD(std::string(rmotd->motd)); + r->SendRaidMOTD(); + break; + } + case ServerOP_SpawnPlayerCorpse: { SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer; Corpse* NewCorpse = database.LoadPlayerCorpse(s->player_corpse_id); @@ -1777,6 +1798,24 @@ void WorldServer::Process() { break; } + case ServerOP_CZSetEntityVariableByNPCTypeID: + { + CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer; + NPC* n = entity_list.GetNPCByNPCTypeID(CZM->npctype_id); + if (n != 0) { + n->SetEntityVariable(CZM->id, CZM->m_var); + } + break; + } + case ServerOP_CZSignalNPC: + { + CZNPCSignal_Struct* CZCN = (CZNPCSignal_Struct*)pack->pBuffer; + NPC* n = entity_list.GetNPCByNPCTypeID(CZCN->npctype_id); + if (n != 0) { + n->SignalNPC(CZCN->data); + } + break; + } case ServerOP_CZSignalClient: { CZClientSignal_Struct* CZCS = (CZClientSignal_Struct*) pack->pBuffer; @@ -1856,6 +1895,7 @@ bool WorldServer::SendChannelMessage(Client* from, const char* to, uint8 chan_nu scm->chan_num = chan_num; scm->guilddbid = guilddbid; scm->language = language; + scm->queued = 0; strcpy(scm->message, buffer); pack->Deflate(); @@ -2180,3 +2220,19 @@ void WorldServer::HandleLFPMatches(ServerPacket *pack) { safe_delete(outapp); } } + +void WorldServer::RequestTellQueue(const char *who) +{ + if (!who) + return; + + ServerPacket* pack = new ServerPacket(ServerOP_RequestTellQueue, sizeof(ServerRequestTellQueue_Struct)); + ServerRequestTellQueue_Struct* rtq = (ServerRequestTellQueue_Struct*) pack->pBuffer; + + strn0cpy(rtq->name, who, sizeof(rtq->name)); + + SendPacket(pack); + safe_delete(pack); + return; +} + diff --git a/zone/worldserver.h b/zone/worldserver.h index 3c2e03fa6..6feead496 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -57,6 +57,8 @@ public: void HandleLFGMatches(ServerPacket *pack); void HandleLFPMatches(ServerPacket *pack); + void RequestTellQueue(const char *who); + private: virtual void OnConnected(); diff --git a/zone/zone.cpp b/zone/zone.cpp index 2f1052f73..0207b28d4 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -77,8 +77,6 @@ extern bool staticzone; Zone* zone = 0; volatile bool ZoneLoaded = false; extern QuestParserCollection* parse; -extern DBAsyncFinishedQueue MTdbafq; -extern DBAsync *dbasync; bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { const char* zonename = database.GetZoneName(iZoneID); @@ -105,7 +103,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { } zone->zonemap = Map::LoadMapFile(zone->map_name); zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name); - zone->pathing = PathManager::LoadPathFile(zone->map_name); + zone->pathing = PathManager::LoadPathFile(zone->map_name); char tmp[10]; if (database.GetVariable("loglevel",tmp, 9)) { @@ -309,41 +307,40 @@ bool Zone::LoadGroundSpawns() { return(true); } -int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold){ +int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold) { int freeslot = 0; std::list merlist = merchanttable[merchantid]; std::list::const_iterator itr; uint32 i = 1; for (itr = merlist.begin(); itr != merlist.end(); ++itr) { MerchantList ml = *itr; - if(ml.item == item) + if (ml.item == item) return 0; // Account for merchant lists with gaps in them. - if(ml.slot >= i) + if (ml.slot >= i) i = ml.slot + 1; - } std::list tmp_merlist = tmpmerchanttable[npcid]; std::list::const_iterator tmp_itr; bool update_charges = false; TempMerchantList ml; - while(freeslot == 0 && !update_charges){ + while (freeslot == 0 && !update_charges) { freeslot = i; for (tmp_itr = tmp_merlist.begin(); tmp_itr != tmp_merlist.end(); ++tmp_itr) { ml = *tmp_itr; - if(ml.item == item){ + if (ml.item == item) { update_charges = true; freeslot = 0; break; } - if((ml.slot == i) || (ml.origslot==i)) { - freeslot=0; + if ((ml.slot == i) || (ml.origslot == i)) { + freeslot = 0; } } i++; } - if(update_charges){ + if (update_charges) { tmp_merlist.clear(); std::list oldtmp_merlist = tmpmerchanttable[npcid]; for (tmp_itr = oldtmp_merlist.begin(); tmp_itr != oldtmp_merlist.end(); ++tmp_itr) { @@ -351,27 +348,27 @@ int Zone::SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charg if(ml2.item != item) tmp_merlist.push_back(ml2); } - if(sold) + if (sold) ml.charges = ml.charges + charges; else ml.charges = charges; - if(!ml.origslot) + if (!ml.origslot) ml.origslot = ml.slot; - if(charges>0){ + if (charges > 0) { database.SaveMerchantTemp(npcid, ml.origslot, item, ml.charges); tmp_merlist.push_back(ml); } - else{ - database.DeleteMerchantTemp(npcid,ml.origslot); + else { + database.DeleteMerchantTemp(npcid, ml.origslot); } tmpmerchanttable[npcid] = tmp_merlist; - if(sold) + if (sold) return ml.slot; } - if(freeslot){ - if(charges<0) //sanity check only, shouldnt happen + if (freeslot) { + if (charges < 0) //sanity check only, shouldnt happen charges = 0x7FFF; database.SaveMerchantTemp(npcid, freeslot, item, charges); tmp_merlist = tmpmerchanttable[npcid]; @@ -393,48 +390,42 @@ uint32 Zone::GetTempMerchantQuantity(uint32 NPCID, uint32 Slot) { std::list::const_iterator Iterator; for (Iterator = TmpMerchantList.begin(); Iterator != TmpMerchantList.end(); ++Iterator) - if((*Iterator).slot == Slot) + if ((*Iterator).slot == Slot) return (*Iterator).charges; return 0; } -void Zone::LoadTempMerchantData(){ +void Zone::LoadTempMerchantData() { LogFile->write(EQEMuLog::Status, "Loading Temporary Merchant Lists..."); - - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Zone; - workpt.w2_3() = 0; - workpt.b1() = DBA_b1_Zone_MerchantListsTemp; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read); - dbaw->AddQuery(1, &query, MakeAnyLenString(&query, - "select ml.npcid,ml.slot,ml.itemid,ml.charges " - "from " - " merchantlist_temp ml, " - " spawnentry se, " - " spawn2 s2 " - "where " - " ml.npcid=se.npcid " - " and se.spawngroupid=s2.spawngroupid " - " and s2.zone='%s' and s2.version=%u", GetShortName(), GetInstanceVersion())); - if (!(pQueuedMerchantsWorkID = dbasync->AddWork(&dbaw))) { - safe_delete(dbaw); - LogFile->write(EQEMuLog::Error, "dbasync->AddWork() failed adding merchant list query"); + std::string query = StringFormat( + "SELECT " + "ml.npcid, " + "ml.slot, " + "ml.charges, " + "ml.itemid " + "FROM " + "merchantlist_temp ml, " + "spawnentry se, " + "spawn2 s2 " + "WHERE " + "ml.npcid = se.npcid " + "AND se.spawngroupid = s2.spawngroupid " + "AND s2.zone = '%s' AND s2.version = %i " + "ORDER BY ml.slot ", GetShortName(), GetInstanceVersion()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTempMerchantData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); return; } -} - -void Zone::LoadTempMerchantData_result(MYSQL_RES* result) { - MYSQL_ROW row; - std::map >::iterator cur; + std::map >::iterator cur; uint32 npcid = 0; - while((row = mysql_fetch_row(result))) { + for (auto row = results.begin(); row != results.end(); ++row) { TempMerchantList ml; ml.npcid = atoul(row[0]); - if(npcid != ml.npcid){ + if (npcid != ml.npcid){ cur = tmpmerchanttable.find(ml.npcid); - if(cur == tmpmerchanttable.end()) { + if (cur == tmpmerchanttable.end()) { std::list empty; tmpmerchanttable[ml.npcid] = empty; cur = tmpmerchanttable.find(ml.npcid); @@ -442,19 +433,19 @@ void Zone::LoadTempMerchantData_result(MYSQL_RES* result) { npcid = ml.npcid; } ml.slot = atoul(row[1]); - ml.item = atoul(row[2]); - ml.charges = atoul(row[3]); + ml.charges = atoul(row[2]); + ml.item = atoul(row[3]); ml.origslot = ml.slot; cur->second.push_back(ml); } + pQueuedMerchantsWorkID = 0; } -//there should prolly be a temp counterpart of this... -void Zone::LoadNewMerchantData(uint32 merchantid){ +void Zone::LoadNewMerchantData(uint32 merchantid) { std::list merlist; std::string query = StringFormat("SELECT item, slot, faction_required, level_required, alt_currency_cost, " - "classes_required FROM merchantlist WHERE merchantid=%d", merchantid); + "classes_required FROM merchantlist WHERE merchantid=%d ORDER BY slot", merchantid); auto results = database.QueryDatabase(query); if (!results.Success()) { LogFile->write(EQEMuLog::Error, "Error in LoadNewMerchantData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); @@ -476,16 +467,39 @@ void Zone::LoadNewMerchantData(uint32 merchantid){ merchanttable[merchantid] = merlist; } -void Zone::LoadMerchantData_result(MYSQL_RES* result) { - MYSQL_ROW row; - std::map >::iterator cur; +void Zone::GetMerchantDataForZoneLoad() { + LogFile->write(EQEMuLog::Status, "Loading Merchant Lists..."); + std::string query = StringFormat( + "SELECT " + "ml.merchantid, " + "ml.slot, " + "ml.item, " + "ml.faction_required, " + "ml.level_required, " + "ml.alt_currency_cost, " + "ml.classes_required, " + "ml.probability " + "FROM " + "merchantlist AS ml, " + "npc_types AS nt, " + "spawnentry AS se, " + "spawn2 AS s2 " + "WHERE nt.merchant_id = ml.merchantid AND nt.id = se.npcid " + "AND se.spawngroupid = s2.spawngroupid AND s2.zone = '%s' AND s2.version = %i " + "ORDER BY ml.slot ", GetShortName(), GetInstanceVersion()); + auto results = database.QueryDatabase(query); + std::map >::iterator cur; uint32 npcid = 0; - while((row = mysql_fetch_row(result))) { + if (results.RowCount() == 0) { + LogFile->write(EQEMuLog::Error, "Error in loading Merchant Data for zone"); + return; + } + for (auto row = results.begin(); row != results.end(); ++row) { MerchantList ml; ml.id = atoul(row[0]); - if(npcid != ml.id){ + if (npcid != ml.id) { cur = merchanttable.find(ml.id); - if(cur == merchanttable.end()) { + if (cur == merchanttable.end()) { std::list empty; merchanttable[ml.id] = empty; cur = merchanttable.find(ml.id); @@ -495,15 +509,15 @@ void Zone::LoadMerchantData_result(MYSQL_RES* result) { std::list::iterator iter = cur->second.begin(); bool found = false; - while(iter != cur->second.end()) { - if((*iter).item == ml.id) { + while (iter != cur->second.end()) { + if ((*iter).item == ml.id) { found = true; break; } ++iter; } - if(found) { + if (found) { continue; } @@ -516,28 +530,7 @@ void Zone::LoadMerchantData_result(MYSQL_RES* result) { ml.probability = atoul(row[7]); cur->second.push_back(ml); } -} -void Zone::GetMerchantDataForZoneLoad(){ - LogFile->write(EQEMuLog::Status, "Loading Merchant Lists..."); - char* query = 0; - uint32_breakdown workpt; - workpt.b4() = DBA_b4_Zone; - workpt.w2_3() = 0; - workpt.b1() = DBA_b1_Zone_MerchantLists; - DBAsyncWork* dbaw = new DBAsyncWork(&database, &MTdbafq, workpt, DBAsync::Read); - dbaw->AddQuery(1, &query, MakeAnyLenString(&query, - "select ml.merchantid,ml.slot,ml.item,ml.faction_required,ml.level_required,ml.alt_currency_cost,ml.classes_required,ml.probability " - "from merchantlist ml, npc_types nt, spawnentry se, spawn2 s2 " - "where nt.merchant_id=ml.merchantid and nt.id=se.npcid " - "and se.spawngroupid=s2.spawngroupid and s2.zone='%s' and s2.version=%u " - //"group by ml.merchantid,slot order by merchantid,slot asc" //this made the query use a temp table/filesort (very slow)... so we handle unsorted data on our end. - , GetShortName(), GetInstanceVersion())); - if (!(pQueuedMerchantsWorkID = dbasync->AddWork(&dbaw))) { - safe_delete(dbaw); - LogFile->write(EQEMuLog::Error,"dbasync->AddWork() failed adding merchant list query"); - return; - } } void Zone::LoadMercTemplates(){ @@ -665,61 +658,6 @@ void Zone::LoadMercSpells(){ } -void Zone::DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw) { -// LogFile->write(EQEMuLog::Debug, "Zone work complete..."); - switch (workpt_b1) { - case DBA_b1_Zone_MerchantLists: { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result = 0; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if(dbaq == nullptr) { - LogFile->write(EQEMuLog::Error, "nullptr answer provided for async merchant list load."); - break; - } - if(!dbaq->GetAnswer(errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unable to get results for merchant lists"); - break; - } - if(dbaq->QPT() != 1) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Invalid query part for merchant lists"); - break; - } - - LoadMerchantData_result(result); - - pQueuedMerchantsWorkID = 0; - break; - } - case DBA_b1_Zone_MerchantListsTemp: { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result = 0; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if(dbaq == nullptr) { - LogFile->write(EQEMuLog::Error, "nullptr answer provided for async temp merchant list load."); - break; - } - if(!dbaq->GetAnswer(errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unable to get results for temp merchant lists"); - break; - } - if(dbaq->QPT() != 1) { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Invalid query part for temp merchant lists"); - break; - } - - LoadTempMerchantData_result(result); - - pQueuedMerchantsWorkID = 0; - break; - } - - default: { - LogFile->write(EQEMuLog::Error, "Zone::DBAWComplete(): Unknown workpt_b1"); - break; - } - } -} - bool Zone::IsLoaded() { return ZoneLoaded; } @@ -772,7 +710,6 @@ void Zone::Shutdown(bool quite) zone->ResetAuth(); safe_delete(zone); - dbasync->CommitWrites(); entity_list.ClearAreas(); parse->ReloadQuests(true); UpdateWindowTitle(); @@ -911,8 +848,6 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) } Zone::~Zone() { - if(pQueuedMerchantsWorkID != 0) - dbasync->CancelWork(pQueuedMerchantsWorkID); spawn2_list.Clear(); safe_delete(zonemap); safe_delete(watermap); diff --git a/zone/zone.h b/zone/zone.h index 77132124e..9ad692fa0 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -166,7 +166,6 @@ public: void SetStaticZone(bool sz) { staticzone = sz; } inline bool IsStaticZone() { return staticzone; } inline void GotCurTime(bool time) { gottime = time; } - void DBAWComplete(uint8 workpt_b1, DBAsyncWork* dbaw); void SpawnConditionChanged(const SpawnCondition &c, int16 old_value); @@ -174,8 +173,6 @@ public: void LoadNewMerchantData(uint32 merchantid); void LoadTempMerchantData(); uint32 GetTempMerchantQuantity(uint32 NPCID, uint32 Slot); - void LoadTempMerchantData_result(MYSQL_RES* result); - void LoadMerchantData_result(MYSQL_RES* result); int SaveTempItem(uint32 merchantid, uint32 npcid, uint32 item, int32 charges, bool sold=false); void LoadMercTemplates(); void LoadMercSpells(); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 4369d00cf..e83edf2b9 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -5,6 +5,7 @@ #include "../common/extprofile.h" #include "../common/guilds.h" #include "../common/rulesys.h" +#include "../common/rdtsc.h" #include "zone.h" #include "client.h" #include "merc.h" @@ -13,6 +14,7 @@ #include #include #include +#include extern Zone* zone; @@ -347,41 +349,6 @@ void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ } - -bool ZoneDatabase::GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin, char* account_name, uint32* lsaccountid, uint8* gmspeed, bool* revoked,bool* gmhideme, uint32* account_creation) { - MYSQL_ROW row; - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (admin) - *admin = atoi(row[0]); - if (account_name) - strcpy(account_name, row[1]); - if (lsaccountid) { - - if (row[2]) - *lsaccountid = atoi(row[2]); - else - *lsaccountid = 0; - - - } - if (gmspeed) - *gmspeed = atoi(row[3]); - if (revoked) - *revoked = atoi(row[4]); - if(gmhideme) - *gmhideme = atoi(row[5]); - if(account_creation) - *account_creation = atoul(row[6]); - - return true; - } - else { - return false; - } -} - - bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) { std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id); @@ -599,8 +566,7 @@ TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id) return loadti; } -ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { - +ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { 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); @@ -657,8 +623,7 @@ void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNum } -void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { - +void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", CharID, SerialNumber, Charges); std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i", @@ -752,8 +717,7 @@ void ZoneDatabase::DeleteBuyLines(uint32 CharID) { } -void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) { - +void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) { std::string query = StringFormat("REPLACE INTO buyer VALUES(%i, %i, %i, \"%s\", %i, %i)", CharID, BuySlot, ItemID, ItemName, Quantity, Price); auto results = QueryDatabase(query); @@ -762,8 +726,7 @@ void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, cons } -void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { - +void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i AND buyslot = %i", CharID, BuySlot); auto results = QueryDatabase(query); if (!results.Success()) @@ -771,8 +734,7 @@ void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { } -void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { - +void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { if(Quantity <= 0) { RemoveBuyLine(CharID, BuySlot); return; @@ -787,106 +749,967 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) #define StructDist(in, f1, f2) (uint32(&in->f2)-uint32(&in->f1)) -// Process results of GetCharacterInfoForLogin() -// Query this processes: SELECT id,profile,zonename,x,y,z,guild,guildrank,extprofile,class,level FROM character_ WHERE id=%i -bool ZoneDatabase::GetCharacterInfoForLogin_result(MYSQL_RES* result, - uint32* character_id, char* current_zone, PlayerProfile_Struct* pp, Inventory* inv, - ExtendedProfile_Struct *ext, uint32* pplen, uint32* guilddbid, uint8* guildrank, - uint8 *class_, uint8 *level, bool *LFP, bool *LFG, uint8 *NumXTargets, uint8* firstlogon) { +bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ + std::string query = StringFormat( + "SELECT " + "`name`, " + "last_name, " + "gender, " + "race, " + "class, " + "`level`, " + "deity, " + "birthday, " + "last_login, " + "time_played, " + "pvp_status, " + "level2, " + "anon, " + "gm, " + "intoxication, " + "hair_color, " + "beard_color, " + "eye_color_1, " + "eye_color_2, " + "hair_style, " + "beard, " + "ability_time_seconds, " + "ability_number, " + "ability_time_minutes, " + "ability_time_hours, " + "title, " + "suffix, " + "exp, " + "points, " + "mana, " + "cur_hp, " + "str, " + "sta, " + "cha, " + "dex, " + "`int`, " + "agi, " + "wis, " + "face, " + "y, " + "x, " + "z, " + "heading, " + "pvp2, " + "pvp_type, " + "autosplit_enabled, " + "zone_change_count, " + "drakkin_heritage, " + "drakkin_tattoo, " + "drakkin_details, " + "toxicity, " + "hunger_level, " + "thirst_level, " + "ability_up, " + "zone_id, " + "zone_instance, " + "leadership_exp_on, " + "ldon_points_guk, " + "ldon_points_mir, " + "ldon_points_mmc, " + "ldon_points_ruj, " + "ldon_points_tak, " + "ldon_points_available, " + "tribute_time_remaining, " + "show_helm, " + "career_tribute_points, " + "tribute_points, " + "tribute_active, " + "endurance, " + "group_leadership_exp, " + "raid_leadership_exp, " + "group_leadership_points, " + "raid_leadership_points, " + "air_remaining, " + "pvp_kills, " + "pvp_deaths, " + "pvp_current_points, " + "pvp_career_points, " + "pvp_best_kill_streak, " + "pvp_worst_death_streak, " + "pvp_current_kill_streak, " + "aa_points_spent, " + "aa_exp, " + "aa_points, " + "group_auto_consent, " + "raid_auto_consent, " + "guild_auto_consent, " + "RestTimer, " + "`e_aa_effects`, " + "`e_percent_to_aa`, " + "`e_expended_aa_spent` " + "FROM " + "character_data " + "WHERE `id` = %i ", character_id); + auto results = database.QueryDatabase(query); int r = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + strcpy(pp->name, row[r]); r++; // "`name`, " + strcpy(pp->last_name, row[r]); r++; // "last_name, " + pp->gender = atoi(row[r]); r++; // "gender, " + pp->race = atoi(row[r]); r++; // "race, " + pp->class_ = atoi(row[r]); r++; // "class, " + pp->level = atoi(row[r]); r++; // "`level`, " + pp->deity = atoi(row[r]); r++; // "deity, " + pp->birthday = atoi(row[r]); r++; // "birthday, " + pp->lastlogin = atoi(row[r]); r++; // "last_login, " + pp->timePlayedMin = atoi(row[r]); r++; // "time_played, " + pp->pvp = atoi(row[r]); r++; // "pvp_status, " + pp->level2 = atoi(row[r]); r++; // "level2, " + pp->anon = atoi(row[r]); r++; // "anon, " + pp->gm = atoi(row[r]); r++; // "gm, " + pp->intoxication = atoi(row[r]); r++; // "intoxication, " + pp->haircolor = atoi(row[r]); r++; // "hair_color, " + pp->beardcolor = atoi(row[r]); r++; // "beard_color, " + pp->eyecolor1 = atoi(row[r]); r++; // "eye_color_1, " + pp->eyecolor2 = atoi(row[r]); r++; // "eye_color_2, " + pp->hairstyle = atoi(row[r]); r++; // "hair_style, " + pp->beard = atoi(row[r]); r++; // "beard, " + pp->ability_time_seconds = atoi(row[r]); r++; // "ability_time_seconds, " + pp->ability_number = atoi(row[r]); r++; // "ability_number, " + pp->ability_time_minutes = atoi(row[r]); r++; // "ability_time_minutes, " + pp->ability_time_hours = atoi(row[r]); r++; // "ability_time_hours, " + strcpy(pp->title, row[r]); r++; // "title, " + strcpy(pp->suffix, row[r]); r++; // "suffix, " + pp->exp = atoi(row[r]); r++; // "exp, " + pp->points = atoi(row[r]); r++; // "points, " + pp->mana = atoi(row[r]); r++; // "mana, " + pp->cur_hp = atoi(row[r]); r++; // "cur_hp, " + pp->STR = atoi(row[r]); r++; // "str, " + pp->STA = atoi(row[r]); r++; // "sta, " + pp->CHA = atoi(row[r]); r++; // "cha, " + pp->DEX = atoi(row[r]); r++; // "dex, " + pp->INT = atoi(row[r]); r++; // "`int`, " + pp->AGI = atoi(row[r]); r++; // "agi, " + pp->WIS = atoi(row[r]); r++; // "wis, " + pp->face = atoi(row[r]); r++; // "face, " + pp->y = atof(row[r]); r++; // "y, " + pp->x = atof(row[r]); r++; // "x, " + pp->z = atof(row[r]); r++; // "z, " + pp->heading = atof(row[r]); r++; // "heading, " + pp->pvp2 = atoi(row[r]); r++; // "pvp2, " + pp->pvptype = atoi(row[r]); r++; // "pvp_type, " + pp->autosplit = atoi(row[r]); r++; // "autosplit_enabled, " + pp->zone_change_count = atoi(row[r]); r++; // "zone_change_count, " + pp->drakkin_heritage = atoi(row[r]); r++; // "drakkin_heritage, " + pp->drakkin_tattoo = atoi(row[r]); r++; // "drakkin_tattoo, " + pp->drakkin_details = atoi(row[r]); r++; // "drakkin_details, " + pp->toxicity = atoi(row[r]); r++; // "toxicity, " + pp->hunger_level = atoi(row[r]); r++; // "hunger_level, " + pp->thirst_level = atoi(row[r]); r++; // "thirst_level, " + pp->ability_up = atoi(row[r]); r++; // "ability_up, " + pp->zone_id = atoi(row[r]); r++; // "zone_id, " + pp->zoneInstance = atoi(row[r]); r++; // "zone_instance, " + pp->leadAAActive = atoi(row[r]); r++; // "leadership_exp_on, " + pp->ldon_points_guk = atoi(row[r]); r++; // "ldon_points_guk, " + pp->ldon_points_mir = atoi(row[r]); r++; // "ldon_points_mir, " + pp->ldon_points_mmc = atoi(row[r]); r++; // "ldon_points_mmc, " + pp->ldon_points_ruj = atoi(row[r]); r++; // "ldon_points_ruj, " + pp->ldon_points_tak = atoi(row[r]); r++; // "ldon_points_tak, " + pp->ldon_points_available = atoi(row[r]); r++; // "ldon_points_available, " + pp->tribute_time_remaining = atoi(row[r]); r++; // "tribute_time_remaining, " + pp->showhelm = atoi(row[r]); r++; // "show_helm, " + pp->career_tribute_points = atoi(row[r]); r++; // "career_tribute_points, " + pp->tribute_points = atoi(row[r]); r++; // "tribute_points, " + pp->tribute_active = atoi(row[r]); r++; // "tribute_active, " + pp->endurance = atoi(row[r]); r++; // "endurance, " + pp->group_leadership_exp = atoi(row[r]); r++; // "group_leadership_exp, " + pp->raid_leadership_exp = atoi(row[r]); r++; // "raid_leadership_exp, " + pp->group_leadership_points = atoi(row[r]); r++; // "group_leadership_points, " + pp->raid_leadership_points = atoi(row[r]); r++; // "raid_leadership_points, " + pp->air_remaining = atoi(row[r]); r++; // "air_remaining, " + pp->PVPKills = atoi(row[r]); r++; // "pvp_kills, " + pp->PVPDeaths = atoi(row[r]); r++; // "pvp_deaths, " + pp->PVPCurrentPoints = atoi(row[r]); r++; // "pvp_current_points, " + pp->PVPCareerPoints = atoi(row[r]); r++; // "pvp_career_points, " + pp->PVPBestKillStreak = atoi(row[r]); r++; // "pvp_best_kill_streak, " + pp->PVPWorstDeathStreak = atoi(row[r]); r++; // "pvp_worst_death_streak, " + pp->PVPCurrentKillStreak = atoi(row[r]); r++; // "pvp_current_kill_streak, " + pp->aapoints_spent = atoi(row[r]); r++; // "aa_points_spent, " + pp->expAA = atoi(row[r]); r++; // "aa_exp, " + pp->aapoints = atoi(row[r]); r++; // "aa_points, " + pp->groupAutoconsent = atoi(row[r]); r++; // "group_auto_consent, " + pp->raidAutoconsent = atoi(row[r]); r++; // "raid_auto_consent, " + pp->guildAutoconsent = atoi(row[r]); r++; // "guild_auto_consent, " + pp->RestTimer = atoi(row[r]); r++; // "RestTimer, " + m_epp->aa_effects = atoi(row[r]); r++; // "`e_aa_effects`, " + m_epp->perAA = atoi(row[r]); r++; // "`e_percent_to_aa`, " + m_epp->expended_aa = atoi(row[r]); r++; // "`e_expended_aa_spent` " + } + return true; +} - MYSQL_ROW row; - unsigned long* lengths; +bool ZoneDatabase::LoadCharacterFactionValues(uint32 character_id, faction_map & val_list) { + std::string query = StringFormat("SELECT `faction_id`, `current_value` FROM `faction_values` WHERE `char_id` = %i", character_id); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { val_list[atoi(row[0])] = atoi(row[1]); } + return true; +} - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if (pp && pplen) { - if (lengths[1] == sizeof(PlayerProfile_Struct)) { - memcpy(pp, row[1], sizeof(PlayerProfile_Struct)); - } else { - LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetCharacterInfo Expected: %i, Got: %i", - sizeof(PlayerProfile_Struct), lengths[1]); - return false; - } - - *pplen = lengths[1]; - pp->zone_id = GetZoneID(row[2]); - pp->zoneInstance = atoi(row[13]); - - pp->x = atof(row[3]); - pp->y = atof(row[4]); - pp->z = atof(row[5]); - - pp->lastlogin = time(nullptr); - - if (pp->x == -1 && pp->y == -1 && pp->z == -1) - GetSafePoints(pp->zone_id, database.GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z); +bool ZoneDatabase::LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "slot_id, " + "`spell_id` " + "FROM " + "`character_memmed_spells` " + "WHERE `id` = %u ORDER BY `slot_id`", character_id); + auto results = database.QueryDatabase(query); + int i = 0; + /* Initialize Spells */ + for (i = 0; i < MAX_PP_MEMSPELL; i++){ + pp->mem_spells[i] = 0xFFFFFFFF; + } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_MEMSPELL){ + pp->mem_spells[i] = atoi(row[1]); } + } + return true; +} - uint32 char_id = atoi(row[0]); - if (RuleB(Character, SharedBankPlat)) - pp->platinum_shared = database.GetSharedPlatinum(GetAccountIDByChar(char_id)); - if (character_id) - *character_id = char_id; - if (current_zone) - strcpy(current_zone, row[2]); +bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "slot_id, " + "`spell_id` " + "FROM " + "`character_spells` " + "WHERE `id` = %u ORDER BY `slot_id`", character_id); + auto results = database.QueryDatabase(query); + int i = 0; + /* Initialize Spells */ + for (i = 0; i < MAX_PP_SPELLBOOK; i++){ + pp->spell_book[i] = 0xFFFFFFFF; + } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_SPELLBOOK){ + pp->spell_book[i] = atoi(row[1]); + } + } + return true; +} - if (guilddbid) { - if(row[6] != nullptr) - *guilddbid = atoi(row[6]); - else - *guilddbid = GUILD_NONE; +bool ZoneDatabase::LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "lang_id, " + "`value` " + "FROM " + "`character_languages` " + "WHERE `id` = %u ORDER BY `lang_id`", character_id); + auto results = database.QueryDatabase(query); int i = 0; + /* Initialize Languages */ + for (i = 0; i < MAX_PP_LANGUAGE; i++){ + pp->languages[i] = 0; + } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_LANGUAGE){ + pp->languages[i] = atoi(row[1]); } - if (guildrank) { - if(row[7] != nullptr) - *guildrank = atoi(row[7]); - else - *guildrank = GUILD_RANK_NONE; + } + return true; +} + +bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT slot, rank FROM character_leadership_abilities WHERE `id` = %u", character_id); + auto results = database.QueryDatabase(query); uint32 slot = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + slot = atoi(row[0]); + pp->leader_abilities.ranks[slot] = atoi(row[1]); + } + return true; +} + +bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "disc_id " + "FROM " + "`character_disciplines`" + "WHERE `id` = %u ORDER BY `slot_id`", character_id); + auto results = database.QueryDatabase(query); + int i = 0; + /* Initialize Disciplines */ + memset(pp->disciplines.values, 0, (sizeof(pp->disciplines.values[0]) * MAX_PP_DISCIPLINES)); + for (auto row = results.begin(); row != results.end(); ++row) { + if (i < MAX_PP_DISCIPLINES){ + pp->disciplines.values[i] = atoi(row[0]); } + i++; + } + return true; +} - if(ext) { - //SetExtendedProfile handles any conversion - SetExtendedProfile(ext, row[8], lengths[8]); +bool ZoneDatabase::LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "skill_id, " + "`value` " + "FROM " + "`character_skills` " + "WHERE `id` = %u ORDER BY `skill_id`", character_id); + auto results = database.QueryDatabase(query); int i = 0; + /* Initialize Skill */ + for (i = 0; i < MAX_PP_SKILL; i++){ + pp->skills[i] = 0; + } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); + if (i < MAX_PP_SKILL){ + pp->skills[i] = atoi(row[1]); } + } + return true; +} - if(class_) - *class_ = atoi(row[9]); +bool ZoneDatabase::LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat( + "SELECT " + "platinum, " + "gold, " + "silver, " + "copper, " + "platinum_bank, " + "gold_bank, " + "silver_bank, " + "copper_bank, " + "platinum_cursor, " + "gold_cursor, " + "silver_cursor, " + "copper_cursor, " + "radiant_crystals, " + "career_radiant_crystals," + "ebon_crystals, " + "career_ebon_crystals " + "FROM " + "character_currency " + "WHERE `id` = %i ", character_id); + auto results = database.QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + pp->platinum = atoi(row[0]); + pp->gold = atoi(row[1]); + pp->silver = atoi(row[2]); + pp->copper = atoi(row[3]); + pp->platinum_bank = atoi(row[4]); + pp->gold_bank = atoi(row[5]); + pp->silver_bank = atoi(row[6]); + pp->copper_bank = atoi(row[7]); + pp->platinum_cursor = atoi(row[8]); + pp->gold_cursor = atoi(row[9]); + pp->silver_cursor = atoi(row[10]); + pp->copper_cursor = atoi(row[11]); + pp->currentRadCrystals = atoi(row[12]); + pp->careerRadCrystals = atoi(row[13]); + pp->currentEbonCrystals = atoi(row[14]); + pp->careerEbonCrystals = atoi(row[15]); + } + return true; +} - if(level) - *level = atoi(row[10]); +bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT slot, blue, green, red, use_tint, color FROM `character_material` WHERE `id` = %u LIMIT 9", character_id); + auto results = database.QueryDatabase(query); int i = 0; int r = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + r = 0; + i = atoi(row[r]); /* Slot */ r++; + pp->item_tint[i].rgb.blue = atoi(row[r]); r++; + pp->item_tint[i].rgb.green = atoi(row[r]); r++; + pp->item_tint[i].rgb.red = atoi(row[r]); r++; + pp->item_tint[i].rgb.use_tint = atoi(row[r]); + } + return true; +} - if(LFP) - *LFP = atoi(row[11]); - - if(LFG) - *LFG = atoi(row[12]); - - if(NumXTargets) - { - *NumXTargets = atoi(row[14]); +bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT 16", character_id); + auto results = database.QueryDatabase(query); int i = 0; int r = 0; int si = 0; + for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ + for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + pp->bandoliers[i].items[si].icon = 0; } - - - if(firstlogon) - { - *firstlogon = atoi(row[15]); - } - - // Fix use_tint, previously it was set to 1 for a dyed slot, client wants it set to 0xFF - for(int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - if(pp->item_tint[i].rgb.use_tint == 1) - pp->item_tint[i].rgb.use_tint = 0xFF; - - // Retrieve character inventory - return GetInventory(char_id, inv); } - return false; + for (auto row = results.begin(); row != results.end(); ++row) { + r = 0; + i = atoi(row[r]); /* Bandolier ID */ r++; + si = atoi(row[r]); /* Bandolier Slot */ r++; + pp->bandoliers[i].items[si].item_id = atoi(row[r]); r++; + pp->bandoliers[i].items[si].icon = atoi(row[r]); r++; + strcpy(pp->bandoliers[i].name, row[r]); r++; + si++; + } + return true; +} + +bool ZoneDatabase::LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT `tier`, `tribute` FROM `character_tribute` WHERE `id` = %u", character_id); + auto results = database.QueryDatabase(query); + int i = 0; + for (i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + pp->tributes[i].tribute = 0xFFFFFFFF; + pp->tributes[i].tier = 0; + } + i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + if(atoi(row[1]) != TRIBUTE_NONE){ + pp->tributes[i].tier = atoi(row[0]); + pp->tributes[i].tribute = atoi(row[1]); + i++; + } + } + return true; +} + +bool ZoneDatabase::LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT `potion_id`, `item_id`, `icon` FROM `character_potionbelt` WHERE `id` = %u LIMIT 4", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (i = 0; i < EmuConstants::POTION_BELT_SIZE; i++){ + pp->potionbelt.items[i].icon = 0; + pp->potionbelt.items[i].item_id = 0; + strncpy(pp->potionbelt.items[i].item_name, "\0", 1); + } + for (auto row = results.begin(); row != results.end(); ++row) { + i = atoi(row[0]); /* Potion belt slot number */ + uint32 item_id = atoi(row[1]); + const Item_Struct *item = database.GetItem(item_id); + + if(item) { + pp->potionbelt.items[i].item_id = item_id; + pp->potionbelt.items[i].icon = atoi(row[2]); + strncpy(pp->potionbelt.items[i].item_name, item->Name, 64); + } + } + return true; +} + +bool ZoneDatabase::LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("SELECT `zone_id`, `instance_id`, `x`, `y`, `z`, `heading`, `is_home` FROM `character_bind` WHERE `id` = %u LIMIT 2", character_id); + auto results = database.QueryDatabase(query); int i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + i = 0; + /* Is home bind */ + if (atoi(row[6]) == 1){ + pp->binds[4].zoneId = atoi(row[i]); i++; + i++; /* Instance ID can go here eventually */ + pp->binds[4].x = atoi(row[i]); i++; + pp->binds[4].y = atoi(row[i]); i++; + pp->binds[4].z = atoi(row[i]); i++; + pp->binds[4].heading = atoi(row[i]); i++; + } + /* Is regular bind point */ + else{ + pp->binds[0].zoneId = atoi(row[i]); i++; + i++; /* Instance ID can go here eventually */ + pp->binds[0].x = atoi(row[i]); i++; + pp->binds[0].y = atoi(row[i]); i++; + pp->binds[0].z = atoi(row[i]); i++; + pp->binds[0].heading = atoi(row[i]); i++; + } + } + return true; +} + +bool ZoneDatabase::SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value){ + std::string query = StringFormat("REPLACE INTO `character_languages` (id, lang_id, value) VALUES (%u, %u, %u)", character_id, lang_id, value); QueryDatabase(query); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterLanguage for character ID: %i, lang_id:%u value:%u done", character_id, lang_id, value); + return true; +} + +bool ZoneDatabase::SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home){ + if (zone_id <= 0){ return false; } + /* Save Home Bind Point */ + std::string query = StringFormat("REPLACE INTO `character_bind` (id, zone_id, instance_id, x, y, z, heading, is_home)" + " VALUES (%u, %u, %u, %f, %f, %f, %f, %i)", character_id, zone_id, instance_id, x, y, z, heading, is_home); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterBindPoint for character ID: %i zone_id: %u instance_id: %u x: %f y: %f z: %f heading: %f ishome: %u", character_id, zone_id, instance_id, x, y, z, heading, is_home); + auto results = QueryDatabase(query); if (!results.RowsAffected()){ std::cout << "ERROR Bind Home Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterBindPoint", query); + return true; +} + +bool ZoneDatabase::SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color){ + uint8 red = (color & 0x00FF0000) >> 16; + uint8 green = (color & 0x0000FF00) >> 8; + uint8 blue = (color & 0x000000FF); + + std::string query = StringFormat("REPLACE INTO `character_material` (id, slot, red, green, blue, color, use_tint) VALUES (%u, %u, %u, %u, %u, %u, 255)", character_id, slot_id, red, green, blue, color); auto results = QueryDatabase(query); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterMaterialColor for character ID: %i, slot_id: %u color: %u done", character_id, slot_id, color); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterMaterialColor", query); + return true; +} + +bool ZoneDatabase::SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value){ + std::string query = StringFormat("REPLACE INTO `character_skills` (id, skill_id, value) VALUES (%u, %u, %u)", character_id, skill_id, value); auto results = QueryDatabase(query); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterSkill for character ID: %i, skill_id:%u value:%u done", character_id, skill_id, value); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterSkill", query); + return true; +} + +bool ZoneDatabase::SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id){ + std::string query = StringFormat("REPLACE INTO `character_disciplines` (id, slot_id, disc_id) VALUES (%u, %u, %u)", character_id, slot_id, disc_id); + auto results = QueryDatabase(query); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterDisc for character ID: %i, slot:%u disc_id:%u done", character_id, slot_id, disc_id); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterDisc", query); + return true; +} + +bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp){ + std::string query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", character_id); + QueryDatabase(query); + /* Save Tributes only if we have values... */ + for (int i = 0; i < EmuConstants::TRIBUTE_SIZE; i++){ + if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != TRIBUTE_NONE){ + std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + QueryDatabase(query); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute); + } + } + return true; +} + +bool ZoneDatabase::SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name){ + char bandolier_name_esc[64]; + DoEscapeString(bandolier_name_esc, bandolier_name, strlen(bandolier_name)); + std::string query = StringFormat("REPLACE INTO `character_bandolier` (id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name) VALUES (%u, %u, %u, %u, %u,'%s')", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name_esc); + auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterBandolier", query); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterBandolier for character ID: %i, bandolier_id: %u, bandolier_slot: %u item_id: %u, icon:%u band_name:%s done", character_id, bandolier_id, bandolier_slot, item_id, icon, bandolier_name); + if (!results.RowsAffected()){ std::cout << "ERROR Bandolier Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + return true; +} + +bool ZoneDatabase::SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon) { + std::string query = StringFormat("REPLACE INTO `character_potionbelt` (id, potion_id, item_id, icon) VALUES (%u, %u, %u, %u)", character_id, potion_id, item_id, icon); + auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterPotionBelt", query); + if (!results.RowsAffected()){ std::cout << "ERROR Potionbelt Save: " << results.ErrorMessage() << "\n\n" << query << "\n" << std::endl; } + return true; +} + +bool ZoneDatabase::SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp){ + uint8 first_entry = 0; std::string query = ""; + for (int i = 0; i < MAX_LEADERSHIP_AA_ARRAY; i++){ + if (pp->leader_abilities.ranks[i] > 0){ + if (first_entry != 1){ + query = StringFormat("REPLACE INTO `character_leadership_abilities` (id, slot, rank) VALUES (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + first_entry = 1; + } + query = query + StringFormat(", (%i, %u, %u)", character_id, i, pp->leader_abilities.ranks[i]); + } + } + auto results = QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterLeadershipAA", query); + return true; +} + +bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){ + clock_t t = std::clock(); /* Function timer start */ + std::string query = StringFormat( + "REPLACE INTO `character_data` (" + " id, " + " account_id, " + " `name`, " + " last_name, " + " gender, " + " race, " + " class, " + " `level`, " + " deity, " + " birthday, " + " last_login, " + " time_played, " + " pvp_status, " + " level2, " + " anon, " + " gm, " + " intoxication, " + " hair_color, " + " beard_color, " + " eye_color_1, " + " eye_color_2, " + " hair_style, " + " beard, " + " ability_time_seconds, " + " ability_number, " + " ability_time_minutes, " + " ability_time_hours, " + " title, " + " suffix, " + " exp, " + " points, " + " mana, " + " cur_hp, " + " str, " + " sta, " + " cha, " + " dex, " + " `int`, " + " agi, " + " wis, " + " face, " + " y, " + " x, " + " z, " + " heading, " + " pvp2, " + " pvp_type, " + " autosplit_enabled, " + " zone_change_count, " + " drakkin_heritage, " + " drakkin_tattoo, " + " drakkin_details, " + " toxicity, " + " hunger_level, " + " thirst_level, " + " ability_up, " + " zone_id, " + " zone_instance, " + " leadership_exp_on, " + " ldon_points_guk, " + " ldon_points_mir, " + " ldon_points_mmc, " + " ldon_points_ruj, " + " ldon_points_tak, " + " ldon_points_available, " + " tribute_time_remaining, " + " show_helm, " + " career_tribute_points, " + " tribute_points, " + " tribute_active, " + " endurance, " + " group_leadership_exp, " + " raid_leadership_exp, " + " group_leadership_points, " + " raid_leadership_points, " + " air_remaining, " + " pvp_kills, " + " pvp_deaths, " + " pvp_current_points, " + " pvp_career_points, " + " pvp_best_kill_streak, " + " pvp_worst_death_streak, " + " pvp_current_kill_streak, " + " aa_points_spent, " + " aa_exp, " + " aa_points, " + " group_auto_consent, " + " raid_auto_consent, " + " guild_auto_consent, " + " RestTimer, " + " e_aa_effects, " + " e_percent_to_aa, " + " e_expended_aa_spent " + ") " + "VALUES (" + "%u," // id " id, " + "%u," // account_id " account_id, " + "'%s'," // `name` pp->name, " `name`, " + "'%s'," // last_name pp->last_name, " last_name, " + "%u," // gender pp->gender, " gender, " + "%u," // race pp->race, " race, " + "%u," // class pp->class_, " class, " + "%u," // `level` pp->level, " `level`, " + "%u," // deity pp->deity, " deity, " + "%u," // birthday pp->birthday, " birthday, " + "%u," // last_login pp->lastlogin, " last_login, " + "%u," // time_played pp->timePlayedMin, " time_played, " + "%u," // pvp_status pp->pvp, " pvp_status, " + "%u," // level2 pp->level2, " level2, " + "%u," // anon pp->anon, " anon, " + "%u," // gm pp->gm, " gm, " + "%u," // intoxication pp->intoxication, " intoxication, " + "%u," // hair_color pp->haircolor, " hair_color, " + "%u," // beard_color pp->beardcolor, " beard_color, " + "%u," // eye_color_1 pp->eyecolor1, " eye_color_1, " + "%u," // eye_color_2 pp->eyecolor2, " eye_color_2, " + "%u," // hair_style pp->hairstyle, " hair_style, " + "%u," // beard pp->beard, " beard, " + "%u," // ability_time_seconds pp->ability_time_seconds, " ability_time_seconds, " + "%u," // ability_number pp->ability_number, " ability_number, " + "%u," // ability_time_minutes pp->ability_time_minutes, " ability_time_minutes, " + "%u," // ability_time_hours pp->ability_time_hours, " ability_time_hours, " + "'%s'," // title pp->title, " title, " " + "'%s'," // suffix pp->suffix, " suffix, " + "%u," // exp pp->exp, " exp, " + "%u," // points pp->points, " points, " + "%u," // mana pp->mana, " mana, " + "%u," // cur_hp pp->cur_hp, " cur_hp, " + "%u," // str pp->STR, " str, " + "%u," // sta pp->STA, " sta, " + "%u," // cha pp->CHA, " cha, " + "%u," // dex pp->DEX, " dex, " + "%u," // `int` pp->INT, " `int`, " + "%u," // agi pp->AGI, " agi, " + "%u," // wis pp->WIS, " wis, " + "%u," // face pp->face, " face, " + "%f," // y pp->y, " y, " + "%f," // x pp->x, " x, " + "%f," // z pp->z, " z, " + "%f," // heading pp->heading, " heading, " + "%u," // pvp2 pp->pvp2, " pvp2, " + "%u," // pvp_type pp->pvptype, " pvp_type, " + "%u," // autosplit_enabled pp->autosplit, " autosplit_enabled, " + "%u," // zone_change_count pp->zone_change_count, " zone_change_count, " + "%u," // drakkin_heritage pp->drakkin_heritage, " drakkin_heritage, " + "%u," // drakkin_tattoo pp->drakkin_tattoo, " drakkin_tattoo, " + "%u," // drakkin_details pp->drakkin_details, " drakkin_details, " + "%i," // toxicity pp->toxicity, " toxicity, " + "%i," // hunger_level pp->hunger_level, " hunger_level, " + "%i," // thirst_level pp->thirst_level, " thirst_level, " + "%u," // ability_up pp->ability_up, " ability_up, " + "%u," // zone_id pp->zone_id, " zone_id, " + "%u," // zone_instance pp->zoneInstance, " zone_instance, " + "%u," // leadership_exp_on pp->leadAAActive, " leadership_exp_on, " + "%u," // ldon_points_guk pp->ldon_points_guk, " ldon_points_guk, " + "%u," // ldon_points_mir pp->ldon_points_mir, " ldon_points_mir, " + "%u," // ldon_points_mmc pp->ldon_points_mmc, " ldon_points_mmc, " + "%u," // ldon_points_ruj pp->ldon_points_ruj, " ldon_points_ruj, " + "%u," // ldon_points_tak pp->ldon_points_tak, " ldon_points_tak, " + "%u," // ldon_points_available pp->ldon_points_available, " ldon_points_available, " + "%u," // tribute_time_remaining pp->tribute_time_remaining, " tribute_time_remaining, " + "%u," // show_helm pp->showhelm, " show_helm, " + "%u," // career_tribute_points pp->career_tribute_points, " career_tribute_points, " + "%u," // tribute_points pp->tribute_points, " tribute_points, " + "%u," // tribute_active pp->tribute_active, " tribute_active, " + "%u," // endurance pp->endurance, " endurance, " + "%u," // group_leadership_exp pp->group_leadership_exp, " group_leadership_exp, " + "%u," // raid_leadership_exp pp->raid_leadership_exp, " raid_leadership_exp, " + "%u," // group_leadership_points pp->group_leadership_points, " group_leadership_points, " + "%u," // raid_leadership_points pp->raid_leadership_points, " raid_leadership_points, " + "%u," // air_remaining pp->air_remaining, " air_remaining, " + "%u," // pvp_kills pp->PVPKills, " pvp_kills, " + "%u," // pvp_deaths pp->PVPDeaths, " pvp_deaths, " + "%u," // pvp_current_points pp->PVPCurrentPoints, " pvp_current_points, " + "%u," // pvp_career_points pp->PVPCareerPoints, " pvp_career_points, " + "%u," // pvp_best_kill_streak pp->PVPBestKillStreak, " pvp_best_kill_streak, " + "%u," // pvp_worst_death_streak pp->PVPWorstDeathStreak, " pvp_worst_death_streak, " + "%u," // pvp_current_kill_streak pp->PVPCurrentKillStreak, " pvp_current_kill_streak, " + "%u," // aa_points_spent pp->aapoints_spent, " aa_points_spent, " + "%u," // aa_exp pp->expAA, " aa_exp, " + "%u," // aa_points pp->aapoints, " aa_points, " + "%u," // group_auto_consent pp->groupAutoconsent, " group_auto_consent, " + "%u," // raid_auto_consent pp->raidAutoconsent, " raid_auto_consent, " + "%u," // guild_auto_consent pp->guildAutoconsent, " guild_auto_consent, " + "%u," // RestTimer pp->RestTimer, " RestTimer) " + "%u," // e_aa_effects + "%u," // e_percent_to_aa + "%u" // e_expended_aa_spent + ")", + character_id, // " id, " + account_id, // " account_id, " + EscapeString(pp->name).c_str(), // " `name`, " + EscapeString(pp->last_name).c_str(), // " last_name, " + pp->gender, // " gender, " + pp->race, // " race, " + pp->class_, // " class, " + pp->level, // " `level`, " + pp->deity, // " deity, " + pp->birthday, // " birthday, " + pp->lastlogin, // " last_login, " + pp->timePlayedMin, // " time_played, " + pp->pvp, // " pvp_status, " + pp->level2, // " level2, " + pp->anon, // " anon, " + pp->gm, // " gm, " + pp->intoxication, // " intoxication, " + pp->haircolor, // " hair_color, " + pp->beardcolor, // " beard_color, " + pp->eyecolor1, // " eye_color_1, " + pp->eyecolor2, // " eye_color_2, " + pp->hairstyle, // " hair_style, " + pp->beard, // " beard, " + pp->ability_time_seconds, // " ability_time_seconds, " + pp->ability_number, // " ability_number, " + pp->ability_time_minutes, // " ability_time_minutes, " + pp->ability_time_hours, // " ability_time_hours, " + EscapeString(pp->title).c_str(), // " title, " + EscapeString(pp->suffix).c_str(), // " suffix, " + pp->exp, // " exp, " + pp->points, // " points, " + pp->mana, // " mana, " + pp->cur_hp, // " cur_hp, " + pp->STR, // " str, " + pp->STA, // " sta, " + pp->CHA, // " cha, " + pp->DEX, // " dex, " + pp->INT, // " `int`, " + pp->AGI, // " agi, " + pp->WIS, // " wis, " + pp->face, // " face, " + pp->y, // " y, " + pp->x, // " x, " + pp->z, // " z, " + pp->heading, // " heading, " + pp->pvp2, // " pvp2, " + pp->pvptype, // " pvp_type, " + pp->autosplit, // " autosplit_enabled, " + pp->zone_change_count, // " zone_change_count, " + pp->drakkin_heritage, // " drakkin_heritage, " + pp->drakkin_tattoo, // " drakkin_tattoo, " + pp->drakkin_details, // " drakkin_details, " + pp->toxicity, // " toxicity, " + pp->hunger_level, // " hunger_level, " + pp->thirst_level, // " thirst_level, " + pp->ability_up, // " ability_up, " + pp->zone_id, // " zone_id, " + pp->zoneInstance, // " zone_instance, " + pp->leadAAActive, // " leadership_exp_on, " + pp->ldon_points_guk, // " ldon_points_guk, " + pp->ldon_points_mir, // " ldon_points_mir, " + pp->ldon_points_mmc, // " ldon_points_mmc, " + pp->ldon_points_ruj, // " ldon_points_ruj, " + pp->ldon_points_tak, // " ldon_points_tak, " + pp->ldon_points_available, // " ldon_points_available, " + pp->tribute_time_remaining, // " tribute_time_remaining, " + pp->showhelm, // " show_helm, " + pp->career_tribute_points, // " career_tribute_points, " + pp->tribute_points, // " tribute_points, " + pp->tribute_active, // " tribute_active, " + pp->endurance, // " endurance, " + pp->group_leadership_exp, // " group_leadership_exp, " + pp->raid_leadership_exp, // " raid_leadership_exp, " + pp->group_leadership_points, // " group_leadership_points, " + pp->raid_leadership_points, // " raid_leadership_points, " + pp->air_remaining, // " air_remaining, " + pp->PVPKills, // " pvp_kills, " + pp->PVPDeaths, // " pvp_deaths, " + pp->PVPCurrentPoints, // " pvp_current_points, " + pp->PVPCareerPoints, // " pvp_career_points, " + pp->PVPBestKillStreak, // " pvp_best_kill_streak, " + pp->PVPWorstDeathStreak, // " pvp_worst_death_streak, " + pp->PVPCurrentKillStreak, // " pvp_current_kill_streak, " + pp->aapoints_spent, // " aa_points_spent, " + pp->expAA, // " aa_exp, " + pp->aapoints, // " aa_points, " + pp->groupAutoconsent, // " group_auto_consent, " + pp->raidAutoconsent, // " raid_auto_consent, " + pp->guildAutoconsent, // " guild_auto_consent, " + pp->RestTimer, // " RestTimer) " + m_epp->aa_effects, + m_epp->perAA, + m_epp->expended_aa + ); + auto results = database.QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase:SaveCharacterData", query); + LogFile->write(EQEMuLog::Debug, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC); + return true; +} + +bool ZoneDatabase::SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp){ + if (pp->copper < 0) { pp->copper = 0; } + if (pp->silver < 0) { pp->silver = 0; } + if (pp->gold < 0) { pp->gold = 0; } + if (pp->platinum < 0) { pp->platinum = 0; } + if (pp->copper_bank < 0) { pp->copper_bank = 0; } + if (pp->silver_bank < 0) { pp->silver_bank = 0; } + if (pp->gold_bank < 0) { pp->gold_bank = 0; } + if (pp->platinum_bank < 0) { pp->platinum_bank = 0; } + if (pp->platinum_cursor < 0) { pp->platinum_cursor = 0; } + if (pp->gold_cursor < 0) { pp->gold_cursor = 0; } + if (pp->silver_cursor < 0) { pp->silver_cursor = 0; } + if (pp->copper_cursor < 0) { pp->copper_cursor = 0; } + std::string query = StringFormat( + "REPLACE INTO `character_currency` (id, platinum, gold, silver, copper," + "platinum_bank, gold_bank, silver_bank, copper_bank," + "platinum_cursor, gold_cursor, silver_cursor, copper_cursor, " + "radiant_crystals, career_radiant_crystals, ebon_crystals, career_ebon_crystals)" + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u)", + character_id, + pp->platinum, + pp->gold, + pp->silver, + pp->copper, + pp->platinum_bank, + pp->gold_bank, + pp->silver_bank, + pp->copper_bank, + pp->platinum_cursor, + pp->gold_cursor, + pp->silver_cursor, + pp->copper_cursor, + pp->currentRadCrystals, + pp->careerRadCrystals, + pp->currentEbonCrystals, + pp->careerEbonCrystals); + auto results = database.QueryDatabase(query); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterCurrency", query); + LogFile->write(EQEMuLog::Debug, "Saving Currency for character ID: %i, done", character_id); + return true; +} + +bool ZoneDatabase::SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level){ + std::string rquery = StringFormat("REPLACE INTO `character_alternate_abilities` (id, aa_id, aa_value)" + " VALUES (%u, %u, %u)", + character_id, aa_id, current_level); + auto results = QueryDatabase(rquery); + ThrowDBError(results.ErrorMessage(), "ZoneDatabase::SaveCharacterAA", rquery); + LogFile->write(EQEMuLog::Debug, "Saving AA for character ID: %u, aa_id: %u current_level: %u", character_id, aa_id, current_level); + return true; +} + +bool ZoneDatabase::SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("REPLACE INTO `character_memmed_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("REPLACE INTO `character_spells` (id, slot_id, spell_id) VALUES (%u, %u, %u)", character_id, slot_id, spell_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("DELETE FROM `character_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterDisc(uint32 character_id, uint32 slot_id){ + std::string query = StringFormat("DELETE FROM `character_disciplines` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterBandolier(uint32 character_id, uint32 band_id){ + std::string query = StringFormat("DELETE FROM `character_bandolier` WHERE `bandolier_id` = %u AND `id` = %u", band_id, character_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterLeadershipAAs(uint32 character_id){ + std::string query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", character_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterAAs(uint32 character_id){ + std::string query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", character_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterDye(uint32 character_id){ + std::string query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", character_id); + QueryDatabase(query); + return true; +} + +bool ZoneDatabase::DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id){ + std::string query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `slot_id` = %u AND `id` = %u", slot_id, character_id); + QueryDatabase(query); + return true; } bool ZoneDatabase::NoRentExpired(const char* name){ - std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW())-timelaston) " - "FROM character_ WHERE name = '%s'", name); + std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW()) - last_login) FROM `character_data` WHERE name = '%s'", name); auto results = QueryDatabase(query); if (!results.Success()) return false; @@ -1711,7 +2534,7 @@ void ZoneDatabase::RefreshGroupFromDB(Client *client){ gu->action = groupActUpdate; strcpy(gu->yourname, client->GetName()); - GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); + GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); gu->NPCMarkerID = group->GetNPCMarkerID(); int index = 0; @@ -2072,96 +2895,74 @@ void ZoneDatabase::LoadBuffs(Client *client) { } } -void ZoneDatabase::SavePetInfo(Client *client) { +void ZoneDatabase::SavePetInfo(Client *client) +{ + PetInfo *petinfo = nullptr; - 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()) + std::string query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) return; - query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); + query = StringFormat("DELETE FROM `character_pet_inventory` WHERE `char_id` = %u", client->CharacterID()); results = database.QueryDatabase(query); - if(!results.Success()) + if (!results.Success()) return; - 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; + for (int pet = 0; pet < 2; pet++) { + petinfo = client->GetPetInfo(pet); + if (!petinfo) + continue; - 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_info` " + "(`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " + "VALUES (%u, %u, '%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(), pet, petinfo->Name, petinfo->petpower, petinfo->SpellID, + petinfo->HP, petinfo->Mana, petinfo->size, // and now the ON DUPLICATE ENTRIES + petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size); + results = database.QueryDatabase(query); + if (!results.Success()) + return; + query.clear(); - 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); - } + // pet buffs! + for (int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { + if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) + continue; + if (query.length() == 0) + query = StringFormat("INSERT INTO `character_pet_buffs` " + "(`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " + "`ticsremaining`, `counters`) " + "VALUES (%u, %u, %u, %u, %u, %u, %d)", + client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, + petinfo->Buffs[index].level, petinfo->Buffs[index].duration, + petinfo->Buffs[index].counters); + else + query += StringFormat(", (%u, %u, %u, %u, %u, %u, %d)", + client->CharacterID(), pet, index, petinfo->Buffs[index].spellid, + petinfo->Buffs[index].level, petinfo->Buffs[index].duration, + petinfo->Buffs[index].counters); + } + database.QueryDatabase(query); + query.clear(); - for(int index = 0; index < RuleI(Spells, MaxTotalSlotsPET); index++) { - if (petinfo->Buffs[index].spellid == SPELL_UNKNOWN || petinfo->Buffs[index].spellid == 0) - continue; + // pet inventory! + for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { + if (!petinfo->Items[index]) + 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); + if (query.length() == 0) + query = StringFormat("INSERT INTO `character_pet_inventory` " + "(`char_id`, `pet`, `slot`, `item_id`) " + "VALUES (%u, %u, %u, %u)", + client->CharacterID(), pet, index, petinfo->Items[index]); + else + query += StringFormat(", (%u, %u, %u, %u)", client->CharacterID(), pet, index, petinfo->Items[index]); + } + database.QueryDatabase(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); - } - - 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; - - - 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 *client) { @@ -2335,31 +3136,6 @@ bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race return true; } -bool ZoneDatabase::LoadFactionValues(uint32 char_id, faction_map & val_list) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT faction_id,current_value FROM faction_values WHERE char_id = %i",char_id), errbuf, &result)) { - safe_delete_array(query); - bool ret = LoadFactionValues_result(result, val_list); - mysql_free_result(result); - return ret; - } - else { - std::cerr << "Error in LoadFactionValues query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return false; -} - -bool ZoneDatabase::LoadFactionValues_result(MYSQL_RES* result, faction_map & val_list) { - MYSQL_ROW row; - while((row = mysql_fetch_row(result))) { - val_list[atoi(row[0])] = atoi(row[1]); - } - return true; -} - //o-------------------------------------------------------------- //| Name: GetFactionName; rembrant, Dec. 16 //o-------------------------------------------------------------- @@ -2528,11 +3304,3 @@ bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::list + //#include "doors.h" struct wplist { @@ -212,9 +214,7 @@ public: ZoneDatabase(const char* host, const char* user, const char* passwd, const char* database,uint32 port); virtual ~ZoneDatabase(); - /* - * Objects and World Containers - */ + /* Objects and World Containers */ void LoadWorldContainer(uint32 parentid, ItemInst* container); void SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container); void DeleteWorldContainer(uint32 parent_id,uint32 zone_id); @@ -223,10 +223,7 @@ public: void DeleteObject(uint32 id); Ground_Spawns* LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs); - /* - * Traders - */ - + /* Traders */ void SaveTraderItem(uint32 char_id,uint32 itemid,uint32 uniqueid, int32 charges,uint32 itemcost,uint8 slot); void UpdateTraderItemCharges(int char_id, uint32 ItemInstID, int32 charges); void UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice); @@ -236,38 +233,67 @@ public: Trader_Struct* LoadTraderItem(uint32 char_id); TraderCharges_Struct* LoadTraderItemWithCharges(uint32 char_id); - // Buyer/Barter - // + /* Buyer/Barter */ void AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char *ItemName, uint32 Quantity, uint32 Price); void RemoveBuyLine(uint32 CharID, uint32 BuySlot); void DeleteBuyLines(uint32 CharID); void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity); - /* - * General Character Related Stuff - */ - void StoreCharacterLookup(uint32 char_id); - bool GetAccountInfoForLogin_result(MYSQL_RES* result, int16* admin = 0, char* account_name = 0, - uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = nullptr, - uint32* account_creation = 0); - bool GetCharacterInfoForLogin_result(MYSQL_RES* result, uint32* character_id = 0, char* current_zone = 0, - PlayerProfile_Struct* pp = 0, Inventory* inv = 0, ExtendedProfile_Struct *ext = 0, uint32* pplen = 0, - uint32* guilddbid = 0, uint8* guildrank = 0, uint8 *class_= 0, uint8 *level = 0, bool *LFP = 0, - bool *LFG = 0, uint8 *NumXTargets = 0, uint8* firstlogon = 0); + /* General Character Related Stuff */ + bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs); + uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs); + void SaveBuffs(Client *c); void LoadBuffs(Client *c); void LoadPetInfo(Client *c); void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); - /* - * Character Inventory - */ + /* Character Data Loaders */ + bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list); + bool LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); + bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp); + bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); + + /* Character Data Saves */ + bool SaveCharacterBindPoint(uint32 character_id, uint32 zone_id, uint32 instance_id, float x, float y, float z, float heading, uint8 is_home); + bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp); + bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp); + bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level); + bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color); + bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value); + bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value); + bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id); + bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp); + bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name); + bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon); + bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp); + + /* Character Data Deletes */ + bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id); + bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id); + bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id); + bool DeleteCharacterLeadershipAAs(uint32 character_id); + bool DeleteCharacterAAs(uint32 character_id); + bool DeleteCharacterDye(uint32 character_id); + + /* Character Inventory */ bool NoRentExpired(const char* name); - /* - * Corpses - */ + /* Corpses */ bool GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes); uint32 CreatePlayerCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading); bool CreatePlayerCorpseBackup(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading); @@ -293,21 +319,15 @@ public: uint32 GetPlayerCorpseItemAt(uint32 corpse_id, uint16 slotid); uint32 GetPlayerCorpseTimeLeft(uint8 corpse, uint8 type); - /* - * Faction - */ + /* Faction */ bool GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, int32* value, uint8* temp, int32* primary_faction = 0); bool GetFactionData(FactionMods* fd, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id); //rembrant, needed for factions Dec, 16 2001 bool GetFactionName(int32 faction_id, char* name, uint32 buflen); // rembrant, needed for factions Dec, 16 2001 bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // neotokyo: improve faction handling bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // rembrant, needed for factions Dec, 16 2001 bool LoadFactionData(); - bool LoadFactionValues(uint32 char_id, faction_map & val_list); - bool LoadFactionValues_result(MYSQL_RES* result, faction_map & val_list); - /* - * AAs - */ + /* AAs */ bool LoadAAEffects(); bool LoadAAEffects2(); bool LoadSwarmSpells(); @@ -319,9 +339,7 @@ public: uint32 CountAAEffects(); void FillAAEffects(SendAA_Struct* aa_struct); - /* - * Zone related - */ + /* Zone related */ bool GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *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); bool SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd); bool LoadStaticZonePoints(LinkedList* zone_point_list,const char* zonename, uint32 version); @@ -329,9 +347,7 @@ public: uint8 GetUseCFGSafeCoords(); int getZoneShutDownDelay(uint32 zoneID, uint32 version); - /* - * Spawns and Spawn Points - */ + /* Spawns and Spawn Points */ bool LoadSpawnGroups(const char* zone_name, uint16 version, SpawnGroupList* spawn_group_list); bool LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_group_list); bool PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay = 0); @@ -341,9 +357,7 @@ public: uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id); void UpdateSpawn2Status(uint32 id, uint8 new_status); - /* - * Grids/Paths - */ + /* Grids/Paths */ uint32 GetFreeGrid(uint16 zoneid); void DeleteGrid(Client *c, uint32 sg2, uint32 grid_num, bool grid_too,uint16 zoneid); void DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num,uint16 zoneid); @@ -359,11 +373,16 @@ public: int GetHighestGrid(uint32 zoneid); int GetHighestWaypoint(uint32 zoneid, uint32 gridid); - /* - * NPCs - */ + /* NPCs */ const NPCType* GetNPCType(uint32 id); uint32 NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete + uint32 CreateNewNPCCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 extra); + uint32 AddNewNPCSpawnGroupCommand(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 respawnTime); + uint32 DeleteSpawnLeaveInNPCTypeTable(const char* zone, Client *client, NPC* spawn); + uint32 DeleteSpawnRemoveFromNPCTypeTable(const char* zone, uint32 zone_version, Client *client, NPC* spawn); + uint32 AddSpawnFromSpawnGroup(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); + uint32 AddNPCTypes(const char* zone, uint32 zone_version, Client *client, NPC* spawn, uint32 spawnGroupID); + uint32 UpdateNPCTypeAppearance(Client *client, NPC* spawn); bool SetSpecialAttkFlag(uint8 id, const char* flag); bool GetPetEntry(const char *pet_type, PetRecord *into); bool GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into); @@ -375,9 +394,7 @@ public: DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID); - /* - * Mercs - */ + /* Mercs */ const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel); void LoadMercEquipment(Merc *merc); void SaveMercBuffs(Merc *merc); @@ -389,9 +406,7 @@ public: //void LoadMercTypesForMercMerchant(NPC *merchant); //void LoadMercsForMercMerchant(NPC *merchant); - /* - * Petitions - */ + /* Petitions */ void UpdateBug(BugStruct* bug); void UpdateBug(PetitionBug_Struct* bug); void DeletePetitionFromDB(Petition* wpet); @@ -399,16 +414,11 @@ public: void InsertPetitionToDB(Petition* wpet); void RefreshPetitionsFromDB(); - - /* - * Merchants - */ + /* Merchants */ void SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges); void DeleteMerchantTemp(uint32 npcid, uint32 slot); - /* - * Tradeskills - */ + /* Tradeskills */ bool GetTradeRecipe(const ItemInst* container, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec); bool GetTradeRecipe(uint32 recipe_id, uint8 c_type, uint32 some_id, uint32 char_id, DBTradeskillRecipe_Struct *spec); uint32 GetZoneForage(uint32 ZoneID, uint8 skill); /* for foraging */ @@ -417,14 +427,10 @@ public: bool EnableRecipe(uint32 recipe_id); bool DisableRecipe(uint32 recipe_id); - /* - * Tribute - */ + /* Tribute */ bool LoadTributes(); - /* - * Doors - */ + /* Doors */ bool DoorIsOpen(uint8 door_id,const char* zone_name); void SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name); bool LoadDoors(int32 iDoorCount, Door *into, const char *zone_name, int16 version); @@ -437,64 +443,46 @@ public: int32 GetDoorsDBCountPlusOne(const char *zone_name, int16 version); void InsertDoor(uint32 did, 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); - /* - * Blocked Spells - */ - + /* Blocked Spells */ int32 GetBlockedSpellsCount(uint32 zoneid); bool LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid); - /* - * Traps - */ + /* Traps */ bool LoadTraps(const char* zonename, int16 version); char* GetTrapMessage(uint32 trap_id); - /* - * Time - */ + /* Time */ uint32 GetZoneTZ(uint32 zoneid, uint32 version); bool SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz); - /* - * Group - */ + /* Group */ void RefreshGroupFromDB(Client *c); uint8 GroupCount(uint32 groupid); - /* - * Raid - */ + + /* Raid */ uint8 RaidGroupCount(uint32 raidid, uint32 groupid); - /* - * Instancing - */ + /* Instancing */ void ListAllInstances(Client* c, uint32 charid); - /* - * QGlobals - */ + /* QGlobals */ void QGlobalPurge(); - /* - * Alternate Currency - */ + /* Alternate Currency */ void LoadAltCurrencyValues(uint32 char_id, std::map ¤cy); void UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value); /* - * Misc stuff. - * PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD - * REALLY HAS NO BETTER SECTION + * Misc stuff. + * PLEASE DO NOT ADD TO THIS COLLECTION OF CRAP UNLESS YOUR METHOD + * REALLY HAS NO BETTER SECTION */ bool logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname,const char* target, const char* descriptiontype, const char* description,int event_nid); void GetEventLogs(const char* name,char* target,uint32 account_id=0,uint8 eventid=0,char* detail=0,char* timestamp=0, CharacterEventLog_Struct* cel=0); uint32 GetKarma(uint32 acct_id); void UpdateKarma(uint32 acct_id, uint32 amount); - /* - * Things which really dont belong here... - */ + /* Things which really dont belong here... */ int16 CommandRequirement(const char* commandname); protected: diff --git a/zone/zonedbasync.cpp b/zone/zonedbasync.cpp deleted file mode 100644 index 579517d55..000000000 --- a/zone/zonedbasync.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "../common/debug.h" -#include -#include "entity.h" -#include "masterentity.h" -#include "../common/string_util.h" -#include "../common/breakdowns.h" -#include - -extern EntityList entity_list; - -void DispatchFinishedDBAsync(DBAsyncWork* dbaw) { - uint32_breakdown workpt; - workpt = dbaw->WPT(); - switch (workpt.b4()) { -/* case DBA_b4_Main: { - switch (workpt.i24_1()) { - case DBA_i24_1_Main_LoadVariables: { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* result; - DBAsyncQuery* dbaq = dbaw->PopAnswer(); - if (dbaq->GetAnswer(errbuf, result)) - database.LoadVariables_result(result); - else - std::cout << "Async DB.LoadVariables() failed: '" << errbuf << "'" << std::endl; - break; - } - default: { - std::cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4" << std::endl; - break; - } - } - }*/ - case DBA_b4_Zone: { - if(zone == nullptr) - break; - zone->DBAWComplete(workpt.b1(), dbaw); - break; - } - case DBA_b4_Entity: { - Entity* entity = entity_list.GetID(workpt.w2_3()); - if (!entity) - break; - entity->DBAWComplete(workpt.b1(), dbaw); - break; - } - default: { - std::cout << "Error: DispatchFinishedDBAsync(): Unknown workpt.b4: " << (int) workpt.b4() << ", workpt=" << workpt << std::endl; - break; - } - } - safe_delete(dbaw); -} - -#define MAX_TO_DELETE 10 -#define MAX_BACKUPS 5 -bool DBAsyncCB_CharacterBackup(DBAsyncWork* iWork) { // return true means delete data - char errbuf[MYSQL_ERRMSG_SIZE] = "dbaq == 0"; - MYSQL_RES* result = 0; - MYSQL_ROW row; - char* query = 0; - uint32 i; - uint8 ToDeleteIndex = 0; - uint32 ToDelete[MAX_TO_DELETE]; - memset(ToDelete, 0, sizeof(ToDelete)); - - uint32 BackupAges[MAX_BACKUPS]; // must be sorted, highest value in lowest index - memset(BackupAges, 0, sizeof(BackupAges)); - - bool FoundBackup[MAX_BACKUPS]; - memset(FoundBackup, 0, sizeof(FoundBackup)); - - BackupAges[0] = 86400; - BackupAges[1] = 3600; - - DBAsyncQuery* dbaq = iWork->PopAnswer(); - if (dbaq && dbaq->GetAnswer(errbuf, &result)) { - while ((row = mysql_fetch_row(result))) { - for (i=0; i BackupAges[i]) - i = MAX_BACKUPS; - else if (!FoundBackup[i]) { - FoundBackup[i] = true; - break; - } - } - if (i >= MAX_BACKUPS) - ToDelete[ToDeleteIndex++] = atoi(row[0]); - if (ToDeleteIndex >= MAX_TO_DELETE) - break; - } - if (ToDelete[0]) { - uint32 len = 0, size = 0; - AppendAnyLenString(&query, &size, &len, "Delete from character_backup where id=%u", ToDelete[0]); - for (uint8 i=1; iwrite(EQEMuLog::Error, "Error in DBAsyncCB_CharacterBackup query2 '%s' %s", query, errbuf); - safe_delete_array(query); - return true; - } - safe_delete_array(query); - } - bool needtoinsert = false; - for (i=0; iWPT()), errbuf)) { - std::cout << "Error in DBAsyncCB_CharacterBackup query3 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return true; - } - safe_delete_array(query); - } - } - else { -// std::cout << "Error in DBAsyncCB_CharacterBackup query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return true; - } - return true; -} diff --git a/zone/zonedbasync.h b/zone/zonedbasync.h deleted file mode 100644 index b91389322..000000000 --- a/zone/zonedbasync.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ZONEDBASYNC_H -#define ZONEDBASYNC_H - -#include "../common/dbasync.h" -void DispatchFinishedDBAsync(DBAsyncWork* iDBAW); -bool DBAsyncCB_CharacterBackup(DBAsyncWork* iWork); - -#define DBA_b4_Main 1 -#define DBA_b4_Worldserver 2 -#define DBA_b4_Zone 3 -#define DBA_b4_Entity 4 - -#define DBA_b1_Entity_SeeQPT 0 -#define DBA_b1_Entity_Client_InfoForLogin 1 -#define DBA_b1_Entity_Client_Save 2 -#define DBA_b1_Entity_Client_Backup 3 -#define DBA_b1_Entity_Corpse_Backup 4 -#define DBA_b1_Zone_MerchantLists 5 -#define DBA_b1_Zone_MerchantListsTemp 6 - -#endif - diff --git a/zone/zoning.cpp b/zone/zoning.cpp index b50e43d33..6a8abb6d4 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -354,7 +354,7 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc m_pp.zoneInstance = instance_id; //Force a save so its waiting for them when they zone - Save(2); + Save(2); if (zone_id == zone->GetZoneID() && instance_id == zone->GetInstanceID()) { // No need to ask worldserver if we're zoning to ourselves (most @@ -704,6 +704,7 @@ void Client::SetBindPoint(int to_zone, float new_x, float new_y, float new_z) { m_pp.binds[0].y = new_y; m_pp.binds[0].z = new_z; } + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[0].zoneId, 0, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0, 0); } void Client::GoToBind(uint8 bindnum) {