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 afe35dddd..584fe5a64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ #EQEMU_DISABLE_LOGSYS #EQEMU_COMMANDS_LOGGING #EQEMU_BUILD_SERVER -#EQEMU_BUILD_LOGIN -#EQEMU_BUILD_TESTS +#EQEMU_BUILD_LOGIN +#EQEMU_BUILD_TESTS #EQEMU_BUILD_PERL #EQEMU_BUILD_LUA #EQEMU_SANITIZE_LUA_LIBS @@ -103,7 +103,7 @@ IF(MSVC) SET(CMAKE_MODULE_LINKER_FLAGS_RELEASE "${CMAKE_MODULE_LINKER_FLAGS_RELEASE} /SAFESEH:NO") SET(CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO} /SAFESEH:NO") ENDIF(EQEMU_DISABLE_SAFESEH) - + OPTION(EQEMU_BUILD_MSVC_MP "Enable build with multiple processes." ON) IF(EQEMU_BUILD_MSVC_MP) SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") @@ -115,7 +115,7 @@ IF(MSVC) STRING(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}") ENDIF(${flag_var} MATCHES "/MD") ENDFOREACH(flag_var) - + ADD_DEFINITIONS(-DNOMINMAX) ELSE(MSVC) #Normally set by perl but we don't use the perl flags anymore so we set it. @@ -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 Inport/Export Data Programs." ON) #C++11 stuff IF(NOT MSVC) - ADD_DEFINITIONS(-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) @@ -301,26 +306,26 @@ FIND_PACKAGE(ZLIB REQUIRED) FIND_PACKAGE(MySQL REQUIRED) IF(EQEMU_BUILD_PERL) FIND_PACKAGE(PerlLibs REQUIRED) - INCLUDE_DIRECTORIES("${PERL_INCLUDE_PATH}") + INCLUDE_DIRECTORIES(SYSTEM "${PERL_INCLUDE_PATH}") ENDIF(EQEMU_BUILD_PERL) IF(EQEMU_BUILD_LUA) FIND_PACKAGE(EQLua51 REQUIRED) SET(Boost_USE_STATIC_LIBS OFF) - SET(Boost_USE_MULTITHREADED ON) + SET(Boost_USE_MULTITHREADED ON) SET(Boost_USE_STATIC_RUNTIME OFF) SET(BOOST_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/boost") FIND_PACKAGE(Boost REQUIRED) - INCLUDE_DIRECTORIES("${LUA_INCLUDE_DIR}" "${Boost_INCLUDE_DIRS}" "dependencies/luabind") - + INCLUDE_DIRECTORIES(SYSTEM "${LUA_INCLUDE_DIR}" "${Boost_INCLUDE_DIRS}" "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/luabind") + OPTION(EQEMU_SANITIZE_LUA_LIBS "Sanitize Lua Libraries (Remove OS and IO standard libraries from being able to run)." ON) IF(EQEMU_SANITIZE_LUA_LIBS) ADD_DEFINITIONS(-DSANITIZE_LUA_LIBS) ENDIF(EQEMU_SANITIZE_LUA_LIBS) ENDIF(EQEMU_BUILD_LUA) -INCLUDE_DIRECTORIES("${ZLIB_INCLUDE_DIRS}" "${MySQL_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/common/glm/glm" "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libwebsockets" "${CMAKE_CURRENT_SOURCE_DIR}/common/rapidjson") +INCLUDE_DIRECTORIES(SYSTEM "${ZLIB_INCLUDE_DIRS}" "${MySQL_INCLUDE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/common/glm/glm" "${CMAKE_CURRENT_SOURCE_DIR}/dependencies/libwebsockets" "${CMAKE_CURRENT_SOURCE_DIR}/common/rapidjson") IF(EQEMU_BUILD_SERVER OR EQEMU_BUILD_LOGIN OR EQEMU_BUILD_TESTS) ADD_SUBDIRECTORY(common) diff --git a/changelog.txt b/changelog.txt index 556069f21..d8ed052ab 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,524 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 12/01/2014 == +Trevius: Mercenaries now spawn as the same Gender and Size of the Merchant they are purchased from. +Trevius: Mercenaries now spawn with randomized facial features when purchased. +Trevius: Setting a lastname for NPCs will now override any hard coded lastname (such as GM Trainers). + +Required SQL: utils/sql/git/required/2014_12_01_mercs_table_update.sql + +== 11/28/2014 == +Trevius: Fixed a zone crash related to numhits for spells. +Trevius: Fixed a query related to group leaders logging in. +Trevius (Natedog): Fixed a world crash related to attempting to join an adventure with Mercenaries. + +== 11/27/2014 == +Kayen: Projectiles (ie Arrows) fired from archery will now do damage upon impact instead of instantly (consistent w/ live). +Optional SQL: utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql + +== 11/25/2014 == +Trevius: Spells that modify model size are now limited to 2 size adjustments from the base size. +Trevius: Fix to prevent Mercenaries from being set as Group Leader. + +== 11/24/2014 == +Trevius: Added Rule NPC:EnableMeritBasedFaction (disabled by default) - Allows faction gain to work similar to experience. + +== 11/22/2014 == +Trevius: Grouping with Mercenaries is now considerably less buggy. +Trevius: Fixed an issue with Spell Globals related to high Character IDs. +Trevius: Crash fix for Swarm Pets. + +== 11/19/2014 == +Trevius: Mercenaries now Dismiss, Suspend, Unsuspend, and Die correctly. + +== 11/18/2014 == +Trevius: Mercenaries can now zone once again. + +== 11/17/2014 == +demonstar55: Correct OP_AugmentInfo reply. This fixes RoF display issue with Adventurer's Stone. Still issues with UF/SoF/SoD though. + +== 11/16/2014 == +demonstar55: fix size issue with ControlBoat_Struct and exploit fix in OP_BoardBoat + +Akkadius: Implemented Automatic Database update and versioning system +Akkadius: Created database revision define, this is located in version.h in common #define CURRENT_BINARY_DATABASE_VERSION 9057 + - This revision define will need to be incremented each time a database update is made + - Along with a revision define increment, you will need to update the db_update manifest located in: + - https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt + - An entry needs to be made at the bottom of the manifest, the entry is quite simple + - Example: 9057|2014_11_13_spells_new_updates.sql|SHOW COLUMNS FROM `spells_new` LIKE 'disallow_sit'|empty| + - This latest example is checking to see if the spells_new table contains the column 'disallow_sit', if its empty, the update needs to be ran + - More examples of match types below: + # Example: Version|Filename.sql|Query_to_Check_Condition_For_Needed_Update|match type|text to match + # 0 = Database Version + # 1 = Filename.sql + # 2 = Query_to_Check_Condition_For_Needed_Update + # 3 = Match Type - If condition from match type to Value 4 is true, update will flag for needing to be ran + # contains = If query results contains text from 4th value + # match = If query results matches text from 4th value + # missing = If query result is missing text from 4th value + # empty = If the query results in no results + # not_empty = If the query is not empty + # 4 = Text to match + - The manifest contains all database updates 'Required' to be made to the schema, and it will contain a working backport all the way back to SVN - + currently it is tested and backported through the beginning of our Github repo + - On world bootup or standalone run of db_update.pl, users will be prompted with a simple menu that we will expand upon later: + + ============================================================ + EQEmu: Automatic Database Upgrade Check + ============================================================ + Operating System is: MSWin32 + (Windows) MySQL is in system path + Path = C:\Program Files\MariaDB 10.0\bin/mysql + ============================================================ + Binary Database Version: (9057) + Local Database Version: (9057) + + Database up to Date: Continuing World Bootup... + ============================================================ + Retrieving latest database manifest... + URL: https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt + Saved: db_update/db_update_manifest.txt + +Database Management Menu (Please Select): + 1) Backup Database - (Saves to Backups folder) + Ideal to perform before performing updates + 2) Backup Database Compressed - (Saves to Backups folder) + Ideal to perform before performing updates + 3) Check for pending Database updates + Stages updates for automatic upgrade... + 0) Exit + +Akkadius: Created db_update.pl, placed in utils/scripts folder, used for the automatic database update routine (Linux/Windows) + - db_update.pl script created db_version table if not created, if old one is present it will remove it +Akkadius: Created db_dumper.pl, placed in utils/scripts folder, used for the automatic database update routine backups and standalone backups (Linux/Windows) +Akkadius: World will now check the db_update.pl script on bootup, if the db_update.pl script is not present, it will fetch it remotely before running - + when db_update.pl is done running, world will continue with bootup + +== 11/15/2014 == +Uleat(Natedog): A better fix for OP_ShopPlayerBuy - doesn't cause the issues that I introduced +Kayen: Implemented NPC Special Ability 41 'Allow To Tank', gives NPC opportunity to take aggro over a client in melee range. +Kayen: Updated swarm pet AI to be consistent with live. + +*OLD AI: Swarm pet would lock on to target until target died, then depop as soon as target died. + +*NEW AI: Swarm pet will attack cast on target, NOT perma locked it can change targets if attacked +by something else that generate more hate. When target dies swarm pet will follow owner, if owner is +attacked by something else the swarm pet will attack it (until duration timer despawns the pet). + +Kayen: Updated perl quest function: MakeTempPet(Tspell_id, name=nullptr, duration=0, target=nullptr, sticktarg=0) +Kayen: Implemented perl quest function: Mob::TypesTempPet(npctypesid, name=nullptr, duration=0, follow=0, target=nullptr, sticktarg=0) +Note: 'sticktarg' field will cause the swarm pet to use the OLD AI + +Rule to use OLD AI only - default is disabled. +Optional SQL: utils/sql/git/optional/2014_11_15_SwarmPetTargetLock.sql + +== 11/14/2014 == +Secrets: Identified object size and solidtype as flags. Exported them as functions to Perl. +demonstar55: Don't use the hack for charms that doesn't work on RoF +demonstar55: UF too +demonstar55: Tit +demonstar55: SoF +demonstar55: SoD +demonstar55: 62 (untested) + +== 11/13/2014 == +Kayen: Implemented target type (44) 'Beams' (which projects an AE infront of caster with a specified length and width). +Kayen: Implemented target type (32) AE Target HateList +Kayen: Implemented target type (36) Area Client Only +Kayen: Implemented target type (37) Area PC Only +Kayen: Implemented target type (39) Group No Pet +Uleat: PlayerLogMerchantTransactions does not support partial stack purchase logging at this time + +== 11/12/2014 == +Uleat: Changed 'GMTrainee' struct to reflect the actual client hard-coded max skill count (100) - applies to all currently supported clients (6.2->RoF) + +== 11/11/2014 == +Uleat: Third attempt at a fix for GM trainer zone crashes... (this is starting to look like a KLS fix...) + +== 11/10/2014 == +Uleat: Fix for GM Trainer crashing server (really!) +JJ: Yellow faction messages. + +== 11/09/2014 == +Kayen: Implemented support for spell target type (45) 'Target Rings' on Underfoot (does work earlier expansions). Thanks to Lecht for figuring out the op_code side. +JJ: Implement new Live-like faction adjustment message using rule Client:UseLiveFactionMessage. +Optional SQL: utils/sql/git/optional/2014_11_09_LiveFactionMessages.sql + +== 11/06/2014 == +demonstar55: Tracking default sort will now be correct order +Trevius: Fixed dynamic merchant list loading. Allows any merchant to be used in any zone. + +== 11/03/2014 == +Secrets: Fixed an overflow in melee lifetap calculations (int16 vs int32) +Secrets: Fixed overflow on AC and ATK values that can go out of range. +Secrets: Merc/Bot fixes for previous updates. +Secrets: Changed a lot of int16s for stat-related functions to int32 because they were causing combat formula overflows (int16/int32 mismatch). +Secrets: Linux fix? + +== 11/02/2014 == +Akkadius: Added out of range checking for Spell Save/Loads + +== 11/01/2014 == +Trevius: Fixed potential crash related to Pets/Mercs buffs when targeting themselves. +JJ: (noudess) Revamped faction system. See https://github.com/EQEmu/Server/pull/256 + +== 10/28/2014 == +Uleat: Added Client::InterrogateInventory(). Can be invoked by #interrogateinv and is also called when Handle_OP_MoveItem() calls for SwapItemResync() + +== 10/22/2014 == +Uleat: Fix for stacking items in a world object..added a new command option: #peekinv world - will show world container contents, if one is in use by target. + +== 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. +demonstar55: Fix some effect calcs + implement more (derived from the client) + +== 09/15/2014 == +Kayen: Nimbus effects will now be reapplied after zoning and will be removed when associated buff fades. + +== 09/13/2014 == +demonstar55: Fix rogues not having Thieves' Cant + +== 09/09/2014 == +demonstar55: Incrase Mob kick/bash timer by 3 + see: http://www.eqemulator.org/forums/showthread.php?t=38734 +demonstar55: Fix slow effect on NPC special attack reuse timers + see: http://www.eqemulator.org/forums/showthread.php?t=38734 +demonstar55: Slow fixes to bots! +demonstar55: Revamped how NPC attack rate is set + SQL: 2014_09_09_attack_delay.sql +demonstar55: Added attackdelay to #npcedit + +== 09/08/2014 == +demonstar55: Fix slow calc + see: http://www.eqemulator.org/forums/showthread.php?t=38734 + +== 09/07/2014 == +Akkadius: Fixed ROF Augment item dupe with not checking for available slots properly and adding items to the virtual instance + +== 09/06/2014 == +Uleat: Tweaked 'Smart' trading code to return main slots before sub slots in stackable and free space search processes. (If enough people ask for it, I'll add an optional rule to allow 'bag packing' - the original implementation behavior) + +== 09/05/2014 == +Uleat: Fix for cursor item loss when zoning. (Thanks to the other devs who traced and fixed the 'macro' issue!) +demonstar55: Fix size getting nuked with lua's SendIllusionPacket + +== 09/03/2014 == +Secrets: Identified the routines needed to augment items in RoF. Currently, only Insert and Remove are supported. Swap and Destroy do not work due to missing functions related to the cursor. +demonstar55: Added work around command to show numhits on your buffs (#shownumhits) +Uleat: Fix for timer issue introduced by Zone::ShutDown() fix. + +== 09/02/2014 == +Secrets: Identified OP_GuildPromote for RoF clients. +Secrets: Fixed promotion, demotion, transferring a leader and displaying of client ranks in the Rain of Fear client. The rain of fear client, as such, will only have 3 ranks like the other clients, but supports a theoretical 8 ranks later. +Secrets/Akkadius: Fixed an issue involving character name lookup in the new DB code. +demonstar55: crash fix checking DivineAura in hate_list.cpp +Secrets: Reverted some code that got in the main branch that shouldn't have gotten there. +Uleat: Changed #loc to report the same precision as /loc for Cartesians + +== 08/31/2014 == +KLS: Fixed a bug in fishing in S3D zones +KLS: Fixed a bug in turnins with new any abstraction +KLS: Fixed a few quest related inconsistencies. +KLS: Added Lua EntityList::ChannelMessage(from, type, msg, language) + +== 08/30/2014 == +demonstar55: (noudess) Merchants should be more descriptive of why they don't sell to you + +== 08/26/2014 == +Uleat: Implemented 'Smart' Player Trade transfers. Trades are processed by containers, stackables and then all remaining. QueryServ logs have been updated to match these transactions. +Note: QueryServ logs previously listed 'Items' on the main entry table. This indicated the number of slots affected and not the actual number of items. +This field now indicates the actual number of items transferred. For non-stackable, the value is '1' and stackable is the number of charges. A _detail_count +property has been added to both 'Trade' and 'Handin' structs to indicate the number of details recorded..though, not tracked..it could be added. + +== 08/24/2014 == +Uleat: Fix (attempted) for zone crashes related to zone shut-down. This change disables all Mob AI and disables/deletes all Mob timers once Zone::ShutDown() is called. More areas will be addressed as reports come in. +Note: Perl and Lua quests tested to work..please post any aberrant behavior. (I finally set my spell-check to US English...) +Akkadius: Character creation process crash fix and query cleanup +Akkadius: Created `character_lookup` table for applications that mirrors all `character_` table fields minus blob fields for application lookups + - A 2.4GB character_ table will take 7 seconds to query on a SSD versus .1s on the character_lookup table + - This also causes applications like Magelo to burst reads of the entire character table because of the blob fields that come with the reads, as much as 500-600MB/s even if a indexed id filter is provided + - This field is synchronized on player save and has 0.001s DB hit + - When we split out from the blob, ideally this table can be removed + - Required SQL: utils\sql\git\required\2014_08_24_character_lookup.sql + +== 08/23/2014 == +Akkadius: Changed zone process window title format, example: 'crushbone :: clients: 6 inst_id: 1 inst_ver: 0 :: port: 7015' +Akkadius: Most of the following changes are QueryServ related, fully implemented its original functionality to be able to offload + intensive or metric based logging to a remote server process that could exist on another server entirely +Akkadius: Implemented Player Event Logging Types (Go to table `qs_player_events`): + 1 = Player_Log_Quest, + 2 = Player_Log_Zoning, + 3 = Player_Log_Deaths, + 4 = Player_Log_Connect_State, + 5 = Player_Log_Levels, + 6 = Player_Log_Keyring_Addition, + 7 = Player_Log_QGlobal_Update, + 8 = Player_Log_Task_Updates, + 9 = Player_Log_AA_Purchases, + 10 = Player_Log_Trade_Skill_Events, + 11 = Player_Log_Issued_Commands, + 12 = Player_Log_Money_Transactions, + 13 = Player_Log_Alternate_Currency_Transactions, + - All QueryServ logging will be implemented with a front end in EoC 2.0 very soon + - Architecture page: http://wiki.eqemulator.org/p?QueryServ_Architecture +Akkadius: Changed all QS Error related logging to 'QUERYSERV__ERROR' +Akkadius: (Natedog) (Crash Fix) Legacy MySQL bug revert for loading AA's COALESCE( from COALESCE ( +Akkadius: Implemented Perl Quest objects (LUA still needed to be exported): + - quest::qs_send_query("MySQL query") - Will send a raw query to the QueryServ process, useful for custom logging + - quest::qs_player_event(char_id, event_desc); - Will process a quest type event to table `qs_player_events` +Akkadius: Added MySQL Tables + - `qs_player_aa_rate_hourly` + - `qs_player_events` + - Source table structures from: + - utils\sql\git\queryserv\required\08_23_2014_player_events_and_player_aa_rate_hourly + To get the complete QueryServ schema, source from here: + - utils\sql\git\queryserv\required\Complete_QueryServ_Table_Structures.sql +Akkadius: Added rules for each logging type, source rules here with them enabled by default: + - utils\sql\git\queryserv\required\Complete_QueryServ_Rules_Enabled.sql +Akkadius: Spawn related logging cleanup +Akkadius: General code cleanup +Akkadius: More to come for QueryServ + +== 08/22/2014 == +Uleat: Rework of Trade::FinishedTrade() and Trade::ResetTrade() to parse items a little more intelligently. + +Trade window items are now sent to client inventory in this order: + - Bags + - Partial stack movements + - All remaining items + +If any of these procedures cause any problems, please post them immediately. + +== 08/20/2014 == +Uleat: Rework of Trade::AddEntity() - function used to move items into the trade window. Now accepts argument for 'stack_size' and updates client properly. +Note: I tested trade with Titanium:{SoF,SoD,UF,RoF} in both directions and no client generated an OP_MoveItem event for attempting to place a stackable +onto a partial stack already in the trade window. The only way to achieve stacking is to click on the receiving client. If there is a partial stack remaining +on the cursor after the OP_MoveItem event, and there is room available, the client will generate subsequent events to move the remaining count. + +== 08/19/2014 == +Akkadius: Implemented a Stop_Return feature (Accidental item handin prevention) that will be symmetrically used with plugin::return_items that I am currently running live testing on EZ before releasing to EQEmu. This does not hurt to have this in the source. +Akkadius: Fixed crash where 'attacker' validation is not being checked +Akkadius: Removed petition console spam that does not follow traditional logging and is useless +Akkadius: Made fix with SympatheticProcChances where it was checking for TempItem->Focus.Effect instead of TempItemAug->Focus.Effect + +== 08/18/2014 == +Uleat: Fix for https://github.com/EQEmu/Server/issues/127 -- also activated a remarked action in doors.cpp to eliminate a memory leak. + == 08/16/2014 == KLS: (addmoreice) Trying out some unstable DB changes. Do backup your database before trying them as master will be considered unstable for a few days at least. Uleat (Noudness): Fixed a floating-point comparison error that led to the notorious 'client bounce' (this is not related to the diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index d89976b88..e9b127aad 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -19,11 +19,11 @@ #include #include "../../common/debug.h" #include "../../common/shareddb.h" -#include "../../common/EQEmuConfig.h" +#include "../../common/eqemu_config.h" #include "../../common/platform.h" #include "../../common/crash.h" #include "../../common/rulesys.h" -#include "../../common/StringUtil.h" +#include "../../common/string_util.h" void ExportSpells(SharedDatabase *db); void ExportSkillCaps(SharedDatabase *db); @@ -32,7 +32,7 @@ void ExportBaseData(SharedDatabase *db); int main(int argc, char **argv) { RegisterExecutablePlatform(ExePlatformClientExport); set_exception_handler(); - + LogFile->write(EQEMuLog::Status, "Client Files Export Utility"); if(!EQEmuConfig::LoadConfig()) { LogFile->write(EQEMuLog::Error, "Unable to load configuration file."); @@ -52,11 +52,11 @@ int main(int argc, char **argv) { "database connection"); return 1; } - + ExportSpells(&database); ExportSkillCaps(&database); ExportBaseData(&database); - + return 0; } @@ -69,72 +69,69 @@ void ExportSpells(SharedDatabase *db) { return; } - char errbuf[MYSQL_ERRMSG_SIZE]; - const char *query = "SELECT * FROM spells_new ORDER BY id"; - MYSQL_RES *result; - MYSQL_ROW row; - if(db->RunQuery(query, strlen(query), errbuf, &result)) { - while(row = mysql_fetch_row(result)) { + const std::string query = "SELECT * FROM spells_new ORDER BY id"; + auto results = db->QueryDatabase(query); + + if(results.Success()) { + for (auto row = results.begin(); row != results.end(); ++row) { std::string line; - unsigned int fields = mysql_num_fields(result); + unsigned int fields = results.ColumnCount(); for(unsigned int i = 0; i < fields; ++i) { if(i != 0) { line.push_back('^'); } - - line += row[i]; + + if(row[i] != nullptr) { + line += row[i]; + } } - + fprintf(f, "%s\n", line.c_str()); } - mysql_free_result(result); } else { - LogFile->write(EQEMuLog::Error, "Error in ExportSpells query '%s' %s", query, errbuf); + LogFile->write(EQEMuLog::Error, "Error in ExportSpells query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); } - + fclose(f); } bool SkillUsable(SharedDatabase *db, int skill_id, int class_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - bool res = false; - if(db->RunQuery(query, MakeAnyLenString(&query, "SELECT max(cap) FROM skill_caps WHERE class=%d AND skillID=%d", - class_id, skill_id), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - if(row[0] && atoi(row[0]) > 0) { - res = true; - } - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in skill_usable query '%s' %s", query, errbuf); - } - safe_delete_array(query); - return res; + bool res = false; + + std::string query = StringFormat("SELECT max(cap) FROM skill_caps WHERE class=%d AND skillID=%d", + class_id, skill_id); + auto results = db->QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in skill_usable query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + if(row[0] && atoi(row[0]) > 0) + return true; + + return false; } int GetSkill(SharedDatabase *db, int skill_id, int class_id, int level) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - int res = 0; - if(db->RunQuery(query, MakeAnyLenString(&query, "SELECT cap FROM skill_caps WHERE class=%d AND skillID=%d AND level=%d", - class_id, skill_id, level), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - res = atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in get_skill query '%s' %s", query, errbuf); - } - safe_delete_array(query); - return res; + std::string query = StringFormat("SELECT cap FROM skill_caps WHERE class=%d AND skillID=%d AND level=%d", + class_id, skill_id, level); + auto results = db->QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in get_skill query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } void ExportSkillCaps(SharedDatabase *db) { @@ -175,27 +172,25 @@ void ExportBaseData(SharedDatabase *db) { return; } - char errbuf[MYSQL_ERRMSG_SIZE]; - const char *query = "SELECT * FROM base_data ORDER BY level, class"; - MYSQL_RES *result; - MYSQL_ROW row; - if(db->RunQuery(query, strlen(query), errbuf, &result)) { - while(row = mysql_fetch_row(result)) { + const std::string query = "SELECT * FROM base_data ORDER BY level, class"; + auto results = db->QueryDatabase(query); + if(results.Success()) { + for (auto row = results.begin();row != results.end();++row) { std::string line; - unsigned int fields = mysql_num_fields(result); - for(unsigned int i = 0; i < fields; ++i) { - if(i != 0) { + unsigned int fields = results.ColumnCount(); + for(unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) { + if(rowIndex != 0) line.push_back('^'); - } - line += row[i]; + if(row[rowIndex] != nullptr) { + line += row[rowIndex]; + } } fprintf(f, "%s\n", line.c_str()); } - mysql_free_result(result); } else { - LogFile->write(EQEMuLog::Error, "Error in ExportBaseData query '%s' %s", query, errbuf); + LogFile->write(EQEMuLog::Error, "Error in ExportBaseData query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); } fclose(f); diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 0bbbaa3fd..9683e3bfe 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -18,11 +18,11 @@ #include "../../common/debug.h" #include "../../common/shareddb.h" -#include "../../common/EQEmuConfig.h" +#include "../../common/eqemu_config.h" #include "../../common/platform.h" #include "../../common/crash.h" #include "../../common/rulesys.h" -#include "../../common/StringUtil.h" +#include "../../common/string_util.h" void ImportSpells(SharedDatabase *db); void ImportSkillCaps(SharedDatabase *db); @@ -31,7 +31,7 @@ void ImportBaseData(SharedDatabase *db); int main(int argc, char **argv) { RegisterExecutablePlatform(ExePlatformClientImport); set_exception_handler(); - + LogFile->write(EQEMuLog::Status, "Client Files Import Utility"); if(!EQEmuConfig::LoadConfig()) { LogFile->write(EQEMuLog::Error, "Unable to load configuration file."); @@ -55,26 +55,20 @@ int main(int argc, char **argv) { ImportSpells(&database); ImportSkillCaps(&database); ImportBaseData(&database); - + return 0; } int GetSpellColumns(SharedDatabase *db) { - char errbuf[MYSQL_ERRMSG_SIZE]; - const char *query = "DESCRIBE spells_new"; - MYSQL_RES *result; - MYSQL_ROW row; - int res = 0; - if(db->RunQuery(query, (uint32)strlen(query), errbuf, &result)) { - while(row = mysql_fetch_row(result)) { - ++res; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetSpellColumns query '%s' %s", query, errbuf); - } - return res; + const std::string query = "DESCRIBE spells_new"; + auto results = db->QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetSpellColumns query '%s' %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + return results.RowCount(); } void ImportSpells(SharedDatabase *db) { @@ -85,8 +79,8 @@ void ImportSpells(SharedDatabase *db) { return; } - std::string delete_sql = "DELETE FROM spells_new"; - db->RunQuery(delete_sql.c_str(), (uint32)delete_sql.length()); + std::string query = "DELETE FROM spells_new"; + db->QueryDatabase(query); int columns = GetSpellColumns(db); int spells_imported = 0; @@ -103,8 +97,8 @@ void ImportSpells(SharedDatabase *db) { std::string escaped = ::EscapeString(buffer); auto split = SplitString(escaped, '^'); int line_columns = (int)split.size(); + std::string sql; - if(line_columns >= columns) { sql = "INSERT INTO spells_new VALUES("; for(int i = 0; i < columns; ++i) { @@ -140,7 +134,7 @@ void ImportSpells(SharedDatabase *db) { sql += ");"; } - db->RunQuery(sql.c_str(), (uint32)sql.length()); + db->QueryDatabase(sql); spells_imported++; if(spells_imported % 1000 == 0) { @@ -165,17 +159,17 @@ void ImportSkillCaps(SharedDatabase *db) { } std::string delete_sql = "DELETE FROM skill_caps"; - db->RunQuery(delete_sql.c_str(), (uint32)delete_sql.length()); + db->QueryDatabase(delete_sql); char buffer[2048]; while(fgets(buffer, 2048, f)) { auto split = SplitString(buffer, '^'); - + if(split.size() < 4) { continue; } - + int class_id, skill_id, level, cap; class_id = atoi(split[0].c_str()); skill_id = atoi(split[1].c_str()); @@ -185,7 +179,7 @@ void ImportSkillCaps(SharedDatabase *db) { std::string sql = StringFormat("INSERT INTO skill_caps(class, skillID, level, cap) VALUES(%d, %d, %d, %d)", class_id, skill_id, level, cap); - db->RunQuery(sql.c_str(), (uint32)sql.length()); + db->QueryDatabase(sql); } fclose(f); @@ -201,7 +195,7 @@ void ImportBaseData(SharedDatabase *db) { } std::string delete_sql = "DELETE FROM base_data"; - db->RunQuery(delete_sql.c_str(), (uint32)delete_sql.length()); + db->QueryDatabase(delete_sql); char buffer[2048]; while(fgets(buffer, 2048, f)) { @@ -230,7 +224,7 @@ void ImportBaseData(SharedDatabase *db) { "mana_fac, end_fac) VALUES(%d, %d, %f, %f, %f, %f, %f, %f, %f, %f)", level, class_id, hp, mana, end, unk1, unk2, hp_fac, mana_fac, end_fac); - db->RunQuery(sql.c_str(), (uint32)sql.length()); + db->QueryDatabase(sql); } fclose(f); diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 4270cb106..06a78bfea 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,82 +1,81 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) SET(common_sources - BasePacket.cpp + base_packet.cpp classes.cpp - Condition.cpp + condition.cpp crash.cpp - CRC16.cpp + crc16.cpp crc32.cpp database.cpp - dbasync.cpp dbcore.cpp debug.cpp emu_opcodes.cpp - EmuTCPConnection.cpp - EmuTCPServer.cpp + emu_tcp_connection.cpp + emu_tcp_server.cpp eq_dictionary.cpp - EQDB.cpp - EQDBRes.cpp + eqdb.cpp + eqdb_res.cpp eqemu_exception.cpp - EQEmuConfig.cpp - EQEMuError.cpp - EQPacket.cpp - EQStream.cpp - EQStreamFactory.cpp - EQStreamIdent.cpp - EQStreamProxy.cpp + eqemu_config.cpp + eqemu_error.cpp + eq_packet.cpp + eq_stream.cpp + eq_stream_factory.cpp + eq_stream_ident.cpp + eq_stream_proxy.cpp eqtime.cpp extprofile.cpp faction.cpp guild_base.cpp guilds.cpp ipc_mutex.cpp - Item.cpp + item.cpp logsys.cpp logsys_eqemu.cpp md5.cpp memory_mapped_file.cpp misc.cpp - MiscFunctions.cpp + misc_functions.cpp moremath.cpp - Mutex.cpp - MySQLRequestResult.cpp - MySQLRequestRow.cpp + mutex.cpp + mysql_request_result.cpp + mysql_request_row.cpp opcode_map.cpp opcodemgr.cpp packet_dump.cpp packet_dump_file.cpp packet_functions.cpp - perl_EQDB.cpp - perl_EQDBRes.cpp - ProcLauncher.cpp + perl_eqdb.cpp + perl_eqdb_res.cpp + proc_launcher.cpp ptimer.cpp races.cpp rdtsc.cpp rulesys.cpp serverinfo.cpp shareddb.cpp + skills.cpp spdat.cpp - StringUtil.cpp - StructStrategy.cpp - TCPConnection.cpp - TCPServer.cpp + string_util.cpp + struct_strategy.cpp + tcp_connection.cpp + tcp_server.cpp timeoutmgr.cpp timer.cpp unix.cpp uuid.cpp worldconn.cpp web_interface_utils.cpp - XMLParser.cpp + xml_parser.cpp platform.cpp - patches/Client62.cpp + patches/client62.cpp patches/patches.cpp - patches/SoD.cpp - patches/SoF.cpp - patches/RoF.cpp - #patches/RoF2.cpp - patches/Titanium.cpp - patches/Underfoot.cpp + patches/sod.cpp + patches/sof.cpp + patches/rof.cpp + patches/titanium.cpp + patches/underfoot.cpp SocketLib/Base64.cpp SocketLib/File.cpp SocketLib/HttpdCookies.cpp @@ -96,41 +95,42 @@ SET(common_sources ) SET(common_headers - BasePacket.h + any.h + base_packet.h base_data.h bodytypes.h breakdowns.h classes.h - Condition.h + condition.h crash.h - CRC16.h + crc16.h crc32.h + data_verification.h database.h - dbasync.h dbcore.h debug.h deity.h emu_opcodes.h emu_oplist.h - EmuTCPConnection.h - EmuTCPServer.h + emu_tcp_connection.h + emu_tcp_server.h eq_constants.h eq_dictionary.h eq_packet_structs.h - EQDB.h - EQDBRes.h + eqdb.h + eqdb_res.h eqemu_exception.h - EQEmuConfig.h - EQEmuConfig_elements.h - EQEMuError.h - EQPacket.h - EQStream.h - EQStreamFactory.h - EQStreamIdent.h - EQStreamIntf.h - EQStreamLocator.h - EQStreamProxy.h - EQStreamType.h + eqemu_config.h + eqemu_config_elements.h + eqemu_error.h + eq_packet.h + eq_stream.h + eq_stream_factory.h + eq_stream_ident.h + eq_stream_intf.h + eq_stream_locator.h + eq_stream_proxy.h + eq_stream_type.h eqtime.h errmsg.h extprofile.h @@ -141,7 +141,7 @@ SET(common_headers guild_base.h guilds.h ipc_mutex.h - Item.h + item.h item_fieldlist.h item_struct.h languages.h @@ -153,18 +153,19 @@ SET(common_headers md5.h memory_mapped_file.h misc.h - MiscFunctions.h + misc_functions.h moremath.h - Mutex.h - MySQLRequestResult.h - MySQLRequestRow.h + mutex.h + mysql_request_result.h + mysql_request_row.h op_codes.h opcode_dispatch.h opcodemgr.h packet_dump.h packet_dump_file.h packet_functions.h - ProcLauncher.h + platform.h + proc_launcher.h profiler.h ptimer.h queue.h @@ -178,11 +179,11 @@ SET(common_headers shareddb.h skills.h spdat.h - StringUtil.h - StructStrategy.h - TCPBasicServer.h - TCPConnection.h - TCPServer.h + string_util.h + struct_strategy.h + tcp_basic_server.h + tcp_connection.h + tcp_server.h timeoutmgr.h timer.h types.h @@ -192,8 +193,8 @@ SET(common_headers version.h worldconn.h web_interface_utils.h - XMLParser.h - ZoneNumbers.h + xml_parser.h + zone_numbers.h platform.h patches/Client62.h patches/Client62_constants.h @@ -201,40 +202,35 @@ SET(common_headers patches/Client62_ops.h patches/Client62_structs.h patches/patches.h - patches/SoD.h - patches/SoD_constants.h - patches/SoD_itemfields.h - patches/SoD_ops.h - patches/SoD_structs.h - patches/SoF.h - patches/SoF_constants.h - patches/SoF_itemfields.h - patches/SoF_opcode_list.h - patches/SoF_ops.h - patches/SoF_structs.h - patches/SSDeclare.h - patches/SSDefine.h - patches/SSRegister.h - patches/RoF.h - patches/RoF_constants.h - patches/RoF_itemfields.h - patches/RoF_ops.h - patches/RoF_structs.h - #patches/RoF2.h - #patches/RoF2_constants.h - #patches/RoF2_itemfields.h - #patches/RoF2_ops.h - #patches/RoF2_structs.h - patches/Titanium.h - patches/Titanium_constants.h - patches/Titanium_itemfields.h - patches/Titanium_ops.h - patches/Titanium_structs.h - patches/Underfoot.h - patches/Underfoot_constants.h - patches/Underfoot_itemfields.h - patches/Underfoot_ops.h - patches/Underfoot_structs.h + patches/sod.h + patches/sod_constants.h + patches/sod_itemfields.h + patches/sod_ops.h + patches/sod_structs.h + patches/sof.h + patches/sof_constants.h + patches/sof_itemfields.h + patches/sof_opcode_list.h + patches/sof_ops.h + patches/sof_structs.h + patches/ss_declare.h + patches/ss_define.h + patches/ss_register.h + patches/rof.h + patches/rof_constants.h + patches/rof_itemfields.h + patches/rof_ops.h + patches/rof_structs.h + patches/titanium.h + patches/titanium_constants.h + patches/titanium_itemfields.h + patches/titanium_ops.h + patches/titanium_structs.h + patches/underfoot.h + patches/underfoot_constants.h + patches/underfoot_itemfields.h + patches/underfoot_ops.h + patches/underfoot_structs.h SocketLib/Base64.h SocketLib/File.h SocketLib/HttpdCookies.h @@ -253,54 +249,48 @@ SET(common_headers ) SOURCE_GROUP(Patches FILES - patches/Client62.h - patches/Client62_itemfields.h - patches/Client62_ops.h - patches/Client62_constants.h - patches/Client62_structs.h + patches/client62.h + patches/client62_itemfields.h + patches/client62_ops.h + patches/client62_constants.h + patches/client62_structs.h patches/patches.h - patches/SoD.h - patches/SoD_itemfields.h - patches/SoD_ops.h - patches/SoD_constants.h - patches/SoD_structs.h - patches/SoF.h - patches/SoF_itemfields.h - patches/SoF_opcode_list.h - patches/SoF_ops.h - patches/SoF_constants.h - patches/SoF_structs.h - patches/SSDeclare.h - patches/SSDefine.h - patches/SSRegister.h - patches/RoF.h - patches/RoF_itemfields.h - patches/RoF_ops.h - patches/RoF_constants.h - patches/RoF_structs.h - #patches/RoF2.h - #patches/RoF2_itemfields.h - #patches/RoF2_ops.h - #patches/RoF2_constants.h - #patches/RoF2_structs.h - patches/Titanium.h - patches/Titanium_itemfields.h - patches/Titanium_ops.h - patches/Titanium_constants.h - patches/Titanium_structs.h - patches/Underfoot.h - patches/Underfoot_itemfields.h - patches/Underfoot_ops.h - patches/Underfoot_constants.h - patches/Underfoot_structs.h - patches/Client62.cpp + patches/sod.h + patches/sod_itemfields.h + patches/sod_ops.h + patches/sod_constants.h + patches/sod_structs.h + patches/sof.h + patches/sof_itemfields.h + patches/sof_opcode_list.h + patches/sof_ops.h + patches/sof_constants.h + patches/sof_structs.h + patches/ss_declare.h + patches/ss_define.h + patches/ss_register.h + patches/rof.h + patches/rof_itemfields.h + patches/rof_ops.h + patches/rof_constants.h + patches/rof_structs.h + patches/titanium.h + patches/titanium_itemfields.h + patches/titanium_ops.h + patches/titanium_constants.h + patches/titanium_structs.h + patches/underfoot.h + patches/underfoot_itemfields.h + patches/underfoot_ops.h + patches/underfoot_constants.h + patches/underfoot_structs.h + patches/client62.cpp patches/patches.cpp - patches/SoD.cpp - patches/SoF.cpp - patches/RoF.cpp - #patches/RoF2.cpp - patches/Titanium.cpp - patches/Underfoot.cpp + patches/sod.cpp + patches/sof.cpp + patches/rof.cpp + patches/titanium.cpp + patches/underfoot.cpp ) SOURCE_GROUP(SocketLib FILES @@ -349,8 +339,9 @@ ADD_LIBRARY(common ${common_sources} ${common_headers}) IF(UNIX) ADD_DEFINITIONS(-fPIC) - #TODO: Add "patches/RoF2.cpp" when it becomes active - SET_SOURCE_FILES_PROPERTIES("patches/SoD.cpp" "patches/SoF.cpp" "patches/RoF.cpp" "patches/Underfoot.cpp" PROPERTIES COMPILE_FLAGS -O0) + SET_SOURCE_FILES_PROPERTIES("SocketLib/Mime.cpp" PROPERTY COMPILE_FLAGS -Wno-unused-result) + SET_SOURCE_FILES_PROPERTIES("patches/sod.cpp" "patches/sof.cpp" "patches/rof.cpp" "patches/underfoot.cpp" PROPERTIES COMPILE_FLAGS -O0) ENDIF(UNIX) + SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) diff --git a/common/EQNetwork.cpp b/common/EQNetwork.cpp deleted file mode 100644 index c4ec019ea..000000000 --- a/common/EQNetwork.cpp +++ /dev/null @@ -1,405 +0,0 @@ -/* 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 -*/ -/* - * EQStream classes, by Quagmire -*/ - -#include "../common/debug.h" - -#include -#include -#ifdef WIN32 - #include -#else - #include - #include - #include - #include - #include - #include - #include - #include - #include "../common/unix.h" - #define SOCKET_ERROR -1 -#endif -#include "EQNetwork.h" -#include "EQStream.h" -#include "../common/packet_dump.h" -#include "../common/packet_dump_file.h" -#include "../common/packet_functions.h" -#include "../common/MiscFunctions.h" -#include "../common/crc32.h" -#include "../common/eq_packet_structs.h" - -#define EQN_DEBUG 0 -#define EQN_DEBUG_Error 0 -#define EQN_DEBUG_Packet 0 -#define EQN_DEBUG_Fragment 0 -#define EQN_DEBUG_ACK 0 -#define EQN_DEBUG_Unknown 0 -#define EQN_DEBUG_NewStream 0 -#define LOG_PACKETS 0 -#define LOG_RAW_PACKETS_OUT 0 -#define LOG_RAW_PACKETS_IN 0 -//#define PRIORITYTEST - -template // LO_BYTE -type LO_BYTE (type a) {return (a&=0xff);} -template // HI_BYTE -type HI_BYTE (type a) {return (a&=0xff00);} -template // LO_WORD -type LO_WORD (type a) {return (a&=0xffff);} -template // HI_WORD -type HI_WORD (type a) {return (a&=0xffff0000);} -template // HI_LOSWAPshort -type HI_LOSWAPshort (type a) {return (LO_BYTE(a)<<8) | (HI_BYTE(a)>>8);} -template // HI_LOSWAPlong -type HI_LOSWAPlong (type x) {return (LO_WORD(a)<<16) | (HIWORD(a)>>16);} - -EQStreamServer::EQStreamServer(uint16 iPort) { - RunLoop = false; - pPort = iPort; - pOpen = false; -#ifdef WIN32 - WORD version = MAKEWORD (1,1); - WSADATA wsadata; - WSAStartup (version, &wsadata); -#endif - sock = 0; -} - -EQStreamServer::~EQStreamServer() { - Close(); - RunLoop = false; - MLoopRunning.lock(); - MLoopRunning.unlock(); -#ifdef WIN32 - WSACleanup(); -#endif - connection_list.clear(); - while (!NewQueue.empty()) - NewQueue.pop(); // they're deleted with the list, clear this queue so it doesnt try to delete them again -} - -bool EQStreamServer::Open(uint16 iPort) { - LockMutex lock(&MOpen); - if (iPort && pPort != iPort) { - if (pOpen) - return false; - pPort = iPort; - } - if (!RunLoop) { - RunLoop = true; -#ifdef WIN32 - _beginthread(EQStreamServerLoop, 0, this); -#else - pthread_t thread; - pthread_create(&thread, NULL, &EQStreamServerLoop, this); -#endif - } - if (pOpen) { - return true; - } - else { - struct sockaddr_in address; -// int reuse_addr = 1; - int bufsize = 64 * 1024; // 64kbyte send/recieve buffers, up from default of 8k -#ifdef WIN32 - unsigned long nonblocking = 1; -#endif - - /* Setup internet address information. - This is used with the bind() call */ - memset((char *) &address, 0, sizeof(address)); - address.sin_family = AF_INET; - address.sin_port = htons(pPort); - address.sin_addr.s_addr = htonl(INADDR_ANY); - - /* Setting up UDP port for new clients */ - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock < 0) { - return false; - } - -//#ifdef WIN32 -// setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &reuse_addr, sizeof(reuse_addr)); - setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*) &bufsize, sizeof(bufsize)); - setsockopt(sock, SOL_SOCKET, SO_SNDBUF, (char*) &bufsize, sizeof(bufsize)); -//#else -// setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuse_addr, sizeof(reuse_addr)); -// setsockopt(sock, SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)); -// setsockopt(sock, SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); -//#endif - - if (bind(sock, (struct sockaddr *) &address, sizeof(address)) < 0) { -#ifdef WIN32 - closesocket(sock); -#else - close(sock); -#endif - return false; - } - -#ifdef WIN32 - ioctlsocket (sock, FIONBIO, &nonblocking); -#else - fcntl(sock, F_SETFL, O_NONBLOCK); -#endif - pOpen = true; - return true; - } -} - -void EQStreamServer::Close() { - SetOpen(false); - if (sock) { -#ifdef WIN32 - closesocket(sock); -#else - close(sock); -#endif - } - sock = 0; -} - -bool EQStreamServer::IsOpen() { - MOpen.lock(); - bool ret = pOpen; - MOpen.unlock(); - return ret; -} - -void EQStreamServer::SetOpen(bool iOpen) { - MOpen.lock(); - pOpen = iOpen; - MOpen.unlock(); -} - -void EQStreamServer::Process() { - _CP(EQStreamServer_Process); - if (!IsOpen()) { - if (sock) { -#ifdef WIN32 - closesocket(sock); -#else - close(sock); -#endif - sock = 0; - } - return; - } - - uchar buffer[1518]; - - int status; - struct sockaddr_in from; - unsigned int fromlen; - - from.sin_family = AF_INET; - fromlen = sizeof(from); - - while (1) { -#ifdef WIN32 - status = recvfrom(sock, (char *) buffer, sizeof(buffer), 0,(struct sockaddr*) &from, (int *) &fromlen); -#else - status = recvfrom(sock, buffer, sizeof(buffer), 0,(struct sockaddr*) &from, &fromlen); -#endif - if (status >= 1) { - cout << "Got data from recvfrom" << endl; - RecvData(buffer, status, from.sin_addr.s_addr, from.sin_port); - } - else { - break; - } - } - - std::map ::iterator connection; - for (connection = connection_list.begin(); connection != connection_list.end();) { - if (!connection->second) { - connection = connection_list.erase(connection); - continue; - } - EQStream* eqs_data = connection->second; - if (eqs_data->IsFree() && (!eqs_data->CheckNetActive())) { - safe_delete(eqs_data); - connection = connection_list.erase(connection); - } else if (!eqs_data->RunLoop) { - eqs_data->Process(sock); - ++connection; - } - } -} - -void EQStreamServer::RecvData(uchar* data, uint32 size, uint32 irIP, uint16 irPort) { -/* - CHANGE HISTORY - - Version Author Date Comment - 1 Unknown Unknown Initial Revision - 2 Joolz 05-Jan-2003 Optimised - 3 Quagmire 05-Feb-2003 Changed so 2 connection objects wouldnt be created for the same ip/port pair, often happened -*/ - - // Check for invalid data - if (!data || size <= 4) return; - //if (CRC32::Generate(data, size-4) != ntohl(*((uint32*) &data[size-4]))) { -#if EQN_DEBUG_Error >= 1 - //cout << "Incomming Packet failed checksum" << endl; -#endif - //return; - //} - - char temp[25]; - sprintf(temp,"%lu:%u",(unsigned long)irIP,irPort); - cout << "Data from " << temp << endl; - EQStream* tmp = NULL; - std::map ::iterator connection; - if ((connection=connection_list.find(temp))!=connection_list.end()) - tmp=connection->second; - if(tmp != NULL && tmp->GetrPort() == irPort) - { - tmp->RecvData(data, size); - return; - } - else if(tmp != NULL && tmp->GetrPort() != irPort) - { - printf("Conflicting IPs & Ports: IP %i and Port %i is conflicting with IP %i and Port %i\n",irIP,irPort,tmp->GetrIP(),tmp->GetrPort()); - return; - } - - if (data[1]==0x01) { - cout << "New EQStream Connection." << endl; - EQStream* tmp = new EQStream(irIP, irPort); - tmp->RecvData(data, size); - connection_list[temp]=tmp; - if (connection_list.find(temp)==connection_list.end()) { - cerr <<"Could not find new connection we just added!" << endl; - } - MNewQueue.lock(); - NewQueue.push(tmp); - MNewQueue.unlock(); - return; - } -#if EQN_DEBUG >= 4 - struct in_addr in; - in.s_addr = irIP; - cout << "WARNING: Stray packet? " << inet_ntoa(in) << ":" << irPort << endl; -#endif -} - -EQStream* EQStreamServer::NewQueuePop() { - EQStream* ret = 0; - MNewQueue.lock(); - if (!NewQueue.empty()) { - ret = NewQueue.front(); - NewQueue.pop(); - } - MNewQueue.unlock(); - return ret; -} - -#ifdef WIN32 - void EQStreamServerLoop(void* tmp) -#else - void* EQStreamServerLoop(void* tmp) -#endif -{ -#ifdef WIN32 - SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); -#endif - EQStreamServer* eqns = (EQStreamServer*) tmp; - eqns->MLoopRunning.lock(); - while (eqns->RunLoop) { - { - _CP(EQStreamServerLoop); - eqns->Process(); - } - Sleep(1); - } - eqns->MLoopRunning.unlock(); -#ifdef WIN32 - _endthread(); -#else - return 0; -#endif -} - -#ifdef WIN32 - void EQStreamInLoop(void* tmp) -#else - void* EQStreamInLoop(void* tmp) -#endif -{ - EQStream* eqs = (EQStream*) tmp; -#ifdef _DEBUG - if (eqs->ConnectionType != Outgoing) { - ThrowError("EQStreamInLoop: eqs->ConnectionType != Outgoing"); - } -#endif - eqs->MLoopRunning.lock(); - Timer* tmp_timer = new Timer(100); - tmp_timer->Start(); - while (eqs->RunLoop) { - { - _CP(EQStreamInLoop); - if(tmp_timer->Check()) - eqs->DoRecvData(); - } - Sleep(1); - } - safe_delete(tmp_timer); - eqs->MLoopRunning.unlock(); -#ifdef WIN32 - _endthread(); -#else - return 0; -#endif -} - -#ifdef WIN32 - void EQStreamOutLoop(void* tmp) -#else - void* EQStreamOutLoop(void* tmp) -#endif -{ - EQStream* eqs = (EQStream*) tmp; -#ifdef _DEBUG - if (eqs->ConnectionType != Outgoing) { - ThrowError("EQStreamOutLoop: eqs->ConnectionType != Outgoing"); - } -#endif - eqs->MLoopRunning.lock(); - Timer* tmp_timer = new Timer(100); - tmp_timer->Start(); - while (eqs->RunLoop) { - { - _CP(EQStreamOutLoop); - if(tmp_timer->Check()) - eqs->Process(eqs->outsock); - } - Sleep(1); - } - safe_delete(tmp_timer); - eqs->MLoopRunning.unlock(); -#ifdef WIN32 - _endthread(); -#else - return 0; -#endif -} - diff --git a/common/EQNetwork.h b/common/EQNetwork.h deleted file mode 100644 index 1ecb23d6d..000000000 --- a/common/EQNetwork.h +++ /dev/null @@ -1,120 +0,0 @@ -/* 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 -*/ -#ifndef EQNETWORK_H -#define EQNETWORK_H - -#include "../common/debug.h" - -//uncomment this to enable the packet profiler. Counts the number -//of each type of packet sent or received on a connection. -#ifdef ZONE -//#define PACKET_PROFILER 1 -#endif - -#include -#include -#include -#include - -#include "../common/types.h" -#include "../common/timer.h" -#include "../common/linked_list.h" -#include "../common/queue.h" -#include "../common/Mutex.h" -#include "../common/packet_functions.h" -#include "EQStream.h" -#ifdef PACKET_PROFILER -#include "../common/rdtsc.h" -#endif - -#define EQNC_TIMEOUT 60000 -#define NAS_TIMER 100 -#define KA_TIMER 400 /* keeps the lag bar constant */ -#define MAX_HEADER_SIZE 39 // Quag: 39 is the max header + opcode + crc32 + unknowns size - -class EQStreamServer; -class EQStream; -class EQStreamPacket; -class EQStreamFragmentGroupList; -class EQStreamFragmentGroup; -typedef EQStreamServer EQNServer; -typedef EQStream EQNConnection; -typedef EQStreamPacket EQNPacket; -typedef EQStreamFragmentGroupList EQNFragmentGroupList; -typedef EQStreamFragmentGroup EQNFragmentGroup; - -#define FLAG_COMPRESSED 0x1000 -#define FLAG_COMBINED 0x2000 -#define FLAG_ENCRYPTED 0x4000 -#define FLAG_IMPLICIT 0x8000 -#define FLAG_ALL 0xF000 -#define StripFlags(x) (x & ~FLAG_ALL) - -// Optimistic compression, used for guessing pre-alloc size on debug output -#define BEST_COMPR_RATIO 300 - -enum eappCompressed { appNormal, appInflated, appDeflated }; - -#ifdef WIN32 - void EQStreamServerLoop(void* tmp); - void EQStreamInLoop(void* tmp); - void EQStreamOutLoop(void* tmp); -#else - void* EQStreamServerLoop(void* tmp); - void* EQStreamInLoop(void* tmp); - void* EQStreamOutLoop(void* tmp); -#endif -class EQStreamServer { -public: - EQStreamServer(uint16 iPort = 0); - virtual ~EQStreamServer(); - - bool Open(uint16 iPort = 0); // opens the port - void Close(); // closes the port - void KillAll(); // kills all clients - inline uint16 GetPort() { return pPort; } - - EQStream* NewQueuePop(); -protected: -#ifdef WIN32 - friend void EQStreamServerLoop(void* tmp); -#else - friend void* EQStreamServerLoop(void* tmp); -#endif - void Process(); - bool IsOpen(); - void SetOpen(bool iOpen); - bool RunLoop; - Mutex MLoopRunning; -private: - void RecvData(uchar* data, uint32 size, uint32 irIP, uint16 irPort); -#ifdef WIN32 - SOCKET sock; -#else - int sock; -#endif - uint16 pPort; - bool pOpen; - Mutex MNewQueue; - Mutex MOpen; - - std::map connection_list; - std::queue NewQueue; -}; - -#endif diff --git a/common/MaxSkill.cpp b/common/MaxSkill.cpp deleted file mode 100644 index d24ffbc91..000000000 --- a/common/MaxSkill.cpp +++ /dev/null @@ -1,1900 +0,0 @@ -#include "../common/races.h" -#include "../common/classes.h" -#include "../zone/skills.h" - -uint8 MaxSkillTable(uint16 skillid, uint16 race, uint16 eqclass, uint16 level); -/* TODO: - Load MaxSkillTable function into ram as a really big matrix: - MaxSkillTable[skillid][race][eqclass][level] - - in preperation for that, dont put anything that wont work in - that table into MaxSkillTable (ie, AA checks, skill values that - depend on other skill values, etc), put it into MaxSkill instead -*/ -uint8 MaxSkill(uint16 skillid, uint16 race, uint16 eqclass, uint16 level) { - uint8 ret = MaxSkillTable(skillid, race, eqclass, level); - return ret; -} - -/* SPECIAL VALUES: - level: - 0 = "skill level at character create" - TODO: catch levels > 65 (ie, npcs) - race: - NPCs will always have their skills maxed, and often ignore class - restrictions, accomplished by the special values below. - EMU_RACE_NPC - EMU_RACE_PET - EMU_RACE_UNKNOWN - return value: - TODO: Find out the specal values for the client for "your class/race doesnt have this skill" and - "must put one point in at GM", etc - -*/ -uint8 MaxSkillTable(uint16 skillid, uint16 race, uint16 eqclass, uint16 level) { - uint16 r_value = 0; - - switch (skillid) { - /////////////// - // Melee Weapon/ Hand to Hand - /////////////// - case _1H_BLUNT: - case _2H_BLUNT: - case PIERCING: - case HAND_TO_HAND: - case _1H_SLASHING: - case _2H_SLASHING:{ - switch (eqclass) { - // Pure melee classes - case WARRIOR: case WARRIORGM: { - r_value = 5 + (level*5); - if ( level < 51 && r_value > 200) - r_value = 200; - if ( level > 51 && r_value > 250 ) - r_value = 250; - switch (skillid) { - case PIERCING: { - if ( r_value > 240 ) - r_value = 240; - break; - } - case HAND_TO_HAND: { - if ( r_value > 100 ) - r_value = 100; - break; - } - default: - break; - } - break; - } - case MONK: case MONKGM: { - r_value = 5 + (level*5); - if ( level < 51 && r_value > 240) - if ( r_value > 240 ) - r_value = 240; - if ( r_value > 252 ) - r_value = 252; - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 225 && level < 51 ) - r_value = 225; - break; - } - case PIERCING: - case _1H_SLASHING: - case _2H_SLASHING:{ - r_value = 0; - break; - } - default: - break; - } - break; - } - case ROGUE: case ROGUEGM: { - r_value = 5 + (level*5); - if ( level > 50 ) { - if ( r_value > 250 ) - r_value = 250; - } - else if ( level < 51 ) { - if ( r_value > 200 && skillid != PIERCING ) - r_value = 200; - switch (skillid) { - case PIERCING: { - if (r_value > 210) - r_value = 210; - break; - } - default: - break; - } - } - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 100 ) - r_value = 100; - break; - } - default: - break; - - } - break; - } - - ////////////////////////////////////////////////////////////// - // Melee Weapon/ Hand to Hand - // Priest classes - ////////////////////////////////////////////////////////////// - case CLERIC: case CLERICGM:{ - r_value = 4 + (level*4); - if ( r_value > 175 ){ - r_value = 175; - } - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 75 ) - r_value = 75; - break; - } - case PIERCING: - case _1H_SLASHING: - case _2H_SLASHING: { - r_value = 0; - break; - } - default: - break; - } - break; - } - case DRUID: case DRUIDGM:{ - r_value = 4 + (level*4); - if ( r_value > 175 ){ - r_value = 175; - } - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 75 ) - r_value = 75; - } - case PIERCING: - case _2H_SLASHING:{ - r_value = 0; - break; - } - default: - break; - } - break; - } - case SHAMAN: case SHAMANGM:{ - r_value = 4 + (level*4); - if ( r_value > 200 ){ - r_value = 200; - } - switch (skillid) { - case HAND_TO_HAND: { - if ( r_value > 75 ) - r_value = 75; - } - case _1H_SLASHING: - case _2H_SLASHING:{ - r_value = 0; - break; - } - default: - break; - } - break; - } - - /////////////////////////////////////////////////////////// - // Melee Weapon/ Hand to Hand - // Hybrids - ////////////////////////////////////////////////////////// - case RANGER: case RANGERGM:{ - r_value = 5 + (level*5); - if ( level > 50 ) { - if ( r_value > 250 ) - r_value = 250; - - switch (skillid) { - case PIERCING: { - if ( r_value > 240 ) - r_value = 240; - break; - } - default: - break; - } - } - else if ( level < 51 ) { - if ( r_value > 200 ) - r_value = 200; - } - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 100 ) - r_value = 100; - break; - } - default: - break; - } - break; - } - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - r_value = 5 + (level*5); - if ( level > 50 ){ - if ( r_value > 225 ) - r_value = 225; - } - if ( level < 51 ){ - if ( r_value > 200 ) - r_value = 200; - } - - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 100 ) - r_value = 100; - break; - } - default: - break; - } - break; - } - case BARD: case BARDGM: { - r_value = 5 + (level*5); - if ( level > 51 && r_value > 225 ) - r_value = 225; - if ( level < 51 && r_value > 200 ) - r_value = 200; - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 100 ) - r_value = 100; - break; - } - case _2H_BLUNT: - case _2H_SLASHING:{ - r_value = 0; - - } - default: - break; - } - break; - } - - case BEASTLORD: case BEASTLORDGM:{ - r_value = 4 + (level*4); - if ( level > 51 ){ - if ( r_value > 225 && skillid != HAND_TO_HAND ) - r_value = 225; - } - if ( r_value > 250 ) - r_value = 250; - if ( level < 51 && r_value > 200 ) - r_value = 200; - - switch (skillid) { - case HAND_TO_HAND:{ - r_value = 5 + (level*5); - if ( level < 51 ) - r_value = 200; - - if ( r_value > 250 ) - r_value = 250; - break; - } - case _1H_SLASHING: - case _2H_SLASHING:{ - r_value = 0; - break; - } - default: - break; - } - break; - } - - // Melee Weapon/ Hand to Hand - // Pure casters - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM:{ - r_value = 3 + (level*3); - if ( r_value > 110 ) - r_value = 110; - switch (skillid) { - case HAND_TO_HAND:{ - if ( r_value > 75 ) - r_value = 75; - break; - } - case _1H_SLASHING: - case _2H_SLASHING:{ - r_value = 0; - break; - } - default: - break; - } - } - default: { - r_value = 0; - break; - } - }// end switch(eqclass) - break; - } // end case weapon skills - - -///////////////////////////////////////////////////////////// -// Combat non weapon -///////////////////////////////////////////////////////////// - -// Attack - case OFFENSE: { - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM: - case ROGUE: case ROGUEGM:{ - // 210 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 252) - r_value = 252; - break; - } - case MONK: case MONKGM:{ - // 230 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 230) - r_value = 230; - } - if (r_value > 252) - r_value = 252; - break; - } - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM:{ - // 200 200 4*level+4 - r_value = ((level*4) + 4); - if (r_value > 200) - r_value = 200; - break; - } - // Hybrid - case BEASTLORD: case BEASTLORDGM:{ - // 200 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 252) - r_value = 252; - break; - } - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM: - case BARD: case BARDGM:{ - // 200 225 5*level+5 - - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 225) - r_value = 225; - break; - } - case RANGER: case RANGERGM:{ - // 210 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 252) - r_value = 252; - break; - } - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM:{ - // 140 140 level*4 - r_value = (level*4); - if (r_value > 140) - r_value = 140; - break; - } - default: { - r_value = 0; - break; - } - } // end switch (eqclass) - break; - } // end case OFFENSE - case THROWING: { - switch (eqclass) { - // Melee - case ROGUE: case ROGUEGM:{ - // 220 250 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 220) - r_value = 220; - } - if (r_value > 250) - r_value = 250; - break; - } - case WARRIOR: case WARRIORGM: - case MONK: case MONKGM:{ - // 113 200 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 113) - r_value = 113; - } - if (r_value > 200) - r_value = 200; - break; - } - // Hybrid - case BEASTLORD: case BEASTLORDGM: - case BARD: case BARDGM: - case RANGER: case RANGERGM:{ - // 113 - r_value = ((level*5) + 5); - if ( r_value > 113 ) - r_value = 113; - break; - } - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM:{ - // 75 - r_value = ((level*3) + 3); - if ( r_value > 75 ) - r_value = 75; - break; - } - // No skill classes - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM: - default: { - r_value = 0; - break; - } - } // end switch (eqclass) - break; - } // end case THROWING: - case ARCHERY: { - switch (eqclass) { - // Melee - case ROGUE: case ROGUEGM: - case WARRIOR: case WARRIORGM:{ - // 200 240 - r_value = ((level*5) + 5); - if ( level < 51 && r_value > 200) - r_value = 200; - if (r_value > 240) - r_value = 240; - break; - } - // Hybrid - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 75 75 - r_value = ((level*5) + 5); - if ( r_value > 75 ) - r_value = 75; - break; - } - case RANGER: case RANGERGM:{ - // 240 240 - r_value = ((level*5) + 5); - if ( r_value > 240 ) - r_value = 240; - break; - } - // Pure - // No skill classes - // Melee - case MONK: case MONKGM: - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case BEASTLORD: case BEASTLORDGM: - case BARD: case BARDGM: - default: { - r_value = 0; - break; - } - } // end switch (eqclass) - break; - } // end case ARCHERY: - case DOUBLE_ATTACK: { - switch (eqclass) { - // Melee - case ROGUE: case ROGUEGM:{ - // 16 200 240 - r_value = ((level*5) + 5); - if ( level < 16 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 240) - r_value = 240; - break; - } - case WARRIOR: case WARRIORGM:{ - // 15 205 245 - r_value = ((level*5) + 5); - if ( level < 15 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 245) - r_value = 245; - break; - } - case MONK: case MONKGM:{ - // 15 210 250 - r_value = ((level*5) + 5); - if ( level < 15 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 250) - r_value = 250; - break; - } - // Hybrid - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 20 200 235 - r_value = ((level*5) + 5); - if ( level < 20 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 235) - r_value = 235; - break; - } - case RANGER: case RANGERGM:{ - // 20 200 245 - r_value = ((level*5) + 5); - if ( level < 20 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 245) - r_value = 245; - break; - } - // Pure - // No skill classes - // Melee - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case BEASTLORD: case BEASTLORDGM: - case BARD: case BARDGM: - default: { - r_value = 0; - - break; - } - } // end switch (eqclass) - break; - } // end case DOUBLE_ATTACK: - case DUEL_WIELD: { - switch (eqclass) { - // Melee - case MONK: case MONKGM:{ - // 1 252 252 - r_value = level*7; // This can't be right can it? - break; - } - case WARRIOR: case WARRIORGM: - case ROGUE: case ROGUEGM: { - // 15 210 245 - r_value = ((level*5) + 5); - if ( level < 15 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 245) - r_value = 245; - break; - } - // Hybrid - case BEASTLORD: case BEASTLORDGM: - // 17 210 245 - case RANGER: case RANGERGM:{ - // 17 210 245 - r_value = ((level*5) + 5); - if ( level < 17 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 245) - r_value = 245; - break; - } - case BARD: case BARDGM:{ - // 17 210 210 - r_value = ((level*5) + 5); - if ( level < 17 ) - r_value = 0; - if (r_value > 210) - r_value = 210; - break; - } - // No skill classes - // Melee - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM: - default: { - r_value = 0; - break; - } - }// end Class switch - break; - } // end case DUEL_WIELD: - case KICK: { - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM:{ - // 1 149 210 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 149) - r_value = 149; - } - if (r_value > 210) - r_value = 210; - break; - } - case MONK: case MONKGM:{ - // 1 200 250 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 250) - r_value = 250; - break; - } - // Hybrid - case RANGER: case RANGERGM:{ - // 5 149 205 - r_value = ((level*5) + 5); - if ( level < 5 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 149) - r_value = 149; - } - if (r_value > 205) - r_value = 205; - break; - } - case BEASTLORD: case BEASTLORDGM:{ - // 5 180 230 - r_value = ((level*5) + 5); - if ( level < 5 ) - r_value = 0; - if ( level < 51 ) { - if (r_value > 180) - r_value = 180; - } - if (r_value > 230) - r_value = 230; - break; - } - // Pure - // No skill classes - case ROGUE: case ROGUEGM: - // Melee - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM: - case BARD: case BARDGM: - default: { - r_value = 0; - break; - } - } // end switch(eqclass) - break; - } // end case KICK: - /////////// - // FIXME Where is slam? - // Quagmire: Slam = bash w/ race check - case BASH:{ - r_value = ((level*5)+5); - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM:{ - // 6 220 240 - if (level < 6) - r_value = 0; - if (level < 51 && r_value > 220) - r_value = 220; - if (r_value > 240) - r_value = 240; - break; - } - // Priest - case CLERIC: case CLERICGM:{ - // 25 180 200 - if (level < 25) - r_value = 0; - if (level < 51 && r_value > 180) - r_value = 180; - if (r_value > 200) - r_value = 200; - break; - } - // Hybrid - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 6 175 200 - if (level < 6) - r_value = 0; - if (level < 51 && r_value > 175) - r_value = 175; - if (r_value > 200) - r_value = 200; - break; - } - // Pure - // No skill classes - // Melee - case MONK: case MONKGM: - case ROGUE: case ROGUEGM: - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case BEASTLORD: case BEASTLORDGM: - case RANGER: case RANGERGM: - case BARD: case BARDGM:{ - switch (race) { - case BARBARIAN: - case TROLL: - case OGRE:{ - r_value = 50; - break; - } - default: { - break; - } - } // end switch (race) - r_value = 0; - break; - } - } - break; - } // end case BASH: - ///////////////////////////////////// - ///////////////////////////////////// - // Defensive skills - case DEFENSE:{ - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM:{ - // 210 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 252) - r_value = 252; - break; - } - case ROGUE: case ROGUEGM:{ - // 200 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 252) - r_value = 252; - break; - } - case MONK: case MONKGM:{ - // 230 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 230) - r_value = 230; - } - if (r_value > 252) - r_value = 252; - break; - } - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM:{ - // 200 200 4*level+4 - r_value = ((level*4) + 4); - if (r_value > 200) - r_value = 200; - break; - } - // Hybrid - case BEASTLORD: case BEASTLORDGM:{ - // 210 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 252) - r_value = 252; - break; - } - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 210 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 210) - r_value = 210; - } - if (r_value > 252) - r_value = 252; - break; - } - case BARD: case BARDGM:{ - // 200 252 5*level+5 - r_value = ((level*5) + 5); - if ( level < 51 ) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 252) - r_value = 252; - break; - } - case RANGER: case RANGERGM:{ - // 200 200 5*level+5 - r_value = ((level*5) + 5); - if (r_value > 200) - r_value = 200; - break; - } - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM:{ - // 145 145 level*4 - r_value = (level*4); - if (r_value > 140) - r_value = 140; - break; - } - default: { - break; - } - } // end switch(eqclass) - break; - } // end case DEFENSE: - case PARRY:{ - switch (eqclass) { - // Melee - case ROGUE: case ROGUEGM:{ - // 12 200 230 - r_value = ((level*5) + 5); - if ( level < 12 ) - r_value = 0; - if (r_value > 200 && level < 51 ) - r_value = 200; - if (r_value > 230) - r_value = 230; - break; - } - case WARRIOR: case WARRIORGM:{ - // 10 200 230 - r_value = ((level*5) + 5); - if ( level < 10 ) - r_value = 0; - if (r_value > 200 && level < 51 ) - r_value = 200; - if (r_value > 230) - r_value = 230; - break; - } - // Hybrid - case BARD: case BARDGM:{ - // 53 0 75 - r_value = ((level*5) + 5); - if ( level < 53 ) - r_value = 0; - if (r_value > 75) - r_value = 75; - break; - } - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 17 175 205 - r_value = ((level*5) + 5); - if ( level < 17 ) - r_value = 0; - if (r_value > 175 && level < 51 ) - r_value = 175; - if (r_value > 205) - r_value = 205; - break; - } - case RANGER: case RANGERGM:{ - // 18 185 220 - r_value = ((level*5) + 5); - if ( level < 18 ) - r_value = 0; - if (r_value > 185 && level < 51 ) - r_value = 185; - if (r_value > 220) - r_value = 220; - break; - } - // Pure - // No skill classes - // Melee - case MONK: case MONKGM: - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case BEASTLORD: case BEASTLORDGM: - default: { - r_value = 0; - break; - } - } // end switch (eqclass) - break; - } // end case PARRY: - case RIPOSTE:{ - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM:{ - // 25 200 225 - r_value = ((level*5) + 5); - if ( level < 25 ) - r_value = 0; - if (r_value > 200 && level < 51 ) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - case ROGUE: case ROGUEGM:{ - // 30 200 225 - r_value = ((level*5) + 5); - if ( level < 30 ) - r_value = 0; - if (r_value > 200 && level < 51 ) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - case MONK: case MONKGM:{ - // 35 200 225 - r_value = ((level*5) + 5); - if ( level < 35 ) - r_value = 0; - if (r_value > 200 && level < 51 ) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - // Hybrid - case BEASTLORD: case BEASTLORDGM:{ - // 40 150 185 - r_value = ((level*5) + 5); - if ( level < 40 ) - r_value = 0; - if (r_value > 150 && level < 51 ) - r_value = 150; - if (r_value > 185) - r_value = 185; - break; - } - case BARD: case BARDGM:{ - // 58 75 75 - r_value = ((level*5) + 5); - if ( level < 58 ) - r_value = 0; - if (r_value > 75) - r_value = 75; - break; - } - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 30 175 200 - r_value = ((level*5) + 5); - if ( level < 30 ) - r_value = 0; - if (r_value > 175 && level < 51 ) - r_value = 175; - if (r_value > 200) - r_value = 200; - break; - } - case RANGER: case RANGERGM:{ - // 35 150 150 - r_value = ((level*5) + 5); - if ( level < 35 ) - r_value = 0; - if (r_value > 150) - r_value = 150; - break; - } - // Pure - // No skill classes - // Melee - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - default: { - r_value = 0; - break; - } - } // end switch (eqclass) - break; - } // end case RIPOSTE: - case DODGE:{ - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM:{ - // 6 140 175 - r_value = ((level*5) + 5); - if ( level < 6 ) - r_value = 0; - if (r_value > 140 && level < 51 ) - r_value = 140; - if (r_value > 175) - r_value = 175; - break; - } - case ROGUE: case ROGUEGM:{ - // 4 150 210 - r_value = ((level*5) + 5); - if ( level < 4 ) - r_value = 0; - if (r_value > 150 && level < 51 ) - r_value = 150; - if (r_value > 210) - r_value = 210; - break; - } - case MONK: case MONKGM:{ - // 1 200 230 - r_value = ((level*5) + 5); - if (r_value > 200) - r_value = 200; - if (r_value > 230) - r_value = 230; - break; - } - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM:{ - // 15 75 75 4*level+4 - r_value = ((level*4) + 4); - if ( level < 15 ) - r_value = 0; - if (r_value > 75) - r_value = 75; - break; - } - // Hybrid - case BEASTLORD: case BEASTLORDGM: - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM: - case BARD: case BARDGM:{ - // 10 125 155 5*level+5 - r_value = ((level*5) + 5); - if ( level < 10 ) - r_value = 0; - if (r_value > 125 && level < 51 ) - r_value = 125; - if (r_value > 155) - r_value = 155; - break; - } - - case RANGER: case RANGERGM:{ - // 8 137 170 5*level+5 - r_value = ((level*5) + 5); - if ( level < 8 ) - r_value = 0; - if (r_value > 137 && level < 51 ) - r_value = 137; - if (r_value > 170) - r_value = 170; - break; - } - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM:{ - // 22 75 75 3*level+3 - r_value = ((level*3) + 3); - if ( level < 22 ) - r_value = 0; - if (r_value > 75) - r_value = 75; - break; - } - // No skill classes - // Melee - // Priest - // Pure - // Hybrid - default: { - r_value = 0; - break; - } - } // end switch (eqclass) - break; - } // end case DODGE: - // Other - case TAUNT:{ - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM:{ - // 1 200 200 - r_value = ((level*5) + 5); - if (r_value > 200) - r_value = 200; - break; - } - // Priest - // Hybrid - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 1 180 180 - r_value = ((level*5) + 5); - if (r_value > 180) - r_value = 180; - break; - } - case RANGER: case RANGERGM:{ - // 1 150 150 - r_value = ((level*5) + 5); - if (r_value > 150) - r_value = 150; - break; - } - // Pure - // No skill classes - // Melee - case ROGUE: case ROGUEGM: - case MONK: case MONKGM: - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case BEASTLORD: case BEASTLORDGM: - case BARD: case BARDGM: - default: { - r_value = 0; - break; - } - } // end swtich (eqclass) - break; - } // end case TAUNT: - - case DISARM:{ - switch (eqclass) { - // Melee - case WARRIOR: case WARRIORGM:{ - // 35 200 200 - r_value = ((level*5) + 5); - if (level < 35) - r_value = 0; - if (r_value > 200) - r_value = 200; - break; - } - case ROGUE: case ROGUEGM: - case MONK: case MONKGM:{ - // 27 200 200 - r_value = ((level*5) + 5); - if (level < 27) - r_value = 0; - if (r_value > 200) - r_value = 200; - break; - } - // Priest - // Hybrid - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 40 70 70 - r_value = ((level*5) + 5); - if (level < 40) - r_value = 0; - if (r_value > 70) - r_value = 70; - break; - } - case RANGER: case RANGERGM:{ - // 35 55 55 - r_value = ((level*5) + 5); - if (level < 35) - r_value = 0; - if (r_value > 55) - r_value = 55; - break; - } - // Pure - // No skill classes - // Melee - // Priest - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM: - case CLERIC: case CLERICGM: - // Pure - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM: - case MAGICIAN: case MAGICIANGM: - case ENCHANTER: case ENCHANTERGM: - // Hybrid - case BARD: case BARDGM: - case BEASTLORD: case BEASTLORDGM: - default: { - r_value = 0; - break; - } - } // end switch (eqclass) - break; - } // end case DISARM: - /////////////////////////////////////////// - /////////////////////////////////////////// - // Spell Skills - case MEDITATE: - case ABJURE: - - case ALTERATION: - case CHANNELING: - case CONJURATION: - case DIVINATION: - - case EVOCATION:{ - r_value = ((level*5) + 5); - switch(eqclass){ - // Hybrid - case RANGER: case RANGERGM:{ - // 9 235 235 - // Channel 9 200 215 - // Med 12 185 235 - if (level < 9) - r_value = 0; - if (level < 12 && skillid == MEDITATE) - r_value = 0; - if (r_value > 0 && skillid == CHANNELING) { - if ( level < 51 && r_value > 200) - r_value = 200; - if (r_value > 215) - r_value = 215; - } - if (r_value > 0 && skillid == MEDITATE) { - if ( level < 51 && r_value > 185) - r_value = 185; - if (r_value > 235) - r_value = 235; - } - break; - } - case BEASTLORD: case BEASTLORDGM: - case PALADIN: case PALADINGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM:{ - // 9 235 235 - // Channel 9 200 220 - // Med 12 185 235 - if (level < 9) - r_value = 0; - if (level < 12 && skillid == MEDITATE) - r_value = 0; - if (r_value > 0 && skillid == CHANNELING) { - if ( level < 51 && r_value > 185) - r_value = 185; - if (r_value > 220) - r_value = 220; - } - if (r_value > 0 && skillid == MEDITATE) { - if ( level < 51 && r_value > 185) - r_value = 185; - if (r_value > 235) - r_value = 235; - } - break; - } - // Priest - case CLERIC: case CLERICGM: - case DRUID: case DRUIDGM: - case SHAMAN: case SHAMANGM:{ - // 1 235 235 - // Channel 4 200 220 - // Med 8 235 252 - if (level < 4 && skillid == CHANNELING) - r_value = 0; - if (level < 8 && skillid == MEDITATE) - r_value = 0; - if (r_value > 0 && skillid == CHANNELING) { - if ( level < 51 && r_value > 200) - r_value = 200; - if (r_value > 220) - r_value = 220; - } - if (r_value > 0 && skillid == MEDITATE) { - if ( level < 51 && r_value > 235) - r_value = 235; - if (r_value > 252) - r_value = 252; - } - break; - } - // Int caster - case ENCHANTER: case ENCHANTERGM: - case MAGICIAN: case MAGICIANGM: - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM:{ - // 1 235 235 - // Channel 1 200 220 - // Med 4 235 252 - if (level < 4 && skillid == MEDITATE) - r_value = 0; - if (r_value > 0 && skillid == CHANNELING) { - if ( level < 51 && r_value > 200) - r_value = 200; - if (r_value > 220) - r_value = 220; - } - if (r_value > 0 && skillid == MEDITATE) { - if ( level < 51 && r_value > 235) - r_value = 235; - if (r_value > 252) - r_value = 252; - } - break; - } - case BARD: case BARDGM:{ - r_value = 0; - if (level > 9 && skillid == MEDITATE) - r_value = 1; - break; - } - default: { - // Unknown class - r_value = 0; - break; - } - }// Class Switch - break; - } // end spell skills - - case SPECIALIZE_ABJURE: - case SPECIALIZE_ALTERATION: - case SPECIALIZE_CONJURATION: - case SPECIALIZE_DIVINATION: - case SPECIALIZE_EVOCATION: - case RESEARCH:{ - r_value = ((level*5) + 5); - switch(eqclass){ - // Int caster - case ENCHANTER: case ENCHANTERGM: - case MAGICIAN: case MAGICIANGM: - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM:{ - // Res 16 200 200 - if (level < 16 && skillid == RESEARCH) - r_value = 0; - if (r_value > 0 && skillid == RESEARCH) { - if (r_value > 200) - r_value = 200; - } - if (r_value > 235) - r_value = 235; - // FIXME Only let one SPEC go above what ever limit theres supposed to be - break; - } - default:{ - r_value = 0; - break; - } - }// Class Switch - break; - } // end specilize & research skills - - case BRASS_INSTRUMENTS: - case SINGING: - case STRINGED_INSTRUMENTS: - case WIND_INSTRUMENTS: - case PERCUSSION_INSTRUMENTS:{ - switch(eqclass){ - case BARD: case BARDGM:{ - r_value = ((level*5) + 5); - if (level < 5 && skillid == PERCUSSION_INSTRUMENTS){ - r_value = 0; - } - if (level < 8 && skillid == STRINGED_INSTRUMENTS){ - r_value = 0; - } - if (level < 11 && skillid == BRASS_INSTRUMENTS){ - r_value = 0; - } - if (level < 14 && skillid == WIND_INSTRUMENTS){ - r_value = 0; - } - if (r_value > 235) - r_value = 235; - break; - } - default: { - r_value = 0; - } - break; - }// Class Switch - break; - } // bard song skills - /////////////////////////////////////////// - /////////////////////////////////////////// - // Class skills - // Rogue - case APPLY_POISON: - case MAKE_POISON: - case PICK_POCKETS: - case BACKSTAB:{ - switch (eqclass) { - // Melee - case ROGUE: case ROGUEGM: { - r_value = ((level*5) + 5); - switch (skillid){ - case APPLY_POISON:{ - // 18 200 200 - if (level < 18) - r_value = 0; - if (r_value > 200) - r_value = 200; - break; - } - case MAKE_POISON:{ - // 20 200 250 - if (level < 20) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 250) - r_value = 250; - break; - } - case PICK_POCKETS:{ - // 7 200 210 - if (level < 7) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 210) - r_value = 210; - break; - } - case BACKSTAB:{ - // 10 200 225 - if (level < 10) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - default: { - r_value = 0; - break; - } - } // end switch (skillid) - break; - } // end case ROGUE: case ROGUEGM: - default: { - r_value = 0; - break; - } - }// Class Switch - break; - } // end rogue skills - // Monk - case FEIGN_DEATH: - case MEND: - case DRAGON_PUNCH: - case EAGLE_STRIKE: - case FLYING_KICK: - case ROUND_KICK: - case TIGER_CLAW: - case BLOCKSKILL:{ - switch(eqclass){ - case MONK: case MONKGM:{ - r_value = ((level*5) + 5); - switch (skillid){ - case MEND:{ - // 1 200 200 - if (r_value > 200) - r_value = 200; - break; - } - case ROUND_KICK:{ - // 5 200 225 - if (level < 5) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - case TIGER_CLAW:{ - // 10 200 225 - if (level < 10) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - case BLOCKSKILL:{ - // 12 200 230 - if (level < 12) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 230) - r_value = 230; - break; - } - case FEIGN_DEATH:{ - // 17 200 200 - if (level < 17) - r_value = 0; - if (r_value > 200) - r_value = 200; - break; - } - case EAGLE_STRIKE:{ - // 20 200 225 - if (level < 20) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - case DRAGON_PUNCH:{ - // 25 200 225 - if (level < 25) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - case FLYING_KICK:{ - // 30 200 225 - if (level < 30) - r_value = 0; - if (level < 51 && r_value > 200) - r_value = 200; - if (r_value > 225) - r_value = 225; - break; - } - default: { - r_value = 0; - break; - } - } // end switch (skillid) - break; - } // end case MONK: case MONKGM: - default: { - r_value = 0; - break; - } - }// Class Switch - break; - } // end monk skills - // Shaman - case ALCHEMY:{ - switch(eqclass){ - case SHAMAN: case SHAMANGM:{ - // 25 130 180 - r_value = ((level*5) + 5); - if (level < 25) - r_value = 0; - if (level < 51 && r_value > 130) - r_value = 130; - if (r_value > 180) - r_value = 180; - break; - } - default: { - r_value = 0; - break; - } - }// Class Switch - break; - } // end case ALCHEMY: - /////////////////////////////////////////// - ////////////////////////////////////////// - // Shared skill - // Shared Rogue - case HIDE: - case SNEAK:{ - switch(eqclass){ - // True class - case ROGUE: case ROGUEGM:{ - break; - } - // Hybrids - case MONK: case MONKGM: - case RANGER: case RANGERGM: - case SHADOWKNIGHT: case SHADOWKNIGHTGM: - case BARD: case BARDGM:{ - break; - } - default: { - r_value = 0; - break; - } - }// Class Switch - } // end sneak/hide - case SENSE_TRAPS: - case PICK_LOCK: - case DISARM_TRAPS:{ - switch(eqclass){ - // True class - case ROGUE: case ROGUEGM:{ - break; - } - // Hybrids - case BARD: case BARDGM:{ - break; - } - default: { - r_value = 0; - break; - } - }// Class Switch - break; - } // end case SENSE_TRAPS/PICK_LOCK/DISARM_TRAPS - case SAFE_FALL: - case INTIMIDATION:{ - switch(eqclass){ - // Melee - case MONK: case MONKGM: - case ROGUE: case ROGUEGM:{ - break; - } - default: { - r_value = 0; - break; - } - }// Class Switch - break; - } // end SAFE_FALL/INTIMIDATION - // Druid/Ranger/Bard - case FORAGE:{ - switch(eqclass) { - case DRUID: case DRUIDGM: - case RANGER: case RANGERGM:{ - if (r_value > 200) - r_value = 200; - break; - } - case BARD: case BARDGM: { - r_value = 55; - break; - } - default: { - r_value = 00; - break; - } - } // end switch (eqclass) - break; - } // end case FORAGE: - case TRACKING:{ - switch(eqclass){ - case RANGER: case RANGERGM: - case BARD: case BARDGM: - case DRUID: case DRUIDGM: { - } - default: { - r_value = 0; - break; - } - }// Class Switch - } // end case TRACKING - /////////////////////////////////////////// - /////////////////////////////////////////// - // Tradeskills - case BAKING: - case TAILORING: - case BLACKSMITHING: - case FLETCHING: - case BREWING: - case JEWELRY_MAKING: - case POTTERY: - case FISHING:{ - // Check for Any Trade above 200, check for X (aa skill) Trades above 200 - r_value = 200; - break; - } - - /////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////// - // Gnome - /////////////////////////////////////////////////////////////////// - case TINKERING:{ - if ( race == GNOME && level > 24 ) { - r_value = ((level*5)+5); - break; - } - r_value = 0; - break; - } // end case TINKERING: - - ///////////////////////////////////////// - // Common - ///////////////////////////////////////// - case BIND_WOUND:{ - r_value = 5 + (level*5); - if (level > 50){ - // Check for aa and class - } - if (r_value > 200) - r_value = 200; - switch (eqclass) { - case ENCHANTER: case ENCHANTERGM: - case MAGICIAN: case MAGICIANGM: - case NECROMANCER: case NECROMANCERGM: - case WIZARD: case WIZARDGM:{ - if ( r_value > 100 ) - r_value = 100; - } - default: { - break; - } - } // end switch (eqclass) - break; - } // end case BIND_WOUND: - case SENSE_HEADING: - case SWIMMING: - case ALCOHOL_TOLERANCE: - case BEGGING:{ - r_value = 5 + (level*5); - if (r_value > 200) - r_value = 200; - break; - } - //case BERSERKING: - default: { - // Unknown skill we should like print something to a log/debug here - r_value = 0; - break; - } - } // end switch (skillid) -// NO skill may go over 252 - if (r_value > 252) - r_value = 252; - return r_value; -} diff --git a/common/SocketLib/HTTPSocket.cpp b/common/SocketLib/HTTPSocket.cpp index 9d0eaa5be..ecde78403 100644 --- a/common/SocketLib/HTTPSocket.cpp +++ b/common/SocketLib/HTTPSocket.cpp @@ -46,7 +46,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include "Parse.h" #include "HTTPSocket.h" -#include "../TCPConnection.h" +#include "../tcp_connection.h" #include #include diff --git a/common/SocketLib/HTTPSocket.h b/common/SocketLib/HTTPSocket.h index 96f4cf389..409c90bce 100644 --- a/common/SocketLib/HTTPSocket.h +++ b/common/SocketLib/HTTPSocket.h @@ -42,7 +42,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #include -#include "../TCPConnection.h" +#include "../tcp_connection.h" #ifdef SOCKETS_NAMESPACE diff --git a/common/SocketLib/socket_include.h b/common/SocketLib/socket_include.h index 20bc39370..c7489ffa3 100644 --- a/common/SocketLib/socket_include.h +++ b/common/SocketLib/socket_include.h @@ -211,7 +211,7 @@ typedef unsigned short port_t; #endif #ifdef _THREADSAFE_SOCKETS -#include "Mutex.h" +#include "mutex.h" #include "Lock.h" #endif diff --git a/common/any.h b/common/any.h new file mode 100644 index 000000000..fd80fbc5f --- /dev/null +++ b/common/any.h @@ -0,0 +1,190 @@ +/* + * Boost Software License - Version 1.0 - August 17th, 2003 + * + * Permission is hereby granted, free of charge, to any person or organization + * obtaining a copy of the software and accompanying documentation covered by + * this license (the "Software") to use, reproduce, display, distribute, + * execute, and transmit the Software, and to prepare derivative works of the + * Software, and to permit third-parties to whom the Software is furnished to + * do so, all subject to the following: + * + * The copyright notices in the Software and this entire statement, including + * the above license grant, this restriction and the following disclaimer, + * must be included in all copies of the Software, in whole or in part, and + * all derivative works of the Software, unless such copies or derivative + * works are solely in the form of machine-executable object code generated by + * a source language processor. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT + * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE + * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +// EQEmu::Any is a modified version of Boost::Any and as such retains the Boost licensing. + +#ifndef EQEMU_COMMON_ANY_H +#define EQEMU_COMMON_ANY_H + +#include +#include + +namespace EQEmu +{ + class Any + { + public: + Any() + : content(nullptr) + { + } + + template + Any(const ValueType &value) + : content(new Holder(value)) + { + } + + Any(const Any &other) + : content(other.content ? other.content->clone() : 0) + { + } + + ~Any() + { + if(content) + delete content; + } + + Any& swap(Any &rhs) + { + std::swap(content, rhs.content); + return *this; + } + + template + Any& operator=(const ValueType &rhs) + { + Any(rhs).swap(*this); + return *this; + } + + Any& operator=(Any rhs) + { + rhs.swap(*this); + return *this; + } + + bool empty() const + { + return !content; + } + + const std::type_info& type() const + { + return content ? content->type() : typeid(void); + } + + class Placeholder + { + public: + virtual ~Placeholder() + { + } + + virtual const std::type_info& type() const = 0; + virtual Placeholder* clone() const = 0; + }; + + + template + class Holder : public Placeholder + { + public: + Holder(const ValueType &value) + : held(value) + { + } + + virtual const std::type_info& type() const + { + return typeid(ValueType); + } + + virtual Placeholder* clone() const + { + return new Holder(held); + } + + ValueType held; + + private: + Holder& operator=(const Holder&); + }; + + private: + template + friend ValueType* any_cast(Any*); + + template + friend ValueType* unsafe_any_cast(Any*); + + Placeholder* content; + }; + + class bad_any_cast : public std::bad_cast + { + public: + virtual const char * what() const throw() + { + return "DBI::bad_any_cast: failed conversion using DBI::any_cast"; + } + }; + + template + ValueType* any_cast(Any* operand) + { + return operand && + operand->type() == typeid(ValueType) ? &static_cast*>(operand->content)->held : nullptr; + } + + template + inline const ValueType* any_cast(const Any* operand) + { + return any_cast(const_cast(operand)); + } + + template + ValueType any_cast(Any& operand) + { + typedef typename std::remove_reference::type nonref; + nonref* result = any_cast(&operand); + if(!result) + throw bad_any_cast(); + return *result; + } + + template + inline ValueType any_cast(const Any& operand) + { + typedef typename std::remove_reference::type nonref; + return any_cast(const_cast(operand)); + } + + template + inline ValueType* unsafe_any_cast(Any* operand) + { + return &static_cast*>(operand->content)->held; + } + + template + inline const ValueType* unsafe_any_cast(const Any* operand) + { + return unsafe_any_cast(const_cast(operand)); + } +} + +#endif diff --git a/common/BasePacket.cpp b/common/base_packet.cpp similarity index 99% rename from common/BasePacket.cpp rename to common/base_packet.cpp index 613e5e25a..44b3643b5 100644 --- a/common/BasePacket.cpp +++ b/common/base_packet.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "debug.h" -#include "BasePacket.h" +#include "base_packet.h" #include "misc.h" #include "packet_dump.h" diff --git a/common/BasePacket.h b/common/base_packet.h similarity index 100% rename from common/BasePacket.h rename to common/base_packet.h diff --git a/common/classes.cpp b/common/classes.cpp index a1a3bf05a..f33d0354a 100644 --- a/common/classes.cpp +++ b/common/classes.cpp @@ -290,50 +290,3 @@ const char* GetEQClassName(uint8 class_, uint8 level) { } } -uint32 GetArrayEQClass(uint8 eqclass) { - switch (eqclass) { - case WARRIOR: - return WARRIOR; - case CLERIC: - return CLERIC; - case PALADIN: - return PALADIN; - case RANGER: - return RANGER; - case SHADOWKNIGHT: - return SHADOWKNIGHT; - case DRUID: - return DRUID; - case MONK: - return MONK; - case BARD: - return BARD; - case ROGUE: - return ROGUE; - case SHAMAN: - return SHAMAN; - case NECROMANCER: - return NECROMANCER; - case WIZARD: - return WIZARD; - case MAGICIAN: - return MAGICIAN; - case ENCHANTER: - return ENCHANTER; - case BEASTLORD: - return BEASTLORD; - case BERSERKER: - return BERSERKER; - default: - return 0; - } -} - -uint8 GetEQArrayEQClass(uint8 eqclass) { - if (eqclass >= WARRIOR && eqclass <= BERSERKER) - return eqclass - WARRIOR; - if (eqclass >= WARRIORGM && eqclass <= BERSERKERGM) - return eqclass - WARRIORGM; - return WARRIOR; -} - diff --git a/common/classes.h b/common/classes.h index b70609379..c2f0c8acf 100644 --- a/common/classes.h +++ b/common/classes.h @@ -86,7 +86,5 @@ #define call_1 65536 const char* GetEQClassName(uint8 class_, uint8 level = 0); -uint32 GetArrayEQClass(uint8 eqclass); -uint8 GetEQArrayEQClass(uint8 eqclass); #endif 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/Condition.cpp b/common/condition.cpp similarity index 99% rename from common/Condition.cpp rename to common/condition.cpp index 7d99d0a43..2dc90b26d 100644 --- a/common/Condition.cpp +++ b/common/condition.cpp @@ -17,7 +17,7 @@ */ #include "debug.h" -#include "Condition.h" +#include "condition.h" #ifdef _WINDOWS diff --git a/common/Condition.h b/common/condition.h similarity index 100% rename from common/Condition.h rename to common/condition.h diff --git a/common/CRC16.cpp b/common/crc16.cpp similarity index 100% rename from common/CRC16.cpp rename to common/crc16.cpp diff --git a/common/CRC16.h b/common/crc16.h similarity index 100% rename from common/CRC16.h rename to common/crc16.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 2d5bdbdcc..4cafc56d1 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -17,16 +17,16 @@ */ #include "../common/debug.h" #include "../common/rulesys.h" +#include +#include +#include #include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include // Disgrace: for windows compile #ifdef _WINDOWS @@ -42,11 +42,21 @@ #include "database.h" #include "eq_packet_structs.h" -#include "guilds.h" -#include "StringUtil.h" +#include "string_util.h" #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 +80,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 @@ -161,6 +141,9 @@ uint32 Database::CheckLogin(const char* name, const char* password, int16* oStat return 0; } + if(results.RowCount() == 0) + return 0; + auto row = results.begin(); uint32 id = atoi(row[0]); @@ -190,24 +173,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()) @@ -220,32 +197,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; } @@ -253,10 +222,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 @@ -280,12 +247,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; } @@ -300,8 +265,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); @@ -316,7 +280,7 @@ bool Database::DeleteAccount(const char* name) { } bool Database::SetLocalPassword(uint32 accid, const char* password) { - std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", password, accid); + std::string query = StringFormat("UPDATE account SET password=MD5('%s') where id=%i;", EscapeString(password).c_str(), accid); auto results = QueryDatabase(query); @@ -341,278 +305,435 @@ 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; } /* -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); + query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `timers` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `inventory` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `char_recipe_list` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `adventure_stats` WHERE `player_id` ='%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `zone_flags` WHERE `charID` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `titles` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `player_titlesets` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `keyring` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `faction_values` WHERE `char_id` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `instance_list_player` WHERE `charid` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = '%d'", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_skills` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_languages` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_bind` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_alternate_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_currency` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_data` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_memmed_spells` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_disciplines` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_material` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_tribute` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_bandolier` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_potionbelt` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_inspect_messages` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_leadership_abilities` WHERE `id` = %u", charid); results = QueryDatabase(query); + query = StringFormat("DELETE FROM `character_alt_currency` WHERE `char_id` = '%d'", charid); results = QueryDatabase(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); + /* 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); + + /* 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); - 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); 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) - { - invquery = StringFormat("INSERT INTO inventory SET charid=%0u, slotid=%0d, itemid=%0u, charges=%0d, color=%0u", - charid, i, newinv->GetItem()->ID,newinv->GetCharges(), newinv->GetColor()); - auto results = QueryDatabase(invquery); + 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); if (!results.RowsAffected()) LogFile->write(EQEMuLog::Error, "StoreCharacter inventory failed. Query '%s' %s", invquery.c_str(), results.ErrorMessage().c_str()); @@ -622,40 +743,41 @@ 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 (results.RowCount() == 1) + { + 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); @@ -666,7 +788,7 @@ uint32 Database::GetAccountIDByChar(const char* charname, uint32* oCharID) { } if (results.RowCount() != 1) - return 0; + return 0; auto row = results.begin(); @@ -680,12 +802,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; } @@ -693,8 +812,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]); } @@ -702,11 +820,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; } @@ -721,8 +838,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 @@ -733,12 +849,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; } @@ -755,19 +869,1553 @@ 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(); - strcpy(name, row[0]); + for (auto row = results.begin(); row != results.end(); ++row) { + strcpy(name, row[0]); + } +} + +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; xnew; $ua->timeout(10); $ua->env_proxy; my $response = $ua->get('https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/db_update.pl'); if ($response->is_success){ open(FILE, '> db_update.pl'); print FILE $response->decoded_content; close(FILE); }\""); +#else + system("wget -O db_update.pl https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/db_update.pl"); +#endif + // } + + /* + Automatic Database Upgrade Script + Script: db_update.pl V 1 - the number that world passes to the script will + force the script to check for a newer version to update itself with + db_update.pl ran_from_world - won't bring up a menu if your database versions match + db_update.pl - ran standalone will bring up a menu prompt + */ + + /* Check for a new version of this script, the arg passed + would have to be higher than the copy they have downloaded + locally and they will re fetch */ + system("perl db_update.pl V 1"); + + /* Run Automatic Database Upgrade Script */ + system("perl db_update.pl ran_from_world"); + + return true; +} + +bool Database::CheckDatabaseConvertPPDeblob(){ + unsigned int lengths; + unsigned int lengths_e; + std::string squery; + Convert::PlayerProfile_Struct* pp; + ExtendedProfile_Struct* e_pp; + uint32 pplen = 0; + uint32 i; + int character_id = 0; + int account_id = 0; + int number_of_characters = 0; + int printppdebug = 0; /* Prints Player Profile */ + int runconvert = 0; + + /* Check For Legacy Storage Method */ + std::string rquery = StringFormat("SHOW TABLES LIKE 'character_'"); + auto results = QueryDatabase(rquery); + if (results.RowCount() == 1){ + runconvert = 1; + printf("\n\n::: Legacy Character Data Binary Blob Storage Detected... \n"); + printf("----------------------------------------------------------\n\n"); + printf(" Database currently has character data being stored via \n"); + printf(" the legacy character storage method and will proceed with converting...\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); + } + + // runconvert = 0; + // printppdebug = 1; + + if (runconvert == 1){ + printf("Running character binary blob to database conversion... \n"); + /* Get the number of characters */ + rquery = StringFormat("SELECT COUNT(`id`) FROM `character_`"); + results = QueryDatabase(rquery); + for (auto row = results.begin(); row != results.end(); ++row) { + number_of_characters = atoi(row[0]); + printf("Number of Characters in Database: %i \n", number_of_characters); + } + + /* Check for table `character_data` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_data'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_data` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_data` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`account_id` int(11) NOT NULL DEFAULT '0', " + "`name` varchar(64) NOT NULL DEFAULT '', " + "`last_name` varchar(64) NOT NULL DEFAULT '', " + "`title` varchar(32) NOT NULL DEFAULT '', " + "`suffix` varchar(32) NOT NULL DEFAULT '', " + "`zone_id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`zone_instance` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`y` float NOT NULL DEFAULT '0', " + "`x` float NOT NULL DEFAULT '0', " + "`z` float NOT NULL DEFAULT '0', " + "`heading` float NOT NULL DEFAULT '0', " + "`gender` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`race` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`class` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`deity` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`birthday` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`last_login` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`time_played` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`level2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`anon` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`gm` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`face` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`hair_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`hair_style` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`beard` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`beard_color` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`eye_color_1` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`eye_color_2` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`drakkin_heritage` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`drakkin_tattoo` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`drakkin_details` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_time_seconds` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_number` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_time_minutes` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_time_hours` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`aa_points_spent` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`aa_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`aa_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`group_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`raid_leadership_exp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`group_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`raid_leadership_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`cur_hp` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`mana` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`endurance` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`intoxication` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`str` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`sta` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`cha` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`dex` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`int` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`agi` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`wis` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`zone_change_count` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`toxicity` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`hunger_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`thirst_level` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ability_up` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_guk` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_mir` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_mmc` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_ruj` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_tak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`ldon_points_available` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`tribute_time_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`career_tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`tribute_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`tribute_active` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_status` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_kills` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_deaths` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_current_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_career_points` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_best_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_worst_death_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_current_kill_streak` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp2` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`pvp_type` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`show_helm` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`group_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`raid_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`guild_auto_consent` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`leadership_exp_on` tinyint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`RestTimer` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`air_remaining` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`autosplit_enabled` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`lfp` tinyint(1) unsigned NOT NULL DEFAULT '0', " + "`lfg` tinyint(1) unsigned NOT NULL DEFAULT '0', " + "`mailkey` char(16) NOT NULL DEFAULT '', " + "`xtargets` tinyint(3) unsigned NOT NULL DEFAULT '5', " + "`firstlogon` tinyint(3) NOT NULL DEFAULT '0', " + "`e_aa_effects` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`e_percent_to_aa` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`e_expended_aa_spent` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "PRIMARY KEY(`id`), " + "UNIQUE KEY `name` (`name`), " + "KEY `account_id` (`account_id`) " + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1; " + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_currency` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_currency'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_currency` doesn't exist... creating..."); + rquery = StringFormat( + " CREATE TABLE `character_currency` ( " + " `id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `platinum` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `gold` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `silver` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `copper` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `platinum_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `gold_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `silver_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `copper_bank` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `platinum_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `gold_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `silver_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `copper_cursor` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `radiant_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `career_radiant_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `ebon_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `career_ebon_crystals` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " PRIMARY KEY (`id`), " + " KEY `id` (`id`) " + " ) ENGINE=InnoDB DEFAULT CHARSET=latin1; " + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_alternate_abilities` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_alternate_abilities'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_alternate_abilities` doesn't exist... creating..."); + rquery = StringFormat( + " CREATE TABLE `character_alternate_abilities` ( " + " `id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `slot` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `aa_id` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + " `aa_value` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + " PRIMARY KEY(`id`,`slot`), " + " KEY `id` (`id`) " + " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_bind` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_bind'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_bind` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_bind` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`is_home` tinyint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`zone_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`instance_id` mediumint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`x` float NOT NULL DEFAULT '0', " + "`y` float NOT NULL DEFAULT '0', " + "`z` float NOT NULL DEFAULT '0', " + "`heading` float NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `is_home`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_languages` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_languages'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_languages` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_languages` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`lang_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`value` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `lang_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_skills` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_skills'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_skills` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_skills` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`skill_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`value` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `skill_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_spells` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_spells'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_spells` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_spells` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT, " + "`slot_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`spell_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `slot_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_memmed_spells` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_memmed_spells'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_memmed_spells` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_memmed_spells` ( " + "`id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`slot_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "`spell_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`, `slot_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_disciplines` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_disciplines'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_disciplines` doesn't exist... creating..."); + rquery = StringFormat( + " CREATE TABLE `character_disciplines` ( " + " `id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + " `slot_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + " `disc_id` smallint(11) UNSIGNED NOT NULL DEFAULT '0', " + " PRIMARY KEY(`id`, `slot_id`), " + " KEY `id` (`id`) " + " ) ENGINE = InnoDB DEFAULT CHARSET = latin1; " + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_material` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_material'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_material` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_material` ( " + "`id` int(11) UNSIGNED NOT NULL AUTO_INCREMENT," + "`slot` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`blue` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`green` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`red` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`use_tint` tinyint(11) UNSIGNED NOT NULL DEFAULT '0'," + "`color` int(11) UNSIGNED NOT NULL DEFAULT '0'," + "PRIMARY KEY(`id`, `slot`)," + "KEY `id` (`id`)" + ") ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_tribute` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_tribute'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_tribute` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_tribute` ( " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " + "`tier` tinyint(11) unsigned NOT NULL DEFAULT '0', " + "`tribute` int(11) UNSIGNED NOT NULL DEFAULT '0', " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_bandolier` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_bandolier'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_bandolier` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_bandolier` ( " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " + "`bandolier_id` tinyint(11) unsigned NOT NULL DEFAULT '0', " + "`bandolier_slot` tinyint(11) unsigned NOT NULL DEFAULT '0', " + "`item_id` int(11) UNSIGNED NOT NULL DEFAULT '0', " + "`icon` int(11) UNSIGNED NOT NULL DEFAULT '0', " + "`bandolier_name` varchar(32) NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`,`bandolier_id`, `bandolier_slot`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_potionbelt` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_potionbelt'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_potionbelt` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_potionbelt` ( " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " + "`potion_id` tinyint(11) unsigned NOT NULL DEFAULT '0', " + "`item_id` int(11) UNSIGNED NOT NULL DEFAULT '0', " + "`icon` int(11) UNSIGNED NOT NULL DEFAULT '0', " + "PRIMARY KEY(`id`,`potion_id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_potionbelt` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_inspect_messages'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_inspect_messages` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_inspect_messages` ( " + "`id` int(11) unsigned NOT NULL DEFAULT 0, " + "`inspect_message` varchar(255) NOT NULL DEFAULT '', " + "PRIMARY KEY(`id`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1;" + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + /* Check for table `character_leadership_abilities` */ + rquery = StringFormat("SHOW TABLES LIKE 'character_leadership_abilities'"); + results = QueryDatabase(rquery); + if (results.RowCount() == 0){ + printf("Table: `character_leadership_abilities` doesn't exist... creating..."); + rquery = StringFormat( + "CREATE TABLE `character_leadership_abilities` (" + "`id` int(11) UNSIGNED NOT NULL DEFAULT 0, " + "`slot` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + "`rank` smallint(11) UNSIGNED NOT NULL DEFAULT 0, " + "PRIMARY KEY(`id`,`slot`), " + "KEY `id` (`id`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " + ); + auto results = QueryDatabase(rquery); + printf(" done...\n"); + } + + /* Done */ + printf("Starting conversion...\n\n"); + + + int char_iter_count = 0; + rquery = StringFormat("SELECT `id` FROM `character_`"); + results = QueryDatabase(rquery); + + uint8 firstlogon = 0; + uint8 lfg = 0; + uint8 lfp = 0; + std::string mailkey; + uint8 xtargets = 0; + std::string inspectmessage; + + for (auto row = results.begin(); row != results.end(); ++row) { + char_iter_count++; + squery = StringFormat("SELECT `id`, `profile`, `name`, `level`, `account_id`, `firstlogon`, `lfg`, `lfp`, `mailkey`, `xtargets`, `inspectmessage`, `extprofile` FROM `character_` WHERE `id` = %i", atoi(row[0])); + auto results2 = QueryDatabase(squery); + auto row2 = results2.begin(); + pp = (Convert::PlayerProfile_Struct*)row2[1]; + e_pp = (ExtendedProfile_Struct*)row2[11]; + character_id = atoi(row[0]); + account_id = atoi(row2[4]); + /* Convert some data from the character_ table that is still relevant */ + firstlogon = atoi(row2[5]); + lfg = atoi(row2[6]); + lfp = atoi(row2[7]); + mailkey = row2[8]; + xtargets = atoi(row2[9]); + inspectmessage = row2[10]; + + /* Verify PP Integrity */ + lengths = results2.LengthOfColumn(1); + if (lengths == sizeof(Convert::PlayerProfile_Struct)) { /* If PP is the size it is expected to be */ + memcpy(pp, row2[1], sizeof(Convert::PlayerProfile_Struct)); + } + /* Continue of PP Size does not match (Usually a created character never logged in) */ + else { + // printf("%s ID: %i profile mismatch, not converting. PP %u - Profile Length %u \n", row2[2] ? row2[2] : "Unknown", character_id, sizeof(PlayerProfile_Struct), lengths); + std::cout << (row2[2] ? row2[2] : "Unknown") << " ID: " << character_id << " size mismatch. Expected Size: " << sizeof(Convert::PlayerProfile_Struct) << " Seen: " << lengths << std::endl; + continue; + } + + lengths_e = results2.LengthOfColumn(11); + if (lengths_e == sizeof(ExtendedProfile_Struct)) { + memcpy(e_pp, row2[11], sizeof(ExtendedProfile_Struct)); + } + if (e_pp->expended_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); + } + + /* 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); + + 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); + + + /* + 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); } + + /* 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); } + } + + /* 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); } + } + /* 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); } + /* 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); } + /* 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); } + /* 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); } + /* 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); } + /* 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); } + /* 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); } + /* 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); } + /* 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); } + /* 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); } + } + } + 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"); + } + } + return true; +} + +bool Database::CheckDatabaseConvertBotsPostPPDeblob(){ +#ifdef BOTS + int runbotsconvert = 0; + + /* Check For Legacy Bot References */ + std::string rquery = StringFormat("SHOW CREATE VIEW `vwBotCharacterMobs`"); + auto 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); + } + } + + if (runbotsconvert == 1){ + printf("Running bot views/function database conversion... \n"); + + /* Update view `vwbotcharactermobs` */ + rquery = StringFormat("DROP VIEW `vwBotCharacterMobs`;"); + results = QueryDatabase(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); + + + /* Update function `GetMobType` */ + rquery = StringFormat("DROP FUNCTION IF EXISTS `GetMobType`;"); + results = QueryDatabase(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); + + + /* Update view `vwgroups` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwGroups`;"); + results = QueryDatabase(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); + + + /* Update view `vwbotgroups` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwBotGroups`;"); + results = QueryDatabase(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); + + + /* Update view `vwguildmembers` */ + rquery = StringFormat("DROP VIEW IF EXISTS `vwGuildMembers`;"); + results = QueryDatabase(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); + } + + if (runbotsconvert == 1){ + printf("\n\nBot views/function conversion complete, continuing world bootup...\n"); + } + +#endif + return true; +} + +bool Database::CheckDatabaseConvertCorpseDeblob(){ + Convert::DBPlayerCorpse_Struct_temp* dbpc; + Convert::classic_db_temp::DBPlayerCorpse_Struct_temp* dbpc_c; + uint32 in_datasize; + bool is_sof = false; + std::string c_type; + std::string scquery; + int8 first_entry = 0; + + std::string query = StringFormat("SHOW TABLES LIKE 'player_corpses'"); + auto results = QueryDatabase(query); + if (results.RowCount() != 0){ + query = StringFormat( + "CREATE TABLE `character_corpse_items` ( " + "`corpse_id` int(11) unsigned NOT NULL, " + "`equip_slot` int(11) unsigned NOT NULL, " + "`item_id` int(11) unsigned DEFAULT NULL, " + "`charges` int(11) unsigned DEFAULT NULL, " + "`aug_1` int(11) unsigned DEFAULT '0', " + "`aug_2` int(11) unsigned DEFAULT '0', " + "`aug_3` int(11) unsigned DEFAULT '0', " + "`aug_4` int(11) unsigned DEFAULT '0', " + "`aug_5` int(11) unsigned DEFAULT '0', " + "`attuned` smallint(5) NOT NULL DEFAULT '0', " + "PRIMARY KEY(`corpse_id`, `equip_slot`) " + ") ENGINE = InnoDB DEFAULT CHARSET = latin1; " + ); + results = QueryDatabase(query); + query = StringFormat("RENAME TABLE `player_corpses` TO `character_corpses`"); + results = QueryDatabase(query); + query = StringFormat( + " ALTER TABLE `character_corpses` \n" + " ADD COLUMN `is_locked` tinyint(11) NULL DEFAULT 0 AFTER `WasAtGraveyard`, \n" + " ADD COLUMN `exp` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `size` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `level` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `race` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `gender` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `class` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `deity` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `texture` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `helm_texture` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `copper` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `silver` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `gold` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `platinum` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `hair_color` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `beard_color` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `eye_color_1` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `eye_color_2` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `hair_style` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `face` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `beard` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `drakkin_heritage` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `drakkin_tattoo` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `drakkin_details` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_1` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_2` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_3` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_4` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_5` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_6` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_7` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_8` int(11) UNSIGNED NULL DEFAULT 0, \n" + " ADD COLUMN `wc_9` int(11) UNSIGNED NULL DEFAULT 0, \n" + " CHANGE COLUMN `zoneid` `zone_id` smallint(5) NOT NULL DEFAULT 0 AFTER `charname`, \n" + " CHANGE COLUMN `instanceid` `instance_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `zone_id`, \n" + " CHANGE COLUMN `timeofdeath` `time_of_death` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' AFTER `data`, \n" + " CHANGE COLUMN `rezzed` `is_rezzed` tinyint(3) UNSIGNED NULL DEFAULT 0 AFTER `time_of_death`, \n" + " CHANGE COLUMN `IsBurried` `is_buried` tinyint(3) NOT NULL DEFAULT 0 AFTER `is_rezzed`; \n" + + ); + results = QueryDatabase(query); + query = StringFormat( + " ALTER TABLE `character_corpses` \n" + " CHANGE COLUMN `WasAtGraveyard` `was_at_graveyard` tinyint(3) NOT NULL DEFAULT 0 AFTER `is_buried` \n" + ); + results = QueryDatabase(query); + } + + std::string rquery = StringFormat("SHOW COLUMNS FROM `character_corpses` LIKE 'data'"); + results = QueryDatabase(rquery); + if (results.RowCount() != 0){ + rquery = StringFormat("SELECT DISTINCT charid FROM character_corpses"); + results = QueryDatabase(rquery); + for (auto row = results.begin(); row != results.end(); ++row) { + std::string squery = StringFormat("SELECT id, charname, data, time_of_death, is_rezzed FROM character_corpses WHERE `charid` = %i", atoi(row[0])); + auto results2 = QueryDatabase(squery); + for (auto row2 = results2.begin(); row2 != results2.end(); ++row2) { + in_datasize = results2.LengthOfColumn(2); + dbpc = (Convert::DBPlayerCorpse_Struct_temp*)row2[2]; + dbpc_c = (Convert::classic_db_temp::DBPlayerCorpse_Struct_temp*)row2[2]; + + if (dbpc == nullptr) + continue; + if (dbpc_c == nullptr) + continue; + + + /* SoF+ */ + uint32 esize1 = (sizeof(Convert::DBPlayerCorpse_Struct_temp) + (dbpc->itemcount * sizeof(Convert::player_lootitem_temp::ServerLootItem_Struct_temp))); + uint32 esize2 = (sizeof(Convert::classic_db_temp::DBPlayerCorpse_Struct_temp) + (dbpc_c->itemcount * sizeof(Convert::player_lootitem_temp::ServerLootItem_Struct_temp))); + + /* SoF */ + if (in_datasize == esize1) { + is_sof = true; + c_type = "SOF"; + } + /* Classic */ + if (in_datasize == esize2) { + is_sof = false; + c_type = "Legacy"; + } + if (in_datasize != esize2 && in_datasize != esize1) { + // std::cout << "[Error] in Corpse Size - OLD SIZE: " << esize1 << " SOF SIZE: " << esize2 << " db_blob_datasize: " << in_datasize << std::endl; + is_sof = false; + c_type = "NULL"; + continue; + } + std::cout << "Converting Corpse: [OK] [" << c_type << "]: " << "ID: " << atoi(row2[0]) << std::endl; + + if (is_sof){ + scquery = StringFormat("UPDATE `character_corpses` SET \n" + "`is_locked` = %d,\n" + "`exp` = %u,\n" + "`size` = %f,\n" + "`level` = %u,\n" + "`race` = %u,\n" + "`gender` = %u,\n" + "`class` = %u,\n" + "`deity` = %u,\n" + "`texture` = %u,\n" + "`helm_texture` = %u,\n" + "`copper` = %u,\n" + "`silver` = %u,\n" + "`gold` = %u,\n" + "`platinum` = %u,\n" + "`hair_color` = %u,\n" + "`beard_color` = %u,\n" + "`eye_color_1` = %u,\n" + "`eye_color_2` = %u,\n" + "`hair_style` = %u,\n" + "`face` = %u,\n" + "`beard` = %u,\n" + "`drakkin_heritage` = %u,\n" + "`drakkin_tattoo` = %u,\n" + "`drakkin_details` = %u,\n" + "`wc_1` = %u,\n" + "`wc_2` = %u,\n" + "`wc_3` = %u,\n" + "`wc_4` = %u,\n" + "`wc_5` = %u,\n" + "`wc_6` = %u,\n" + "`wc_7` = %u,\n" + "`wc_8` = %u,\n" + "`wc_9` = %u \n" + "WHERE `id` = %u \n", + dbpc->locked, + dbpc->exp, + dbpc->size, + dbpc->level, + dbpc->race, + dbpc->gender, + dbpc->class_, + dbpc->deity, + dbpc->texture, + dbpc->helmtexture, + dbpc->copper, + dbpc->silver, + dbpc->gold, + dbpc->plat, + dbpc->haircolor, + dbpc->beardcolor, + dbpc->eyecolor1, + dbpc->eyecolor2, + dbpc->hairstyle, + dbpc->face, + dbpc->beard, + dbpc->drakkin_heritage, + dbpc->drakkin_tattoo, + dbpc->drakkin_details, + dbpc->item_tint[0].color, + dbpc->item_tint[1].color, + dbpc->item_tint[2].color, + dbpc->item_tint[3].color, + dbpc->item_tint[4].color, + dbpc->item_tint[5].color, + dbpc->item_tint[6].color, + dbpc->item_tint[7].color, + dbpc->item_tint[8].color, + atoi(row2[0]) + ); + if (scquery != ""){ auto sc_results = QueryDatabase(scquery); } + + first_entry = 0; + scquery = ""; + /* Print Items */ + for (unsigned int i = 0; i < dbpc->itemcount; i++) { + if (first_entry != 1){ + scquery = StringFormat("REPLACE INTO `character_corpse_items` \n" + " (corpse_id, equip_slot, item_id, charges, aug_1, aug_2, aug_3, aug_4, aug_5, attuned) \n" + " VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", + atoi(row2[0]), + dbpc->items[i].equipSlot, + dbpc->items[i].item_id, + dbpc->items[i].charges, + dbpc->items[i].aug1, + dbpc->items[i].aug2, + dbpc->items[i].aug3, + dbpc->items[i].aug4, + dbpc->items[i].aug5 + ); + first_entry = 1; + } + else{ + scquery = scquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", + atoi(row2[0]), + dbpc->items[i].equipSlot, + dbpc->items[i].item_id, + dbpc->items[i].charges, + dbpc->items[i].aug1, + dbpc->items[i].aug2, + dbpc->items[i].aug3, + dbpc->items[i].aug4, + dbpc->items[i].aug5 + ); + } + } + if (scquery != ""){ auto sc_results = QueryDatabase(scquery); } + } + else{ + /* Classic Converter */ + scquery = StringFormat("UPDATE `character_corpses` SET \n" + "`is_locked` = %d,\n" + "`exp` = %u,\n" + "`size` = %f,\n" + "`level` = %u,\n" + "`race` = %u,\n" + "`gender` = %u,\n" + "`class` = %u,\n" + "`deity` = %u,\n" + "`texture` = %u,\n" + "`helm_texture` = %u,\n" + "`copper` = %u,\n" + "`silver` = %u,\n" + "`gold` = %u,\n" + "`platinum` = %u,\n" + "`hair_color` = %u,\n" + "`beard_color` = %u,\n" + "`eye_color_1` = %u,\n" + "`eye_color_2` = %u,\n" + "`hair_style` = %u,\n" + "`face` = %u,\n" + "`beard` = %u,\n" + "`wc_1` = %u,\n" + "`wc_2` = %u,\n" + "`wc_3` = %u,\n" + "`wc_4` = %u,\n" + "`wc_5` = %u,\n" + "`wc_6` = %u,\n" + "`wc_7` = %u,\n" + "`wc_8` = %u,\n" + "`wc_9` = %u \n" + "WHERE `id` = %u \n", + dbpc_c->locked, + dbpc_c->exp, + dbpc_c->size, + dbpc_c->level, + dbpc_c->race, + dbpc_c->gender, + dbpc_c->class_, + dbpc_c->deity, + dbpc_c->texture, + dbpc_c->helmtexture, + dbpc_c->copper, + dbpc_c->silver, + dbpc_c->gold, + dbpc_c->plat, + dbpc_c->haircolor, + dbpc_c->beardcolor, + dbpc_c->eyecolor1, + dbpc_c->eyecolor2, + dbpc_c->hairstyle, + dbpc_c->face, + dbpc_c->beard, + dbpc_c->item_tint[0].color, + dbpc_c->item_tint[1].color, + dbpc_c->item_tint[2].color, + dbpc_c->item_tint[3].color, + dbpc_c->item_tint[4].color, + dbpc_c->item_tint[5].color, + dbpc_c->item_tint[6].color, + dbpc_c->item_tint[7].color, + dbpc_c->item_tint[8].color, + atoi(row2[0]) + ); + if (scquery != ""){ auto sc_results = QueryDatabase(scquery); } + + first_entry = 0; + scquery = ""; + + /* Print Items */ + for (unsigned int i = 0; i < dbpc_c->itemcount; i++) { + if (first_entry != 1){ + scquery = StringFormat("REPLACE INTO `character_corpse_items` \n" + " (corpse_id, equip_slot, item_id, charges, aug_1, aug_2, aug_3, aug_4, aug_5, attuned) \n" + " VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", + atoi(row2[0]), + dbpc_c->items[i].equipSlot, + dbpc_c->items[i].item_id, + dbpc_c->items[i].charges, + dbpc_c->items[i].aug1, + dbpc_c->items[i].aug2, + dbpc_c->items[i].aug3, + dbpc_c->items[i].aug4, + dbpc_c->items[i].aug5 + ); + first_entry = 1; + } + else{ + scquery = scquery + StringFormat(", (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", + atoi(row2[0]), + dbpc_c->items[i].equipSlot, + dbpc_c->items[i].item_id, + dbpc_c->items[i].charges, + dbpc_c->items[i].aug1, + dbpc_c->items[i].aug2, + dbpc_c->items[i].aug3, + dbpc_c->items[i].aug4, + dbpc_c->items[i].aug5 + ); + } + } + if (scquery != ""){ auto sc_results = QueryDatabase(scquery); } + } + } + } + QueryDatabase(StringFormat("ALTER TABLE `character_corpses` DROP COLUMN `data`")); + } + return true; } bool Database::LoadVariables() { @@ -1134,7 +2782,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; } @@ -1215,7 +2863,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); @@ -1228,14 +2876,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; } @@ -1271,11 +2919,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()) @@ -1288,13 +2934,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; } @@ -1305,13 +2948,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; } @@ -1323,15 +2963,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; } @@ -1346,13 +2985,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; } @@ -1360,57 +2997,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; } @@ -1418,8 +3009,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); @@ -1501,13 +3091,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; } @@ -1516,20 +3104,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; } @@ -1568,45 +3149,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()); @@ -1618,11 +3189,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); @@ -1633,8 +3202,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()) @@ -1692,46 +3261,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_leaders` WHERE `gid` = '%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"); @@ -1747,6 +3324,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; } @@ -1767,15 +3350,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); @@ -1800,8 +3388,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); @@ -1817,14 +3404,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 @@ -1840,8 +3425,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); @@ -1936,6 +3520,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 @@ -2227,9 +3959,7 @@ bool Database::RemoveClientsFromInstance(uint16 instance_id) return results.Success(); } -bool Database::CheckInstanceExists(uint16 instance_id) -{ - +bool Database::CheckInstanceExists(uint16 instance_id) { std::string query = StringFormat("SELECT * FROM instance_list where id=%u", instance_id); auto results = QueryDatabase(query); @@ -2242,15 +3972,12 @@ bool Database::CheckInstanceExists(uint16 instance_id) return true; } -void Database::BuryCorpsesInInstance(uint16 instance_id) -{ - - std::string query = StringFormat("UPDATE player_corpses SET IsBurried=1, instanceid=0 WHERE instanceid=%u", instance_id); +void Database::BuryCorpsesInInstance(uint16 instance_id) { + std::string query = StringFormat("UPDATE `character_corpses` SET `is_buried` = 1, `instance_id` =0 WHERE `instance_id` = %u", instance_id); auto results = QueryDatabase(query); } -uint16 Database::GetInstanceVersion(uint16 instance_id) -{ +uint16 Database::GetInstanceVersion(uint16 instance_id) { if(instance_id == 0) return 0; @@ -2267,8 +3994,7 @@ uint16 Database::GetInstanceVersion(uint16 instance_id) return atoi(row[0]); } -uint16 Database::GetInstanceID(const char* zone, uint32 charid, int16 version) -{ +uint16 Database::GetInstanceID(const char* zone, uint32 charid, int16 version) { std::string query = StringFormat("SELECT instance_list.id FROM instance_list, instance_list_player " "WHERE instance_list.zone=%u AND instance_list.version=%u AND instance_list.id=instance_list_player.id AND " @@ -2472,8 +4198,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`, " @@ -2488,16 +4213,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 9be05f0cd..6325b4e3d 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 "EQStream.h" -#include "guilds.h" -#include "MiscFunctions.h" -#include "Mutex.h" -#include "Item.h" -#include "extprofile.h"*/ #include #include #include @@ -47,6 +41,7 @@ class SpawnGroupList; class Petition; class Client; class Merc; +class MySQLRequestResult; struct Combine_Struct; //struct Faction; //struct FactionMods; @@ -74,21 +69,15 @@ uint8 eventid; EventLogDetails_Struct eld[255]; }; - -// Added By Hogie -// INSERT into variables (varname,value) values('decaytime [minlevel] [maxlevel]','[number of seconds]'); -// IE: decaytime 1 54 = Levels 1 through 54 -// decaytime 55 100 = Levels 55 through 100 -// It will always put the LAST time for the level (I think) from the Database struct npcDecayTimes_Struct { uint16 minlvl; uint16 maxlvl; uint32 seconds; }; -// Added By Hogie -- End + struct VarCache_Struct { - char varname[26]; // varname is char(25) in database + char varname[26]; char value[0]; }; @@ -99,6 +88,411 @@ struct ExtendedProfile_Struct; struct GuildMember_Struct; class PTimerList; +#pragma pack(1) + +/* Conversion Structs */ + +namespace Convert { + struct BindStruct { + /*000*/ uint32 zoneId; + /*004*/ float x; + /*008*/ float y; + /*012*/ float z; + /*016*/ float heading; + }; + struct Color_Struct + { + union + { + struct + { + uint8 blue; + uint8 green; + uint8 red; + uint8 use_tint; // if there's a tint this is FF + } rgb; + uint32 color; + }; + }; + struct AA_Array + { + uint32 AA; + uint32 value; + }; + struct SpellBuff_Struct + { + /*000*/ uint8 slotid; //badly named... seems to be 2 for a real buff, 0 otherwise + /*001*/ uint8 level; + /*002*/ uint8 bard_modifier; + /*003*/ uint8 effect; //not real + /*004*/ uint32 spellid; + /*008*/ uint32 duration; + /*012*/ uint32 counters; + /*016*/ uint32 player_id; //'global' ID of the caster, for wearoff messages + /*020*/ + }; + struct Tribute_Struct { + uint32 tribute; + uint32 tier; + }; + struct Disciplines_Struct { + uint32 values[MAX_PP_DISCIPLINES]; + }; + struct GroupLeadershipAA_Struct { + 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 { + 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 { + Convert::GroupLeadershipAA_Struct group; + Convert::RaidLeadershipAA_Struct raid; + }; + uint32 ranks[MAX_LEADERSHIP_AA_ARRAY]; + }; + }; + typedef struct + { + /*00*/ char Name[64]; + /*64*/ uint32 Level; + /*68*/ uint32 Race; + /*72*/ uint32 Class; + /*76*/ uint32 Zone; + /*80*/ uint32 Time; + /*84*/ uint32 Points; + /*88*/ + } PVPStatsEntry_Struct; + struct BandolierItem_Struct { + uint32 item_id; + uint32 icon; + char item_name[64]; + }; + struct Bandolier_Struct { + char name[32]; + Convert::BandolierItem_Struct items[EmuConstants::BANDOLIER_SIZE]; + }; + struct PotionBelt_Struct { + Convert::BandolierItem_Struct items[EmuConstants::POTION_BELT_SIZE]; + }; + struct SuspendedMinion_Struct + { + /*000*/ uint16 SpellID; + /*002*/ uint32 HP; + /*006*/ uint32 Mana; + /*010*/ Convert::SpellBuff_Struct Buffs[BUFF_COUNT]; + /*510*/ uint32 Items[_MaterialCount]; + /*546*/ char Name[64]; + /*610*/ + }; + + struct PlayerProfile_Struct { + /*0000*/ uint32 checksum; // Checksum from CRC32::SetEQChecksum + /*0004*/ char name[64]; // Name of player sizes not right + /*0068*/ char last_name[32]; // Last name of player sizes not right + /*0100*/ uint32 gender; // Player Gender - 0 Male, 1 Female + /*0104*/ uint32 race; // Player race + /*0108*/ uint32 class_; // Player class + /*0112*/ uint32 unknown0112; // + /*0116*/ uint32 level; // Level of player (might be one byte) + /*0120*/ Convert::BindStruct binds[5]; // Bind points (primary is first, home city is fifth) + /*0220*/ uint32 deity; // deity + /*0224*/ uint32 guild_id; + /*0228*/ uint32 birthday; // characters bday + /*0232*/ uint32 lastlogin; // last login or zone time + /*0236*/ uint32 timePlayedMin; // in minutes + /*0240*/ uint8 pvp; + /*0241*/ uint8 level2; //no idea why this is here, but thats how it is on live + /*0242*/ uint8 anon; // 2=roleplay, 1=anon, 0=not anon + /*0243*/ uint8 gm; + /*0244*/ uint8 guildrank; + /*0245*/ uint8 guildbanker; + /*0246*/ uint8 unknown0246[6]; // + /*0252*/ uint32 intoxication; + /*0256*/ uint32 spellSlotRefresh[MAX_PP_REF_MEMSPELL]; //in ms + /*0292*/ uint32 abilitySlotRefresh; + /*0296*/ uint8 haircolor; // Player hair color + /*0297*/ uint8 beardcolor; // Player beard color + /*0298*/ uint8 eyecolor1; // Player left eye color + /*0299*/ uint8 eyecolor2; // Player right eye color + /*0300*/ uint8 hairstyle; // Player hair style + /*0301*/ uint8 beard; // Beard type + /*0302*/ uint8 ability_time_seconds; //The following four spots are unknown right now..... + /*0303*/ uint8 ability_number; //ability used + /*0304*/ uint8 ability_time_minutes; + /*0305*/ uint8 ability_time_hours; //place holder + /*0306*/ uint8 unknown0306[6]; // @bp Spacer/Flag? + /*0312*/ uint32 item_material[_MaterialCount]; // Item texture/material of worn/held items + /*0348*/ uint8 unknown0348[44]; + /*0392*/ Convert::Color_Struct item_tint[_MaterialCount]; + /*0428*/ Convert::AA_Array aa_array[MAX_PP_AA_ARRAY]; + /*2348*/ float unknown2384; //seen ~128, ~47 + /*2352*/ char servername[32]; // length probably not right + /*2384*/ char title[32]; // length might be wrong + /*2416*/ char suffix[32]; // length might be wrong + /*2448*/ uint32 guildid2; // + /*2452*/ uint32 exp; // Current Experience + /*2456*/ uint32 unknown2492; + /*2460*/ uint32 points; // Unspent Practice points + /*2464*/ uint32 mana; // current mana + /*2468*/ uint32 cur_hp; // current hp + /*2472*/ uint32 unknown2508; // 0x05 + /*2476*/ uint32 STR; // Strength + /*2480*/ uint32 STA; // Stamina + /*2484*/ uint32 CHA; // Charisma + /*2488*/ uint32 DEX; // Dexterity + /*2492*/ uint32 INT; // Intelligence + /*2496*/ uint32 AGI; // Agility + /*2500*/ uint32 WIS; // Wisdom + /*2504*/ uint8 face; // Player face + /*2505*/ uint8 unknown2541[47]; // ? + /*2552*/ uint8 languages[MAX_PP_LANGUAGE]; + /*2580*/ uint8 unknown2616[4]; + /*2584*/ uint32 spell_book[MAX_PP_REF_SPELLBOOK]; + /*4504*/ uint8 unknown4540[128]; // Was [428] all 0xff + /*4632*/ uint32 mem_spells[MAX_PP_REF_MEMSPELL]; + /*4668*/ uint8 unknown4704[32]; // + /*4700*/ float y; // Player y position + /*4704*/ float x; // Player x position + /*4708*/ float z; // Player z position + /*4712*/ float heading; // Direction player is facing + /*4716*/ uint8 unknown4752[4]; // + /*4720*/ int32 platinum; // Platinum Pieces on player + /*4724*/ int32 gold; // Gold Pieces on player + /*4728*/ int32 silver; // Silver Pieces on player + /*4732*/ int32 copper; // Copper Pieces on player + /*4736*/ int32 platinum_bank; // Platinum Pieces in Bank + /*4740*/ int32 gold_bank; // Gold Pieces in Bank + /*4744*/ int32 silver_bank; // Silver Pieces in Bank + /*4748*/ int32 copper_bank; // Copper Pieces in Bank + /*4752*/ int32 platinum_cursor; // Platinum on cursor + /*4756*/ int32 gold_cursor; // Gold on cursor + /*4760*/ int32 silver_cursor; // Silver on cursor + /*4764*/ int32 copper_cursor; // Copper on cursor + /*4768*/ int32 platinum_shared; // Platinum shared between characters + /*4772*/ uint8 unknown4808[24]; + /*4796*/ uint32 skills[MAX_PP_SKILL]; // [400] List of skills // 100 dword buffer + /*5196*/ uint8 unknown5132[184]; + /*5380*/ uint32 pvp2; // + /*5384*/ uint32 unknown5420; // + /*5388*/ uint32 pvptype; // + /*5392*/ uint32 unknown5428; // + /*5396*/ uint32 ability_down; // Guessing + /*5400*/ uint8 unknown5436[8]; // + /*5408*/ uint32 autosplit; //not used right now + /*5412*/ uint8 unknown5448[8]; + /*5420*/ uint32 zone_change_count; // Number of times user has zoned in their career (guessing) + /*5424*/ uint8 unknown5460[16]; // + /*5440*/ uint32 drakkin_heritage; // + /*5444*/ uint32 drakkin_tattoo; // + /*5448*/ uint32 drakkin_details; // + /*5452*/ uint32 expansions; // expansion setting, bit field of expansions avaliable + /*5456*/ int32 toxicity; //from drinking potions, seems to increase by 3 each time you drink + /*5460*/ char unknown5496[16]; // + /*5476*/ int32 hunger_level; + /*5480*/ int32 thirst_level; + /*5484*/ uint32 ability_up; + /*5488*/ char unknown5524[16]; + /*5504*/ uint16 zone_id; // Current zone of the player + /*5506*/ uint16 zoneInstance; // Instance ID + /*5508*/ Convert::SpellBuff_Struct buffs[BUFF_COUNT]; // Buffs currently on the player + /*6008*/ char groupMembers[6][64];// + /*6392*/ char unknown6428[656]; + /*7048*/ uint32 entityid; + /*7052*/ uint32 leadAAActive; + /*7056*/ uint32 unknown7092; + /*7060*/ int32 ldon_points_guk; //client uses these as signed + /*7064*/ int32 ldon_points_mir; + /*7068*/ int32 ldon_points_mmc; + /*7072*/ int32 ldon_points_ruj; + /*7076*/ int32 ldon_points_tak; + /*7080*/ int32 ldon_points_available; + /*7084*/ int32 ldon_wins_guk; + /*7088*/ int32 ldon_wins_mir; + /*7092*/ int32 ldon_wins_mmc; + /*7096*/ int32 ldon_wins_ruj; + /*7100*/ int32 ldon_wins_tak; + /*7104*/ int32 ldon_losses_guk; + /*7108*/ int32 ldon_losses_mir; + /*7112*/ int32 ldon_losses_mmc; + /*7116*/ int32 ldon_losses_ruj; + /*7120*/ int32 ldon_losses_tak; + /*7124*/ uint8 unknown7160[72]; + /*7196*/ uint32 tribute_time_remaining; //in miliseconds + /*7200*/ uint32 showhelm; + /*7204*/ uint32 career_tribute_points; + /*7208*/ uint32 unknown7244; + /*7212*/ uint32 tribute_points; + /*7216*/ uint32 unknown7252; + /*7220*/ uint32 tribute_active; //1=active + /*7224*/ Convert::Tribute_Struct tributes[EmuConstants::TRIBUTE_SIZE]; + /*7264*/ Convert::Disciplines_Struct disciplines; + /*7664*/ uint32 recastTimers[MAX_RECAST_TYPES]; // Timers (GMT of last use) + /*7744*/ char unknown7780[160]; + /*7904*/ uint32 endurance; + /*7908*/ uint32 group_leadership_exp; //0-1000 + /*7912*/ uint32 raid_leadership_exp; //0-2000 + /*7916*/ uint32 group_leadership_points; + /*7920*/ uint32 raid_leadership_points; + /*7924*/ Convert::LeadershipAA_Struct leader_abilities; + /*8052*/ uint8 unknown8088[132]; + /*8184*/ uint32 air_remaining; + /*8188*/ uint32 PVPKills; + /*8192*/ uint32 PVPDeaths; + /*8196*/ uint32 PVPCurrentPoints; + /*8200*/ uint32 PVPCareerPoints; + /*8204*/ uint32 PVPBestKillStreak; + /*8208*/ uint32 PVPWorstDeathStreak; + /*8212*/ uint32 PVPCurrentKillStreak; + /*8216*/ Convert::PVPStatsEntry_Struct PVPLastKill; + /*8304*/ Convert::PVPStatsEntry_Struct PVPLastDeath; + /*8392*/ uint32 PVPNumberOfKillsInLast24Hours; + /*8396*/ Convert::PVPStatsEntry_Struct PVPRecentKills[50]; + /*12796*/ uint32 aapoints_spent; + /*12800*/ uint32 expAA; + /*12804*/ uint32 aapoints; //avaliable, unspent + /*12808*/ uint8 unknown12844[36]; + /*12844*/ Convert::Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT]; + /*14124*/ uint8 unknown14160[4506]; + /*18630*/ Convert::SuspendedMinion_Struct SuspendedMinion; // No longer in use + /*19240*/ uint32 timeentitledonaccount; + /*19244*/ Convert::PotionBelt_Struct potionbelt; //there should be 3 more of these + /*19532*/ uint8 unknown19568[8]; + /*19540*/ uint32 currentRadCrystals; // Current count of radiant crystals + /*19544*/ uint32 careerRadCrystals; // Total count of radiant crystals ever + /*19548*/ uint32 currentEbonCrystals;// Current count of ebon crystals + /*19552*/ uint32 careerEbonCrystals; // Total count of ebon crystals ever + /*19556*/ uint8 groupAutoconsent; // 0=off, 1=on + /*19557*/ uint8 raidAutoconsent; // 0=off, 1=on + /*19558*/ uint8 guildAutoconsent; // 0=off, 1=on + /*19559*/ uint8 unknown19595[5]; // ***Placeholder (6/29/2005) + /*19564*/ uint32 RestTimer; + /*19568*/ + }; + + + namespace player_lootitem_temp + { + struct ServerLootItem_Struct_temp { + uint32 item_id; + int16 equipSlot; + uint8 charges; + uint16 lootslot; + uint32 aug1; + uint32 aug2; + uint32 aug3; + uint32 aug4; + uint32 aug5; + }; + } + + struct DBPlayerCorpse_Struct_temp { + uint32 crc; + bool locked; + uint32 itemcount; + uint32 exp; + float size; + uint8 level; + uint8 race; + uint8 gender; + uint8 class_; + uint8 deity; + uint8 texture; + uint8 helmtexture; + uint32 copper; + uint32 silver; + uint32 gold; + uint32 plat; + Color_Struct item_tint[9]; + uint8 haircolor; + uint8 beardcolor; + uint8 eyecolor1; + uint8 eyecolor2; + uint8 hairstyle; + uint8 face; + uint8 beard; + uint32 drakkin_heritage; + uint32 drakkin_tattoo; + uint32 drakkin_details; + player_lootitem_temp::ServerLootItem_Struct_temp items[0]; + }; + + namespace classic_db_temp { + struct DBPlayerCorpse_Struct_temp { + uint32 crc; + bool locked; + uint32 itemcount; + uint32 exp; + float size; + uint8 level; + uint8 race; + uint8 gender; + uint8 class_; + uint8 deity; + uint8 texture; + uint8 helmtexture; + uint32 copper; + uint32 silver; + uint32 gold; + uint32 plat; + Color_Struct item_tint[9]; + uint8 haircolor; + uint8 beardcolor; + uint8 eyecolor1; + uint8 eyecolor2; + uint8 hairstyle; + uint8 face; + uint8 beard; + player_lootitem_temp::ServerLootItem_Struct_temp items[0]; + }; + } +} + +#pragma pack() + class Database : public DBcore { public: Database(); @@ -106,9 +500,14 @@ public: bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); ~Database(); + /* * 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 +517,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 +573,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 +602,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 +613,18 @@ 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); + + /* Database Conversions*/ + bool CheckDatabaseConversions(); + bool CheckDatabaseConvertPPDeblob(); + bool CheckDatabaseConvertCorpseDeblob(); + bool CheckDatabaseConvertBotsPostPPDeblob(); /* * Database Variables @@ -250,10 +659,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 +682,7 @@ private: */ void ClearAllRaids(); void ClearAllRaidDetails(); + void ClearAllRaidLeaders(); }; #endif diff --git a/common/dbasync.cpp b/common/dbasync.cpp deleted file mode 100644 index 4153bc6fa..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/MiscFunctions.h" -#include "StringUtil.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/dbcore.cpp b/common/dbcore.cpp index 3d25236c7..84d2528f0 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -4,13 +4,14 @@ #include #endif +#include #include #include #include #include #include "dbcore.h" #include -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include #ifdef _WINDOWS @@ -113,6 +114,16 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo #ifdef _EQDEBUG std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; #endif + + /* Implement Logging at the Root */ + if (mysql_errno(&mysql) > 0 && strlen(query) > 0){ + std::cout << "\n[MYSQL ERR] " << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << " [Query]: \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 << "[MYSQL ERR] " << mysql_error(&mysql) << "\n" << query << "\n"; + log.close(); + } + return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql),errorBuffer); } @@ -126,7 +137,6 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), rowCount, (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql)); - #if DEBUG_MYSQL_QUERIES >= 1 if (requestResult.Success()) { @@ -145,93 +155,16 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo return requestResult; } -bool DBcore::RunQuery(const char* query, uint32 querylen, char* errbuf, MYSQL_RES** result, uint32* affected_rows, uint32* last_insert_id, uint32* errnum, bool retry) { - if (errnum) - *errnum = 0; - if (errbuf) - errbuf[0] = 0; - bool ret = false; - LockMutex lock(&MDatabase); - if (pStatus != Connected) - Open(); +void DBcore::TransactionBegin() { + QueryDatabase("START TRANSACTION"); +} - if (mysql_real_query(&mysql, query, querylen)) { - if (mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) - pStatus = Error; - if (mysql_errno(&mysql) == CR_SERVER_LOST || mysql_errno(&mysql) == CR_SERVER_GONE_ERROR) { - if (retry) { - std::cout << "Database Error: Lost connection, attempting to recover...." << std::endl; - ret = RunQuery(query, querylen, errbuf, result, affected_rows, last_insert_id, errnum, false); - if (ret) - std::cout << "Reconnection to database successful." << std::endl; - } - else { - pStatus = Error; - if (errnum) - *errnum = mysql_errno(&mysql); - if (errbuf) - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); - std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; - ret = false; - } - } - else { - if (errnum) - *errnum = mysql_errno(&mysql); - if (errbuf) - snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql)); -#ifdef _EQDEBUG - std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl; -#endif - ret = false; - } - } - else { - if (result && mysql_field_count(&mysql)) { - *result = mysql_store_result(&mysql); -#ifdef _EQDEBUG - DBMemLeak::Alloc(*result, query); -#endif - } - else if (result) - *result = 0; - if (affected_rows) - *affected_rows = mysql_affected_rows(&mysql); - if (last_insert_id) - *last_insert_id = (uint32)mysql_insert_id(&mysql); - if (result) { - if (*result) { - ret = true; - } - else { -#ifdef _EQDEBUG - std::cout << "DB Query Error: No Result" << std::endl; -#endif - if (errnum) - *errnum = UINT_MAX; - if (errbuf) - strcpy(errbuf, "DBcore::RunQuery: No Result"); - ret = false; - } - } - else { - ret = true; - } - } -#if DEBUG_MYSQL_QUERIES >= 1 - if (ret) { - std::cout << "query successful"; - if (result && (*result)) - std::cout << ", " << (int) mysql_num_rows(*result) << " rows returned"; - if (affected_rows) - std::cout << ", " << (*affected_rows) << " rows affected"; - std::cout<< std::endl; - } - else { - std::cout << "QUERY: query FAILED" << std::endl; - } -#endif - return ret; +void DBcore::TransactionCommit() { + QueryDatabase("COMMIT"); +} + +void DBcore::TransactionRollback() { + QueryDatabase("ROLLBACK"); } uint32 DBcore::DoEscapeString(char* tobuf, const char* frombuf, uint32 fromlen) { diff --git a/common/dbcore.h b/common/dbcore.h index 2c3602a81..0cdceb61b 100644 --- a/common/dbcore.h +++ b/common/dbcore.h @@ -9,12 +9,12 @@ #include #include #include "../common/types.h" -#include "../common/Mutex.h" +#include "../common/mutex.h" #include "../common/linked_list.h" #include "../common/queue.h" #include "../common/timer.h" -#include "../common/Condition.h" -#include "../common/MySQLRequestResult.h" +#include "../common/condition.h" +#include "../common/mysql_request_result.h" class DBcore { public: @@ -23,9 +23,11 @@ public: DBcore(); ~DBcore(); eStatus GetStatus() { return pStatus; } - bool RunQuery(const char* query, uint32 querylen, char* errbuf = 0, MYSQL_RES** result = 0, uint32* affected_rows = 0, uint32* last_insert_id = 0, uint32* errnum = 0, bool retry = true); MySQLRequestResult QueryDatabase(const char* query, uint32 querylen, bool retryOnFailureOnce = true); MySQLRequestResult QueryDatabase(std::string query, bool retryOnFailureOnce = true); + void TransactionBegin(); + void TransactionCommit(); + void TransactionRollback(); uint32 DoEscapeString(char* tobuf, const char* frombuf, uint32 fromlen); void ping(); MYSQL* getMySQL(){ return &mysql; } diff --git a/common/debug.cpp b/common/debug.cpp index fa114a338..228d57631 100644 --- a/common/debug.cpp +++ b/common/debug.cpp @@ -19,8 +19,8 @@ #endif #include "debug.h" -#include "StringUtil.h" -#include "MiscFunctions.h" +#include "string_util.h" +#include "misc_functions.h" #include "platform.h" #ifndef va_copy diff --git a/common/debug.h b/common/debug.h index 856e851e1..5e2fff368 100644 --- a/common/debug.h +++ b/common/debug.h @@ -69,7 +69,7 @@ #include "logsys.h" -#include "../common/Mutex.h" +#include "../common/mutex.h" #include #include @@ -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 edee551a1..e8ce3b986 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -1,542 +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_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/EmuTCPConnection.cpp b/common/emu_tcp_connection.cpp similarity index 99% rename from common/EmuTCPConnection.cpp rename to common/emu_tcp_connection.cpp index f71b3822b..b97d5257f 100644 --- a/common/EmuTCPConnection.cpp +++ b/common/emu_tcp_connection.cpp @@ -30,8 +30,8 @@ tremendously. #include #include -#include "EmuTCPConnection.h" -#include "EmuTCPServer.h" +#include "emu_tcp_connection.h" +#include "emu_tcp_server.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" diff --git a/common/EmuTCPConnection.h b/common/emu_tcp_connection.h similarity index 99% rename from common/EmuTCPConnection.h rename to common/emu_tcp_connection.h index 155319ffa..ff4153f74 100644 --- a/common/EmuTCPConnection.h +++ b/common/emu_tcp_connection.h @@ -1,7 +1,7 @@ #ifndef EmuTCPCONNECTION_H_ #define EmuTCPCONNECTION_H_ -#include "TCPConnection.h" +#include "tcp_connection.h" #include "timer.h" //moved out of TCPConnection:: to be more exportable diff --git a/common/EmuTCPServer.cpp b/common/emu_tcp_server.cpp similarity index 96% rename from common/EmuTCPServer.cpp rename to common/emu_tcp_server.cpp index 3549abacd..985be6edd 100644 --- a/common/EmuTCPServer.cpp +++ b/common/emu_tcp_server.cpp @@ -1,6 +1,6 @@ #include "debug.h" -#include "EmuTCPServer.h" -#include "EmuTCPConnection.h" +#include "emu_tcp_server.h" +#include "emu_tcp_connection.h" EmuTCPServer::EmuTCPServer(uint16 iPort, bool iOldFormat) : TCPServer(iPort), diff --git a/common/EmuTCPServer.h b/common/emu_tcp_server.h similarity index 97% rename from common/EmuTCPServer.h rename to common/emu_tcp_server.h index c84524662..8941812ad 100644 --- a/common/EmuTCPServer.h +++ b/common/emu_tcp_server.h @@ -1,7 +1,7 @@ #ifndef EmuTCPSERVER_H_ #define EmuTCPSERVER_H_ -#include "TCPServer.h" +#include "tcp_server.h" class EmuTCPConnection; struct EmuTCPNetPacket_Struct; diff --git a/common/eq_constants.h b/common/eq_constants.h index 1868d6c3e..426d4a9e5 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -968,4 +968,6 @@ namespace legacy { } InventorySlot; } +static const uint32 MAX_SPELL_DB_ID_VAL = 65535; + #endif diff --git a/common/eq_dictionary.cpp b/common/eq_dictionary.cpp index 4c772689d..f85d3110f 100644 --- a/common/eq_dictionary.cpp +++ b/common/eq_dictionary.cpp @@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "eq_dictionary.h" -#include "StringUtil.h" +#include "string_util.h" // // class EmuConstants @@ -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 eb35f5127..c08809a12 100644 --- a/common/eq_dictionary.h +++ b/common/eq_dictionary.h @@ -26,13 +26,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "eq_constants.h" #include "clientversions.h" #include -#include "../common/patches/Client62_constants.h" -#include "../common/patches/Titanium_constants.h" -#include "../common/patches/SoF_constants.h" -#include "../common/patches/SoD_constants.h" -#include "../common/patches/Underfoot_constants.h" -#include "../common/patches/RoF_constants.h" -//#include "../common/patches/RoF2_constants.h" +#include "../common/patches/client62_constants.h" +#include "../common/patches/titanium_constants.h" +#include "../common/patches/sof_constants.h" +#include "../common/patches/sod_constants.h" +#include "../common/patches/underfoot_constants.h" +#include "../common/patches/rof_constants.h" +//#include "../common/patches/rof2_constants.h" // *** DO NOT CHANGE without a full understanding of the consequences..the server is set up to use these settings explicitly!! *** // *** You will cause compilation failures and corrupt your database if partial or incorrect attempts to change them are made!! *** @@ -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/EQPacket.cpp b/common/eq_packet.cpp similarity index 99% rename from common/EQPacket.cpp rename to common/eq_packet.cpp index 5372a0f9d..3ea622040 100644 --- a/common/EQPacket.cpp +++ b/common/eq_packet.cpp @@ -19,10 +19,10 @@ #include #include #include -#include "EQPacket.h" +#include "eq_packet.h" #include "misc.h" #include "op_codes.h" -#include "CRC16.h" +#include "crc16.h" #include "platform.h" #ifndef STATIC_OPCODE #include "opcodemgr.h" @@ -476,7 +476,7 @@ EQRawApplicationPacket::EQRawApplicationPacket(const unsigned char *buf, const u const unsigned char *packet_start = (buf + 3); const int32 packet_length = len - 3; safe_delete_array(pBuffer); - if(len >= 0) + if(packet_length >= 0) { size = packet_length; pBuffer = new unsigned char[size]; diff --git a/common/EQPacket.h b/common/eq_packet.h similarity index 90% rename from common/EQPacket.h rename to common/eq_packet.h index 8b1f7b628..04418f733 100644 --- a/common/EQPacket.h +++ b/common/eq_packet.h @@ -18,8 +18,8 @@ #ifndef _EQPACKET_H #define _EQPACKET_H -#include "BasePacket.h" -#include "EQStreamType.h" +#include "base_packet.h" +#include "eq_stream_type.h" #include "op_codes.h" #include "platform.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 aab82e4f7..62a166c67 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -478,7 +478,11 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint8 cs_unknown[4]; + uint32 cs_unknown1; + uint32 cs_unknown2; + float y_pos; + float x_pos; + float z_pos; }; struct SpellEffect_Struct @@ -534,7 +538,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; @@ -597,8 +601,8 @@ struct GMTrainee_Struct { /*000*/ uint32 npcid; /*004*/ uint32 playerid; - /*008*/ uint32 skills[73]; - /*300*/ uint8 unknown300[148]; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[40]; /*448*/ }; @@ -689,7 +693,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 +763,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]; + }; }; /** @@ -779,17 +831,18 @@ struct BindStruct { /*008*/ float y; /*012*/ float z; /*016*/ float heading; - /*020*/ + /*020*/ uint32 instance_id; + /*024*/ }; struct SuspendedMinion_Struct { - /*000*/ uint16 SpellID; - /*002*/ uint32 HP; - /*006*/ uint32 Mana; - /*010*/ SpellBuff_Struct Buffs[BUFF_COUNT]; - /*510*/ uint32 Items[_MaterialCount]; - /*546*/ char Name[64]; + /*000*/ uint16 SpellID; + /*002*/ uint32 HP; + /*006*/ uint32 Mana; + /*010*/ SpellBuff_Struct Buffs[BUFF_COUNT]; + /*510*/ uint32 Items[_MaterialCount]; + /*546*/ char Name[64]; /*610*/ }; @@ -800,10 +853,13 @@ 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_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size +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 = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 240; static const uint32 MAX_GROUP_MEMBERS = 6; static const uint32 MAX_RECAST_TYPES = 20; @@ -880,7 +936,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 +975,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 +1502,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 +1521,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 +2236,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; @@ -2287,10 +2352,13 @@ struct Stun_Struct { // 4 bytes total }; struct AugmentItem_Struct { -/*00*/ int16 container_slot; -/*02*/ char unknown02[2]; -/*04*/ int32 augment_slot; -/*08*/ +/*00*/ uint32 container_index; +/*04*/ int32 container_slot; +/*08*/ uint32 augment_index; +/*12*/ int32 augment_slot; +/*16*/ uint32 dest_inst_id; // The unique serial number for the item instance that is being augmented +/*20*/ int32 augment_action; // Guessed - 0 = augment, 1 = remove with distiller, 3 = delete aug +/*24*/ }; // OP_Emote @@ -2596,6 +2664,17 @@ struct Translocate_Struct { /*088*/ uint32 Complete; }; +struct PendingTranslocate_Struct +{ + uint32 zone_id; + uint16 instance_id; + float heading; + float x; + float y; + float z; + uint32 spell_id; +}; + struct Sacrifice_Struct { /*000*/ uint32 CasterID; /*004*/ uint32 TargetID; @@ -3394,7 +3473,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3468,6 +3547,7 @@ struct MerchantList { int8 level_required; uint16 alt_currency_cost; uint32 classes_required; + uint8 probability; }; struct TempMerchantList { @@ -3904,6 +3984,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 @@ -3919,6 +4004,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 @@ -4060,7 +4158,7 @@ struct GroupInvite_Struct { // uint8 unknown128[65]; }; -struct BuffFadeMsg_Struct { +struct ColoredText_Struct { uint32 color; char msg[1]; }; @@ -4257,6 +4355,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 @@ -4290,14 +4395,14 @@ typedef struct { struct ControlBoat_Struct { /*000*/ uint32 boatId; // entitylist id of the boat /*004*/ bool TakeControl; // 01 if taking control, 00 if releasing it -/*007*/ // no idea what these last three bytes represent +/*007*/ char unknown[3]; // no idea what these last three bytes represent }; 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*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // the reply has the text here /*072*/ }; @@ -4460,6 +4565,14 @@ struct GuildBankPromote_Struct /*12*/ uint32 Slot2; // Always appears to be the same as Slot for Action code 3 }; +struct GuildPromoteStruct { +/*000*/ char target[64]; +/*064*/ char name[64]; +/*128*/ uint32 rank; +/*132*/ uint32 myrank; +/*136*/ +}; + struct GuildBankPermissions_Struct { /*00*/ uint32 Action; // 6 @@ -4577,11 +4690,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]; }; @@ -5094,17 +5209,17 @@ struct MercenaryMerchantResponse_Struct { }; struct ServerLootItem_Struct { - uint32 item_id; - int16 equipSlot; - uint8 charges; - uint16 lootslot; - uint32 aug1; - uint32 aug2; - uint32 aug3; - uint32 aug4; - uint32 aug5; - uint8 minlevel; - uint8 maxlevel; + uint32 item_id; // uint32 item_id; + int16 equip_slot; // int16 equip_slot; + uint16 charges; // uint8 charges; + uint16 lootslot; // uint16 lootslot; + uint32 aug_1; // uint32 aug_1; + uint32 aug_2; // uint32 aug_2; + uint32 aug_3; // uint32 aug_3; + uint32 aug_4; // uint32 aug_4; + uint32 aug_5; // uint32 aug_5; + uint8 min_level; // + uint8 max_level; // }; //Found in client near a ref to the string: diff --git a/common/EQStream.cpp b/common/eq_stream.cpp similarity index 95% rename from common/EQStream.cpp rename to common/eq_stream.cpp index 785c11ff3..7b69decc2 100644 --- a/common/EQStream.cpp +++ b/common/eq_stream.cpp @@ -17,12 +17,12 @@ */ #include "debug.h" -#include "EQPacket.h" -#include "EQStream.h" +#include "eq_packet.h" +#include "eq_stream.h" #include "misc.h" -#include "Mutex.h" +#include "mutex.h" #include "op_codes.h" -#include "CRC16.h" +#include "crc16.h" #include "platform.h" #include @@ -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/EQStream.h b/common/eq_stream.h similarity index 98% rename from common/EQStream.h rename to common/eq_stream.h index 97d304642..5a637c46e 100644 --- a/common/EQStream.h +++ b/common/eq_stream.h @@ -9,13 +9,13 @@ #ifndef WIN32 #include #endif -#include "EQStreamType.h" -#include "EQPacket.h" -#include "EQStreamIntf.h" -#include "Mutex.h" +#include "eq_stream_type.h" +#include "eq_packet.h" +#include "eq_stream_intf.h" +#include "mutex.h" #include "../common/opcodemgr.h" #include "../common/misc.h" -#include "../common/Condition.h" +#include "../common/condition.h" #include "../common/timer.h" #define FLAG_COMPRESSED 0x01 diff --git a/common/EQStreamFactory.cpp b/common/eq_stream_factory.cpp similarity index 99% rename from common/EQStreamFactory.cpp rename to common/eq_stream_factory.cpp index 0eb6fe25f..bc3e2e2ba 100644 --- a/common/EQStreamFactory.cpp +++ b/common/eq_stream_factory.cpp @@ -1,5 +1,5 @@ #include "debug.h" -#include "EQStreamFactory.h" +#include "eq_stream_factory.h" #ifdef _WINDOWS #include #include @@ -16,7 +16,7 @@ #include #include #include "op_codes.h" -#include "EQStream.h" +#include "eq_stream.h" #include "logsys.h" ThreadReturnType EQStreamFactoryReaderLoop(void *eqfs) diff --git a/common/EQStreamFactory.h b/common/eq_stream_factory.h similarity index 95% rename from common/EQStreamFactory.h rename to common/eq_stream_factory.h index dd0b7f1b8..7aa5d66ac 100644 --- a/common/EQStreamFactory.h +++ b/common/eq_stream_factory.h @@ -4,8 +4,8 @@ #include #include -#include "../common/EQStream.h" -#include "../common/Condition.h" +#include "../common/eq_stream.h" +#include "../common/condition.h" #include "../common/timeoutmgr.h" #include "../common/opcodemgr.h" #include "../common/timer.h" diff --git a/common/EQStreamIdent.cpp b/common/eq_stream_ident.cpp similarity index 98% rename from common/EQStreamIdent.cpp rename to common/eq_stream_ident.cpp index d4332c9a0..b60ac28d0 100644 --- a/common/EQStreamIdent.cpp +++ b/common/eq_stream_ident.cpp @@ -1,6 +1,6 @@ #include "debug.h" -#include "EQStreamIdent.h" -#include "EQStreamProxy.h" +#include "eq_stream_ident.h" +#include "eq_stream_proxy.h" #include "logsys.h" EQStreamIdentifier::~EQStreamIdentifier() { diff --git a/common/EQStreamIdent.h b/common/eq_stream_ident.h similarity index 97% rename from common/EQStreamIdent.h rename to common/eq_stream_ident.h index 855838c86..c038daf26 100644 --- a/common/EQStreamIdent.h +++ b/common/eq_stream_ident.h @@ -1,7 +1,7 @@ #ifndef EQSTREAMIDENT_H_ #define EQSTREAMIDENT_H_ -#include "EQStream.h" +#include "eq_stream.h" #include "timer.h" #include #include diff --git a/common/EQStreamIntf.h b/common/eq_stream_intf.h similarity index 100% rename from common/EQStreamIntf.h rename to common/eq_stream_intf.h diff --git a/common/EQStreamLocator.h b/common/eq_stream_locator.h similarity index 100% rename from common/EQStreamLocator.h rename to common/eq_stream_locator.h diff --git a/common/EQStreamProxy.cpp b/common/eq_stream_proxy.cpp similarity index 96% rename from common/EQStreamProxy.cpp rename to common/eq_stream_proxy.cpp index 90ea6ffce..20fc4ea06 100644 --- a/common/EQStreamProxy.cpp +++ b/common/eq_stream_proxy.cpp @@ -1,8 +1,8 @@ #include "debug.h" -#include "EQStreamProxy.h" -#include "EQStream.h" -#include "StructStrategy.h" +#include "eq_stream_proxy.h" +#include "eq_stream.h" +#include "struct_strategy.h" EQStreamProxy::EQStreamProxy(EQStream *&stream, const StructStrategy *structs, OpcodeManager **opcodes) diff --git a/common/EQStreamProxy.h b/common/eq_stream_proxy.h similarity index 98% rename from common/EQStreamProxy.h rename to common/eq_stream_proxy.h index cecdf9f38..34ea3a9fc 100644 --- a/common/EQStreamProxy.h +++ b/common/eq_stream_proxy.h @@ -3,7 +3,7 @@ #include "types.h" -#include "EQStreamIntf.h" +#include "eq_stream_intf.h" class EQStream; class StructStrategy; diff --git a/common/EQStreamType.h b/common/eq_stream_type.h similarity index 100% rename from common/EQStreamType.h rename to common/eq_stream_type.h diff --git a/common/EQDB.cpp b/common/eqdb.cpp similarity index 99% rename from common/EQDB.cpp rename to common/eqdb.cpp index 124f2e817..03746b0d4 100644 --- a/common/EQDB.cpp +++ b/common/eqdb.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "debug.h" -#include "EQDB.h" +#include "eqdb.h" #include "database.h" #include #include diff --git a/common/EQDB.h b/common/eqdb.h similarity index 98% rename from common/EQDB.h rename to common/eqdb.h index 8333d6990..b07517b69 100644 --- a/common/EQDB.h +++ b/common/eqdb.h @@ -22,7 +22,7 @@ #include #include #include "types.h" -#include "EQDBRes.h" +#include "eqdb_res.h" #include //this is the main object exported to perl. diff --git a/common/EQDBRes.cpp b/common/eqdb_res.cpp similarity index 98% rename from common/EQDBRes.cpp rename to common/eqdb_res.cpp index 964746c0b..f34229a1f 100644 --- a/common/EQDBRes.cpp +++ b/common/eqdb_res.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "debug.h" -#include "EQDBRes.h" +#include "eqdb_res.h" #include std::vector EQDBRes::fetch_row_array() { diff --git a/common/EQDBRes.h b/common/eqdb_res.h similarity index 100% rename from common/EQDBRes.h rename to common/eqdb_res.h diff --git a/common/EQEmuConfig.cpp b/common/eqemu_config.cpp similarity index 99% rename from common/EQEmuConfig.cpp rename to common/eqemu_config.cpp index b3859faba..40cdc991d 100644 --- a/common/EQEmuConfig.cpp +++ b/common/eqemu_config.cpp @@ -16,8 +16,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "EQEmuConfig.h" -#include "MiscFunctions.h" +#include "eqemu_config.h" +#include "misc_functions.h" #include #include diff --git a/common/EQEmuConfig.h b/common/eqemu_config.h similarity index 98% rename from common/EQEmuConfig.h rename to common/eqemu_config.h index 2871117c3..24e5a7ac4 100644 --- a/common/EQEmuConfig.h +++ b/common/eqemu_config.h @@ -18,7 +18,7 @@ #ifndef __EQEmuConfig_H #define __EQEmuConfig_H -#include "XMLParser.h" +#include "xml_parser.h" #include "linked_list.h" struct LoginConfig { @@ -118,14 +118,14 @@ protected: #define ELEMENT(name) \ void do_##name(TiXmlElement *ele); - #include "EQEmuConfig_elements.h" + #include "eqemu_config_elements.h" EQEmuConfig() { // import the needed handler prototypes #define ELEMENT(name) \ Handlers[#name]=(ElementHandler)&EQEmuConfig::do_##name; - #include "EQEmuConfig_elements.h" + #include "eqemu_config_elements.h" // Set sane defaults diff --git a/common/EQEmuConfig_elements.h b/common/eqemu_config_elements.h similarity index 100% rename from common/EQEmuConfig_elements.h rename to common/eqemu_config_elements.h diff --git a/common/EQEMuError.cpp b/common/eqemu_error.cpp similarity index 98% rename from common/EQEMuError.cpp rename to common/eqemu_error.cpp index 6dc5f9e96..ff9a7bbd6 100644 --- a/common/EQEMuError.cpp +++ b/common/eqemu_error.cpp @@ -18,10 +18,10 @@ #ifdef _WINDOWS #include #endif -#include "EQEMuError.h" +#include "eqemu_error.h" #include "linked_list.h" -#include "Mutex.h" -#include "MiscFunctions.h" +#include "mutex.h" +#include "misc_functions.h" #include #include #ifdef _WINDOWS diff --git a/common/EQEMuError.h b/common/eqemu_error.h similarity index 100% rename from common/EQEMuError.h rename to common/eqemu_error.h diff --git a/common/extprofile.h b/common/extprofile.h index 114983444..a8447d4fc 100644 --- a/common/extprofile.h +++ b/common/extprofile.h @@ -19,8 +19,7 @@ #define EXTENDED_PROFILE_H #include "eq_packet_structs.h" -#include "Item.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/faction.cpp b/common/faction.cpp index 7016f6547..576971ec5 100644 --- a/common/faction.cpp +++ b/common/faction.cpp @@ -47,7 +47,7 @@ const char *FactionValueToString(FACTION_VALUE fv) { //o-------------------------------------------------------------- -//| Name: CalculateFaction; rembrant, Dec. 16, 2001 +//| Name: CalculateFaction; Dec. 16, 2001 //o-------------------------------------------------------------- //| Notes: Returns the faction message value. //| Modify these values to taste. @@ -69,7 +69,7 @@ FACTION_VALUE CalculateFaction(FactionMods* fm, int32 tmpCharacter_value) return FACTION_INDIFFERENT; } -// neotokyo: this function should check if some races have more than one race define +// this function should check if some races have more than one race define bool IsOfEqualRace(int r1, int r2) { if (r1 == r2) @@ -88,7 +88,7 @@ bool IsOfEqualRace(int r1, int r2) return false; } -// neotokyo: trolls endure ogres, dark elves, ... +// trolls endure ogres, dark elves, ... bool IsOfIndiffRace(int r1, int r2) { if (r1 == r2) diff --git a/common/features.h b/common/features.h index 3047475e1..0860788e1 100644 --- a/common/features.h +++ b/common/features.h @@ -163,7 +163,7 @@ enum { //timer settings, all in milliseconds CombatEventTimer_expire = 12000, Tribute_duration = 600000, ZoneTimerResolution = 3, //sleep time between zone main loop runs (milliseconds) - FeignMemoryDuration = 120000, // EverHood - Duration player must feign death to clear zonewide agro. + FeignMemoryDuration = 120000, // Duration player must feign death to clear zonewide agro. EnragedTimer = 360000, EnragedDurationTimer = 10000 }; @@ -208,12 +208,12 @@ enum { //some random constants //chance ratio that a #define THREATENLY_ARRGO_CHANCE 32 // 32/128 (25%) chance that a mob will arrgo on con Threatenly -// max factions per npc faction list +//max factions per npc faction list #define MAX_NPC_FACTIONS 20 -//value caps -#define MAX_FACTION 1500 -#define MIN_FACTION -1500 +//individual faction pool +#define MAX_PERSONAL_FACTION 1200 +#define MIN_PERSONAL_FACTION -3000 //The Level Cap: //#define LEVEL_CAP RuleI(Character, MaxLevel) //hard cap is 127 @@ -261,7 +261,8 @@ enum { commandChangeFlags = 200, //ability to set/refresh flags commandBanPlayers = 100, //can set bans on players commandChangeDatarate = 201, //edit client's data rate - commandZoneToCoords = 0 //can #zone with coords + commandZoneToCoords = 0, //can #zone with coords + commandInterrogateInv = 100 //below this == only log on error state and self-only target dump }; //default states for logging flag on NPCs and clients (having NPCs on by default is prolly a bad idea) diff --git a/common/guild_base.cpp b/common/guild_base.cpp index 494139778..e6df92569 100644 --- a/common/guild_base.cpp +++ b/common/guild_base.cpp @@ -20,8 +20,8 @@ #include "guild_base.h" #include "database.h" #include "logsys.h" -//#include "MiscFunctions.h" -#include "StringUtil.h" +//#include "misc_functions.h" +#include "string_util.h" #include #include @@ -40,6 +40,8 @@ BaseGuildManager::~BaseGuildManager() { ClearGuilds(); } + + bool BaseGuildManager::LoadGuilds() { ClearGuilds(); @@ -49,36 +51,34 @@ bool BaseGuildManager::LoadGuilds() { return(false); } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query("SELECT id, name, leader, minstatus, motd, motd_setter,channel,url FROM guilds"); std::map::iterator res; - // load up all the guilds - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "SELECT id, name, leader, minstatus, motd, motd_setter,channel,url FROM guilds"), errbuf, &result)) { - _log(GUILDS__ERROR, "Error loading guilds '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); - } - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - _CreateGuild(atoi(row[0]), row[1], atoi(row[2]), atoi(row[3]), row[4], row[5], row[6], row[7]); - } - mysql_free_result(result); + auto results = m_db->QueryDatabase(query); - //load up the rank info for each guild. - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "SELECT guild_id,rank,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace FROM guild_ranks"), errbuf, &result)) { - _log(GUILDS__ERROR, "Error loading guild ranks '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); + if (!results.Success()) + { + _log(GUILDS__ERROR, "Error loading guilds '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { + + for (auto row=results.begin();row!=results.end();++row) + _CreateGuild(atoi(row[0]), row[1], atoi(row[2]), atoi(row[3]), row[4], row[5], row[6], row[7]); + + query = "SELECT guild_id,rank,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace FROM guild_ranks"; + results = m_db->QueryDatabase(query); + + if (!results.Success()) + { + _log(GUILDS__ERROR, "Error loading guild ranks '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + for (auto row=results.begin();row!=results.end();++row) + { uint32 guild_id = atoi(row[0]); uint8 rankn = atoi(row[1]); + if(rankn > GUILD_MAX_RANK) { _log(GUILDS__ERROR, "Found invalid (too high) rank %d for guild %d, skipping.", rankn, guild_id); continue; @@ -102,9 +102,8 @@ bool BaseGuildManager::LoadGuilds() { rank.permissions[GUILD_MOTD] = (row[9][0] == '1')?true:false; rank.permissions[GUILD_WARPEACE] = (row[10][0] == '1')?true:false; } - mysql_free_result(result); - return(true); + return true; } bool BaseGuildManager::RefreshGuild(uint32 guild_id) { @@ -113,63 +112,64 @@ bool BaseGuildManager::RefreshGuild(uint32 guild_id) { return(false); } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT name, leader, minstatus, motd, motd_setter, channel,url FROM guilds WHERE id=%lu", (unsigned long)guild_id); std::map::iterator res; GuildInfo *info; // load up all the guilds - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "SELECT name, leader, minstatus, motd, motd_setter, channel,url FROM guilds WHERE id=%lu", (unsigned long)guild_id), errbuf, &result)) { - _log(GUILDS__ERROR, "Error reloading guilds '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) + { + _log(GUILDS__ERROR, "Error reloading guilds '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - if ((row = mysql_fetch_row(result))) { - //delete the old entry and create the new one. - info = _CreateGuild(guild_id, row[0], atoi(row[1]), atoi(row[2]), row[3], row[4], row[5], row[6]); - } else { + + if (results.RowCount() == 0) + { _log(GUILDS__ERROR, "Unable to find guild %d in the database.", guild_id); - return(false); + return false; } - mysql_free_result(result); - //load up the rank info for each guild. - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "SELECT guild_id,rank,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace " - "FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id), errbuf, &result)) { - _log(GUILDS__ERROR, "Error reloading guild ranks '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); + auto row = results.begin(); + + info = _CreateGuild(guild_id, row[0], atoi(row[1]), atoi(row[2]), row[3], row[4], row[5], row[6]); + + query = StringFormat("SELECT guild_id, rank, title, can_hear, can_speak, can_invite, can_remove, can_promote, can_demote, can_motd, can_warpeace " + "FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id); + results = m_db->QueryDatabase(query); + + if (!results.Success()) + { + _log(GUILDS__ERROR, "Error reloading guild ranks '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { + for (auto row=results.begin();row!=results.end();++row) + { uint8 rankn = atoi(row[1]); + if(rankn > GUILD_MAX_RANK) { _log(GUILDS__ERROR, "Found invalid (too high) rank %d for guild %d, skipping.", rankn, guild_id); continue; } + RankInfo &rank = info->ranks[rankn]; rank.name = row[2]; - rank.permissions[GUILD_HEAR] = (row[3][0] == '1')?true:false; - rank.permissions[GUILD_SPEAK] = (row[4][0] == '1')?true:false; - rank.permissions[GUILD_INVITE] = (row[5][0] == '1')?true:false; - rank.permissions[GUILD_REMOVE] = (row[6][0] == '1')?true:false; - rank.permissions[GUILD_PROMOTE] = (row[7][0] == '1')?true:false; - rank.permissions[GUILD_DEMOTE] = (row[8][0] == '1')?true:false; - rank.permissions[GUILD_MOTD] = (row[9][0] == '1')?true:false; - rank.permissions[GUILD_WARPEACE] = (row[10][0] == '1')?true:false; + rank.permissions[GUILD_HEAR] = (row[3][0] == '1') ? true: false; + rank.permissions[GUILD_SPEAK] = (row[4][0] == '1') ? true: false; + rank.permissions[GUILD_INVITE] = (row[5][0] == '1') ? true: false; + rank.permissions[GUILD_REMOVE] = (row[6][0] == '1') ? true: false; + rank.permissions[GUILD_PROMOTE] = (row[7][0] == '1') ? true: false; + rank.permissions[GUILD_DEMOTE] = (row[8][0] == '1') ? true: false; + rank.permissions[GUILD_MOTD] = (row[9][0] == '1') ? true: false; + rank.permissions[GUILD_WARPEACE] = (row[10][0] == '1') ? true: false; } - mysql_free_result(result); _log(GUILDS__DB, "Successfully refreshed guild %d from the database.", guild_id); - return(true); + return true; } BaseGuildManager::GuildInfo *BaseGuildManager::_CreateGuild(uint32 guild_id, const char *guild_name, uint32 leader_char_id, uint8 minstatus, const char *guild_motd, const char *motd_setter, const char *Channel, const char *URL) @@ -231,24 +231,20 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { } GuildInfo *info = res->second; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("DELETE FROM guilds WHERE id=%lu", (unsigned long)guild_id); //clear out old `guilds` entry - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM guilds WHERE id=%lu", (unsigned long)guild_id), errbuf)) - { - _log(GUILDS__ERROR, "Error clearing old guild record when storing %d '%s': %s", guild_id, query, errbuf); - } - safe_delete_array(query); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) + _log(GUILDS__ERROR, "Error clearing old guild record when storing %d '%s': %s", guild_id, query.c_str(), results.ErrorMessage().c_str()); //clear out old `guild_ranks` entries - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id), errbuf)) - { - _log(GUILDS__ERROR, "Error clearing old guild_ranks records when storing %d '%s': %s", guild_id, query, errbuf); - } - safe_delete_array(query); + query = StringFormat("DELETE FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id); + results = m_db->QueryDatabase(query); + + if (!results.Success()) + _log(GUILDS__ERROR, "Error clearing old guild_ranks records when storing %d '%s': %s", guild_id, query.c_str(), results.ErrorMessage().c_str()); //escape our strings. char *name_esc = new char[info->name.length()*2+1]; @@ -259,18 +255,18 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { m_db->DoEscapeString(motd_set_esc, info->motd_setter.c_str(), info->motd_setter.length()); //insert the new `guilds` entry - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO guilds (id,name,leader,minstatus,motd,motd_setter) VALUES(%lu,'%s',%lu,%d,'%s', '%s')", - (unsigned long)guild_id, name_esc, (unsigned long)info->leader_char_id, info->minstatus, motd_esc, motd_set_esc), errbuf)) + query = StringFormat("INSERT INTO guilds (id,name,leader,minstatus,motd,motd_setter) VALUES(%lu,'%s',%lu,%d,'%s', '%s')", + (unsigned long)guild_id, name_esc, (unsigned long)info->leader_char_id, info->minstatus, motd_esc, motd_set_esc); + results = m_db->QueryDatabase(query); + + if (!results.Success()) { - _log(GUILDS__ERROR, "Error inserting new guild record when storing %d. Giving up. '%s': %s", guild_id, query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error inserting new guild record when storing %d. Giving up. '%s': %s", guild_id, query.c_str(), results.ErrorMessage().c_str()); safe_delete_array(name_esc); safe_delete_array(motd_esc); safe_delete_array(motd_set_esc); - return(false); + return false; } - safe_delete_array(query); safe_delete_array(name_esc); safe_delete_array(motd_esc); safe_delete_array(motd_set_esc); @@ -278,36 +274,37 @@ bool BaseGuildManager::_StoreGuildDB(uint32 guild_id) { //now insert the new ranks uint8 rank; for(rank = 0; rank <= GUILD_MAX_RANK; rank++) { - const RankInfo &r = info->ranks[rank]; + const RankInfo &rankInfo = info->ranks[rank]; - char *title_esc = new char[r.name.length()*2+1]; - m_db->DoEscapeString(title_esc, r.name.c_str(), r.name.length()); + char *title_esc = new char[rankInfo.name.length()*2+1]; + m_db->DoEscapeString(title_esc, rankInfo.name.c_str(), rankInfo.name.length()); - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO guild_ranks (guild_id,rank,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace)" + query = StringFormat("INSERT INTO guild_ranks " + "(guild_id,rank,title,can_hear,can_speak,can_invite,can_remove,can_promote,can_demote,can_motd,can_warpeace)" " VALUES(%d,%d,'%s',%d,%d,%d,%d,%d,%d,%d,%d)", guild_id, rank, title_esc, - r.permissions[GUILD_HEAR], - r.permissions[GUILD_SPEAK], - r.permissions[GUILD_INVITE], - r.permissions[GUILD_REMOVE], - r.permissions[GUILD_PROMOTE], - r.permissions[GUILD_DEMOTE], - r.permissions[GUILD_MOTD], - r.permissions[GUILD_WARPEACE]), errbuf)) + rankInfo.permissions[GUILD_HEAR], + rankInfo.permissions[GUILD_SPEAK], + rankInfo.permissions[GUILD_INVITE], + rankInfo.permissions[GUILD_REMOVE], + rankInfo.permissions[GUILD_PROMOTE], + rankInfo.permissions[GUILD_DEMOTE], + rankInfo.permissions[GUILD_MOTD], + rankInfo.permissions[GUILD_WARPEACE]); + results = m_db->QueryDatabase(query); + + if (!results.Success()) { - _log(GUILDS__ERROR, "Error inserting new guild rank record when storing %d for %d. Giving up. '%s': %s", rank, guild_id, query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error inserting new guild rank record when storing %d for %d. Giving up. '%s': %s", rank, guild_id, query.c_str(), results.ErrorMessage().c_str()); safe_delete_array(title_esc); - return(false); + return false; } - safe_delete_array(query); safe_delete_array(title_esc); } _log(GUILDS__DB, "Stored guild %d in the database", guild_id); - return(true); + return true; } uint32 BaseGuildManager::_GetFreeGuildID() { @@ -316,27 +313,39 @@ uint32 BaseGuildManager::_GetFreeGuildID() { return(GUILD_NONE); } - char errbuf[MYSQL_ERRMSG_SIZE]; - char query[100]; - MYSQL_RES *result; - + std::string query; //this has got to be one of the more retarded things I have seen. //none the less, im too lazy to rewrite it right now. + //possibly: + // + // SELECT t1.id + 1 + // FROM guilds t1 + // WHERE NOT EXISTS ( + // SELECT * + // FROM guilds t2 + // WHERE t2.id = t1.id + 1 + // ) + // LIMIT 1 + // + // Seems likely what we should be doing is auto incrementing the guild table + // inserting, then getting the id. NOT getting a free id then inserting. + // could be a race condition. - uint16 x; - for (x = 1; x < MAX_NUMBER_GUILDS; x++) { - snprintf(query, 100, "SELECT id FROM guilds where id=%i;", x); + for (auto index = 1; index < MAX_NUMBER_GUILDS; ++index) + { + query = StringFormat("SELECT id FROM guilds where id=%i;", index); + auto results = m_db->QueryDatabase(query); - if (m_db->RunQuery(query, strlen(query), errbuf, &result)) { - if (mysql_num_rows(result) == 0) { - mysql_free_result(result); - _log(GUILDS__DB, "Located free guild ID %d in the database", x); - return x; - } - mysql_free_result(result); + if (!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error in _GetFreeGuildID query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + continue; } - else { - LogFile->write(EQEMuLog::Error, "Error in _GetFreeGuildID query '%s': %s", query, errbuf); + + if (results.RowCount() == 0) + { + _log(GUILDS__DB, "Located free guild ID %d in the database", index); + return index; } } @@ -465,6 +474,11 @@ bool BaseGuildManager::SetBankerFlag(uint32 charid, bool is_banker) { return(true); } +bool BaseGuildManager::ForceRankUpdate(uint32 charid) { + SendRankUpdate(charid); + return(true); +} + bool BaseGuildManager::SetAltFlag(uint32 charid, bool is_alt) { if(!DBSetAltFlag(charid, is_alt)) @@ -529,23 +543,21 @@ bool BaseGuildManager::DBDeleteGuild(uint32 guild_id) { return(false); } - char *query = 0; - //clear out old `guilds` entry - _RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM guilds WHERE id=%lu", (unsigned long)guild_id), "clearing old guild record"); + std::string query = StringFormat("DELETE FROM guilds WHERE id=%lu", (unsigned long)guild_id); + QueryWithLogging(query, "clearing old guild record"); //clear out old `guild_ranks` entries - _RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id), "clearing old guild_ranks records"); + query = StringFormat("DELETE FROM guild_ranks WHERE guild_id=%lu", (unsigned long)guild_id); + QueryWithLogging(query, "clearing old guild_ranks records"); //clear out people belonging to this guild. - _RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM guild_members WHERE guild_id=%lu", (unsigned long)guild_id), "clearing chars in guild"); + query = StringFormat("DELETE FROM guild_members WHERE guild_id=%lu", (unsigned long)guild_id); + QueryWithLogging(query, "clearing chars in guild"); // Delete the guild bank - _RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM guild_bank WHERE guildid=%lu", (unsigned long)guild_id), "deleting guild bank"); + query = StringFormat("DELETE FROM guild_bank WHERE guildid=%lu", (unsigned long)guild_id); + QueryWithLogging(query, "deleting guild bank"); _log(GUILDS__DB, "Deleted guild %d from the database.", guild_id); @@ -555,81 +567,73 @@ bool BaseGuildManager::DBDeleteGuild(uint32 guild_id) { bool BaseGuildManager::DBRenameGuild(uint32 guild_id, const char* name) { if(m_db == nullptr) { _log(GUILDS__DB, "Requested to rename guild %d when we have no database object.", guild_id); - return(false); + return false; } std::map::const_iterator res; res = m_guilds.find(guild_id); if(res == m_guilds.end()) - return(false); + return false; GuildInfo *info = res->second; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //escape our strings. uint32 len = strlen(name); char *esc = new char[len*2+1]; m_db->DoEscapeString(esc, name, len); //insert the new `guilds` entry - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "UPDATE guilds SET name='%s' WHERE id=%d", - esc, guild_id), errbuf)) + std::string query = StringFormat("UPDATE guilds SET name='%s' WHERE id=%d", esc, guild_id); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) { - _log(GUILDS__ERROR, "Error renaming guild %d '%s': %s", guild_id, query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error renaming guild %d '%s': %s", guild_id, query.c_str(), results.Success()); safe_delete_array(esc); - return(false); + return false; } - safe_delete_array(query); safe_delete_array(esc); _log(GUILDS__DB, "Renamed guild %s (%d) to %s in database.", info->name.c_str(), guild_id, name); info->name = name; //update our local record. - return(true); + return true; } bool BaseGuildManager::DBSetGuildLeader(uint32 guild_id, uint32 leader) { if(m_db == nullptr) { _log(GUILDS__DB, "Requested to set the leader for guild %d when we have no database object.", guild_id); - return(false); + return false; } std::map::const_iterator res; res = m_guilds.find(guild_id); if(res == m_guilds.end()) - return(false); + return false; GuildInfo *info = res->second; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //insert the new `guilds` entry - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "UPDATE guilds SET leader='%d' WHERE id=%d", - leader, guild_id), errbuf)) + std::string query = StringFormat("UPDATE guilds SET leader='%d' WHERE id=%d",leader, guild_id); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) { - _log(GUILDS__ERROR, "Error changing leader on guild %d '%s': %s", guild_id, query, errbuf); - safe_delete_array(query); - return(false); + _log(GUILDS__ERROR, "Error changing leader on guild %d '%s': %s", guild_id, query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); //set the old leader to officer if(!DBSetGuildRank(info->leader_char_id, GUILD_OFFICER)) - return(false); + return false; //set the new leader to leader if(!DBSetGuildRank(leader, GUILD_LEADER)) - return(false); + return false; _log(GUILDS__DB, "Set guild leader for guild %d to %d in the database", guild_id, leader); info->leader_char_id = leader; //update our local record. - return(true); + return true; } bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const char *setter) { @@ -644,9 +648,6 @@ bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const c return(false); GuildInfo *info = res->second; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //escape our strings. uint32 len = strlen(motd); uint32 len2 = strlen(setter); @@ -656,17 +657,16 @@ bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const c m_db->DoEscapeString(esc_set, setter, len2); //insert the new `guilds` entry - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "UPDATE guilds SET motd='%s',motd_setter='%s' WHERE id=%d", - esc, esc_set, guild_id), errbuf)) + std::string query = StringFormat("UPDATE guilds SET motd='%s',motd_setter='%s' WHERE id=%d", esc, esc_set, guild_id); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) { - _log(GUILDS__ERROR, "Error setting MOTD for guild %d '%s': %s", guild_id, query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error setting MOTD for guild %d '%s': %s", guild_id, query.c_str(), results.ErrorMessage().c_str()); safe_delete_array(esc); safe_delete_array(esc_set); - return(false); + return false; } - safe_delete_array(query); safe_delete_array(esc); safe_delete_array(esc_set); @@ -675,48 +675,41 @@ bool BaseGuildManager::DBSetGuildMOTD(uint32 guild_id, const char* motd, const c info->motd = motd; //update our local record. info->motd_setter = setter; //update our local record. - return(true); + return true; } bool BaseGuildManager::DBSetGuildURL(uint32 GuildID, const char* URL) { if(m_db == nullptr) - return(false); - - std::map::const_iterator res; - - res = m_guilds.find(GuildID); + return false; + auto res = m_guilds.find(GuildID); if(res == m_guilds.end()) - return(false); + return false; GuildInfo *info = res->second; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //escape our strings. uint32 len = strlen(URL); - char *esc = new char[len*2+1]; - m_db->DoEscapeString(esc, URL, len); - if (!m_db->RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET url='%s' WHERE id=%d", esc, GuildID), errbuf)) + std::string query = StringFormat("UPDATE guilds SET url='%s' WHERE id=%d", esc, GuildID); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) { - _log(GUILDS__ERROR, "Error setting URL for guild %d '%s': %s", GuildID, query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error setting URL for guild %d '%s': %s", GuildID, query.c_str(), results.ErrorMessage().c_str()); safe_delete_array(esc); return(false); } - safe_delete_array(query); safe_delete_array(esc); _log(GUILDS__DB, "Set URL for guild %d in the database", GuildID); info->url = URL; //update our local record. - return(true); + return true; } bool BaseGuildManager::DBSetGuildChannel(uint32 GuildID, const char* Channel) @@ -724,33 +717,27 @@ bool BaseGuildManager::DBSetGuildChannel(uint32 GuildID, const char* Channel) if(m_db == nullptr) return(false); - std::map::const_iterator res; - - res = m_guilds.find(GuildID); + auto res = m_guilds.find(GuildID); if(res == m_guilds.end()) return(false); GuildInfo *info = res->second; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //escape our strings. uint32 len = strlen(Channel); - char *esc = new char[len*2+1]; - m_db->DoEscapeString(esc, Channel, len); - if (!m_db->RunQuery(query, MakeAnyLenString(&query, "UPDATE guilds SET channel='%s' WHERE id=%d", esc, GuildID), errbuf)) + std::string query = StringFormat("UPDATE guilds SET channel='%s' WHERE id=%d", esc, GuildID); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) { - _log(GUILDS__ERROR, "Error setting Channel for guild %d '%s': %s", GuildID, query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error setting Channel for guild %d '%s': %s", GuildID, query.c_str(), results.ErrorMessage().c_str()); safe_delete_array(esc); return(false); } - safe_delete_array(query); safe_delete_array(esc); _log(GUILDS__DB, "Set Channel for guild %d in the database", GuildID); @@ -766,174 +753,137 @@ bool BaseGuildManager::DBSetGuild(uint32 charid, uint32 guild_id, uint8 rank) { return(false); } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query; if(guild_id != GUILD_NONE) { - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "REPLACE INTO guild_members (char_id,guild_id,rank) VALUES(%d,%d,%d)", - charid, guild_id, rank), errbuf)) - { - _log(GUILDS__ERROR, "Error Changing char %d to guild %d '%s': %s", charid, guild_id, query, errbuf); - safe_delete_array(query); - return(false); + 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()) { + _log(GUILDS__ERROR, "Error Changing char %d to guild %d '%s': %s", charid, guild_id, query.c_str(), results.ErrorMessage().c_str()); + return false; } + } else { - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM guild_members WHERE char_id=%d", - charid), errbuf)) + query = StringFormat("DELETE FROM guild_members WHERE char_id=%d", charid); + auto results = m_db->QueryDatabase(query); + if (!results.Success()) { - _log(GUILDS__ERROR, "Error removing char %d from guild '%s': %s", charid, guild_id, query, errbuf); - safe_delete_array(query); - return(false); + _log(GUILDS__ERROR, "Error removing char %d from guild '%s': %s", charid, guild_id, query.c_str(), results.ErrorMessage().c_str()); + return false; } - } - safe_delete_array(query); - + } _log(GUILDS__DB, "Set char %d to guild %d and rank %d in the database.", charid, guild_id, rank); - - return(true); + return true; } bool BaseGuildManager::DBSetGuildRank(uint32 charid, uint8 rank) { - char *query = 0; - return(_RunQuery(query, MakeAnyLenString(&query, - "UPDATE guild_members SET rank=%d WHERE char_id=%d", - rank, charid), "setting a guild member's rank")); + std::string query = StringFormat("UPDATE guild_members SET rank=%d WHERE char_id=%d", rank, charid); + return(QueryWithLogging(query, "setting a guild member's rank")); } bool BaseGuildManager::DBSetBankerFlag(uint32 charid, bool is_banker) { - char *query = 0; - return(_RunQuery(query, MakeAnyLenString(&query, - "UPDATE guild_members SET banker=%d WHERE char_id=%d", - is_banker?1:0, charid), "setting a guild member's banker flag")); + std::string query = StringFormat("UPDATE guild_members SET banker=%d WHERE char_id=%d", + is_banker? 1: 0, charid); + return(QueryWithLogging(query, "setting a guild member's banker flag")); } bool BaseGuildManager::GetBankerFlag(uint32 CharID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(!m_db) return false; - if(!m_db->RunQuery(query, MakeAnyLenString(&query, "select `banker` from `guild_members` where char_id=%i LIMIT 1", CharID), errbuf, &result)) + std::string query = StringFormat("select `banker` from `guild_members` where char_id=%i LIMIT 1", CharID); + auto results = m_db->QueryDatabase(query); + if(!results.Success()) { - _log(GUILDS__ERROR, "Error retrieving banker flag '%s': %s", query, errbuf); - - safe_delete_array(query); - + _log(GUILDS__ERROR, "Error retrieving banker flag '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - if(mysql_num_rows(result) != 1) + if(results.RowCount() != 1) return false; - row = mysql_fetch_row(result); + auto row = results.begin(); bool IsBanker = atoi(row[0]); - mysql_free_result(result); - return IsBanker; } bool BaseGuildManager::DBSetAltFlag(uint32 charid, bool is_alt) { - char *query = 0; + std::string query = StringFormat("UPDATE guild_members SET alt=%d WHERE char_id=%d", + is_alt ? 1: 0, charid); - return(_RunQuery(query, MakeAnyLenString(&query, - "UPDATE guild_members SET alt=%d WHERE char_id=%d", - is_alt?1:0, charid), "setting a guild member's alt flag")); + return(QueryWithLogging(query, "setting a guild member's alt flag")); } bool BaseGuildManager::GetAltFlag(uint32 CharID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(!m_db) + if(!m_db) return false; - if(!m_db->RunQuery(query, MakeAnyLenString(&query, "select `alt` from `guild_members` where char_id=%i LIMIT 1", CharID), errbuf, &result)) + std::string query = StringFormat("SELECT `alt` FROM `guild_members` WHERE char_id=%i LIMIT 1", CharID); + auto results = m_db->QueryDatabase(query); + if(!results.Success()) { - _log(GUILDS__ERROR, "Error retrieving alt flag '%s': %s", query, errbuf); - - safe_delete_array(query); - + _log(GUILDS__ERROR, "Error retrieving alt flag '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - if(mysql_num_rows(result) != 1) + if(results.RowCount() != 1) return false; - row = mysql_fetch_row(result); + auto row = results.begin(); bool IsAlt = atoi(row[0]); - mysql_free_result(result); - return IsAlt; } bool BaseGuildManager::DBSetTributeFlag(uint32 charid, bool enabled) { - char *query = 0; - return(_RunQuery(query, MakeAnyLenString(&query, - "UPDATE guild_members SET tribute_enable=%d WHERE char_id=%d", - enabled?1:0, charid), "setting a guild member's tribute flag")); + std::string query = StringFormat("UPDATE guild_members SET tribute_enable=%d WHERE char_id=%d", + enabled ? 1: 0, charid); + return(QueryWithLogging(query, "setting a guild member's tribute flag")); } bool BaseGuildManager::DBSetPublicNote(uint32 charid, const char* note) { if(m_db == nullptr) return(false); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //escape our strings. uint32 len = strlen(note); char *esc = new char[len*2+1]; m_db->DoEscapeString(esc, note, len); //insert the new `guilds` entry - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - "UPDATE guild_members SET public_note='%s' WHERE char_id=%d", - esc, charid), errbuf)) - { - _log(GUILDS__ERROR, "Error setting public note for char %d '%s': %s", charid, query, errbuf); - safe_delete_array(query); - safe_delete_array(esc); - return(false); - } - safe_delete_array(query); + std::string query = StringFormat("UPDATE guild_members SET public_note='%s' WHERE char_id=%d", esc, charid); safe_delete_array(esc); + auto results = m_db->QueryDatabase(query); + + if (!results.Success()) + { + _log(GUILDS__ERROR, "Error setting public note for char %d '%s': %s", charid, query.c_str(), results.ErrorMessage().c_str()); + return false; + } _log(GUILDS__DB, "Set public not for char %d", charid); - return(true); + return true; } -bool BaseGuildManager::_RunQuery(char *&query, int len, const char *errmsg) { +bool BaseGuildManager::QueryWithLogging(std::string query, const char *errmsg) { if(m_db == nullptr) return(false); - char errbuf[MYSQL_ERRMSG_SIZE]; + auto results = m_db->QueryDatabase(query); - if (!m_db->RunQuery(query, len, errbuf)) + if (!results.Success()) { - _log(GUILDS__ERROR, "Error %s: '%s': %s", errmsg, query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error %s: '%s': %s", errmsg, query.c_str(), results.ErrorMessage().c_str()); return(false); } - safe_delete_array(query); return(true); } @@ -947,12 +897,12 @@ bool BaseGuildManager::_RunQuery(char *&query, int len, 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(MYSQL_ROW &row, CharGuildInfo &into) { +static void ProcessGuildMember(MySQLRequestRow row, CharGuildInfo &into) { //fields from `characer_` into.char_id = atoi(row[0]); into.char_name = row[1]; @@ -985,31 +935,23 @@ bool BaseGuildManager::GetEntireGuild(uint32 guild_id, std::vectorRunQuery(query, MakeAnyLenString(&query, - GuildMemberBaseQuery " WHERE g.guild_id=%d", guild_id - ), errbuf, &result)) { - _log(GUILDS__ERROR, "Error loading guild member list '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); + std::string query = StringFormat(GuildMemberBaseQuery " WHERE g.guild_id=%d", guild_id); + auto results = m_db->QueryDatabase(query); + if (!results.Success()) { + _log(GUILDS__ERROR, "Error loading guild member list '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { + for (auto row = results.begin(); row != results.end(); ++row) { CharGuildInfo *ci = new CharGuildInfo; ProcessGuildMember(row, *ci); members.push_back(ci); } - mysql_free_result(result); _log(GUILDS__DB, "Retreived entire guild member list for guild %d from the database", guild_id); - return(true); + return true; } bool BaseGuildManager::GetCharInfo(const char *char_name, CharGuildInfo &into) { @@ -1018,38 +960,28 @@ bool BaseGuildManager::GetCharInfo(const char *char_name, CharGuildInfo &into) { return(false); } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - //escape our strings. uint32 nl = strlen(char_name); char *esc = new char[nl*2+1]; m_db->DoEscapeString(esc, char_name, nl); //load up the rank info for each guild. - if (!m_db->RunQuery(query, MakeAnyLenString(&query, - GuildMemberBaseQuery " WHERE c.name='%s'", esc - ), errbuf, &result)) { - _log(GUILDS__ERROR, "Error loading guild member '%s': %s", query, errbuf); - safe_delete_array(query); - safe_delete_array(esc); - return(false); + std::string query = StringFormat(GuildMemberBaseQuery " WHERE c.name='%s'", esc); + safe_delete_array(esc); + auto results = m_db->QueryDatabase(query); + if (!results.Success()) { + _log(GUILDS__ERROR, "Error loading guild member '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - safe_delete_array(esc); - bool ret = true; - if ((row = mysql_fetch_row(result))) { - ProcessGuildMember(row, into); - _log(GUILDS__DB, "Retreived guild member info for char %s from the database", char_name); - } else { - ret = true; - } - mysql_free_result(result); + if (results.RowCount() == 0) + return false; - return(ret); + auto row = results.begin(); + ProcessGuildMember(row, into); + _log(GUILDS__DB, "Retreived guild member info for char %s from the database", char_name); + + return true; } @@ -1057,38 +989,30 @@ bool BaseGuildManager::GetCharInfo(const char *char_name, CharGuildInfo &into) { bool BaseGuildManager::GetCharInfo(uint32 char_id, CharGuildInfo &into) { if(m_db == nullptr) { _log(GUILDS__DB, "Requested char info on %d when we have no database object.", char_id); - return(false); + return false; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - //load up the rank info for each guild. - if (!m_db->RunQuery(query, MakeAnyLenString(&query, + std::string query; #ifdef BOTS - GuildMemberBaseQuery " WHERE c.id=%d AND c.mobtype = 'C'", char_id + query = StringFormat(GuildMemberBaseQuery " WHERE c.id=%d AND c.mobtype = 'C'", char_id); #else - GuildMemberBaseQuery " WHERE c.id=%d", char_id + query = StringFormat(GuildMemberBaseQuery " WHERE c.id=%d", char_id); #endif - ), errbuf, &result)) { - _log(GUILDS__ERROR, "Error loading guild member '%s': %s", query, errbuf); - safe_delete_array(query); - return(false); + auto results = m_db->QueryDatabase(query); + if (!results.Success()) { + _log(GUILDS__ERROR, "Error loading guild member '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - bool ret = true; - if ((row = mysql_fetch_row(result))) { - ProcessGuildMember(row, into); - _log(GUILDS__DB, "Retreived guild member info for char %d", char_id); - } else { - ret = true; - } - mysql_free_result(result); + if (results.RowCount() == 0) + return false; - return(ret); + auto row = results.begin(); + ProcessGuildMember(row, into); + _log(GUILDS__DB, "Retreived guild member info for char %d", char_id); + + return true; } @@ -1316,252 +1240,19 @@ BaseGuildManager::GuildInfo::GuildInfo() { uint32 BaseGuildManager::DoesAccountContainAGuildLeader(uint32 AccountID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - - if (!m_db->RunQuery(query, - MakeAnyLenString(&query, - "select guild_id from guild_members where char_id in (select id from character_ where account_id = %i) and rank = 2", - AccountID), errbuf, &result)) + std::string query = StringFormat("SELECT guild_id FROM guild_members WHERE char_id IN " + "(SELECT id FROM `character_data` WHERE account_id = %i) AND rank = 2", + AccountID); + auto results = m_db->QueryDatabase(query); + if (!results.Success()) { - _log(GUILDS__ERROR, "Error executing query '%s': %s", query, errbuf); - safe_delete_array(query); + _log(GUILDS__ERROR, "Error executing query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return 0; } - safe_delete_array(query); - uint32 Rows = mysql_num_rows(result); - mysql_free_result(result); - - return Rows; -} - - -/* - -bool Database::LoadGuilds(GuildRanks_Struct* guilds) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - // int i; - MYSQL_RES *result; - MYSQL_ROW row; - - for (int a = 0; a < 512; a++) { - guilds[a].leader = 0; - guilds[a].databaseID = 0; - memset(guilds[a].name, 0, sizeof(guilds[a].name)); - for (int i = 0; i <= GUILD_MAX_RANK; i++) { - snprintf(guilds[a].rank[i].rankname, 100, "Guild Rank %i", i); - if (i == 0) { - guilds[a].rank[i].heargu = 1; - guilds[a].rank[i].speakgu = 1; - guilds[a].rank[i].invite = 1; - guilds[a].rank[i].remove = 1; - guilds[a].rank[i].promote = 1; - guilds[a].rank[i].demote = 1; - guilds[a].rank[i].motd = 1; - guilds[a].rank[i].warpeace = 1; - } - else { - guilds[a].rank[i].heargu = 0; - guilds[a].rank[i].speakgu = 0; - guilds[a].rank[i].invite = 0; - guilds[a].rank[i].remove = 0; - guilds[a].rank[i].promote = 0; - guilds[a].rank[i].demote = 0; - guilds[a].rank[i].motd = 0; - guilds[a].rank[i].warpeace = 0; - } - } - Sleep(0); - } - - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, eqid, name, leader, minstatus, rank0title, rank1, rank1title, rank2, rank2title, rank3, rank3title, rank4, rank4title, rank5, rank5title from guilds"), errbuf, &result)) { - - safe_delete_array(query); - uint32 guildeqid = 0xFFFFFFFF; - while ((row = mysql_fetch_row(result))) { - guildeqid = atoi(row[1]); - if (guildeqid < 512) { - guilds[guildeqid].leader = atoi(row[3]); - guilds[guildeqid].databaseID = atoi(row[0]); - guilds[guildeqid].minstatus = atoi(row[4]); - strcpy(guilds[guildeqid].name, row[2]); - for (int i = 0; i <= GUILD_MAX_RANK; i++) { - strcpy(guilds[guildeqid].rank[i].rankname, row[5 + (i*2)]); - if (i == 0) { - guilds[guildeqid].rank[i].heargu = 1; - guilds[guildeqid].rank[i].speakgu = 1; - guilds[guildeqid].rank[i].invite = 1; - guilds[guildeqid].rank[i].remove = 1; - guilds[guildeqid].rank[i].promote = 1; - guilds[guildeqid].rank[i].demote = 1; - guilds[guildeqid].rank[i].motd = 1; - guilds[guildeqid].rank[i].warpeace = 1; - } - else if (strlen(row[4 + (i*2)]) >= 8) { - guilds[guildeqid].rank[i].heargu = (row[4 + (i*2)][GUILD_HEAR] == '1'); - guilds[guildeqid].rank[i].speakgu = (row[4 + (i*2)][GUILD_SPEAK] == '1'); - guilds[guildeqid].rank[i].invite = (row[4 + (i*2)][GUILD_INVITE] == '1'); - guilds[guildeqid].rank[i].remove = (row[4 + (i*2)][GUILD_REMOVE] == '1'); - guilds[guildeqid].rank[i].promote = (row[4 + (i*2)][GUILD_PROMOTE] == '1'); - guilds[guildeqid].rank[i].demote = (row[4 + (i*2)][GUILD_DEMOTE] == '1'); - guilds[guildeqid].rank[i].motd = (row[4 + (i*2)][GUILD_MOTD] == '1'); - guilds[guildeqid].rank[i].warpeace = (row[4 + (i*2)][GUILD_WARPEACE] == '1'); - } - else { - - guilds[guildeqid].rank[i].heargu = 1; - guilds[guildeqid].rank[i].speakgu = 1; - guilds[guildeqid].rank[i].invite = 0; - - guilds[guildeqid].rank[i].remove = 0; - guilds[guildeqid].rank[i].promote = 0; - guilds[guildeqid].rank[i].demote = 0; - guilds[guildeqid].rank[i].motd = 0; - guilds[guildeqid].rank[i].warpeace = 0; - } - - if (guilds[guildeqid].rank[i].rankname[0] == 0) - snprintf(guilds[guildeqid].rank[i].rankname, 100, "Guild Rank %i", i); - } - } - Sleep(0); - } - mysql_free_result(result); - return true; - } - else - { - cerr << "Error in LoadGuilds query '" << query << "' " << errbuf << endl; - safe_delete_array(query); - return false; - } - - return false; -} - - -void Database::SetPublicNote(uint32 guild_id,char* charname, char* note){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char* notebuf = new char[(strlen(note)*2)+3]; - DoEscapeString(notebuf, note, strlen(note)) ; - if (!RunQuery(query, MakeAnyLenString(&query, "update character_ set publicnote='%s' where name='%s' and guild=%i", notebuf,charname,guild_id), errbuf)) { - cerr << "Error running SetPublicNote query: " << errbuf << endl; - } - safe_delete_array(query); - safe_delete_array(notebuf); -} - - - -bool Database::GetGuildRanks(uint32 guildeqid, GuildRanks_Struct* gr) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, eqid, name, leader, minstatus, rank0title, rank1, rank1title, rank2, rank2title, rank3, rank3title, rank4, rank4title, rank5, rank5title from guilds where eqid=%i;", guildeqid), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - gr->leader = atoi(row[3]); - gr->databaseID = atoi(row[0]); - gr->minstatus = atoi(row[4]); - strcpy(gr->name, row[2]); - for (int i = 0; i <= GUILD_MAX_RANK; i++) { - strcpy(gr->rank[i].rankname, row[5 + (i*2)]); - if (i == 0) { - gr->rank[i].heargu = 1; - gr->rank[i].speakgu = 1; - gr->rank[i].invite = 1; - gr->rank[i].remove = 1; - gr->rank[i].promote = 1; - gr->rank[i].demote = 1; - gr->rank[i].motd = 1; - gr->rank[i].warpeace = 1; - } - else if (strlen(row[4 + (i*2)]) >= 8) { - gr->rank[i].heargu = (row[4 + (i*2)][GUILD_HEAR] == '1'); - gr->rank[i].speakgu = (row[4 + (i*2)][GUILD_SPEAK] == '1'); - gr->rank[i].invite = (row[4 + (i*2)][GUILD_INVITE] == '1'); - gr->rank[i].remove = (row[4 + (i*2)][GUILD_REMOVE] == '1'); - gr->rank[i].promote = (row[4 + (i*2)][GUILD_PROMOTE] == '1'); - gr->rank[i].demote = (row[4 + (i*2)][GUILD_DEMOTE] == '1'); - gr->rank[i].motd = (row[4 + (i*2)][GUILD_MOTD] == '1'); - gr->rank[i].warpeace = (row[4 + (i*2)][GUILD_WARPEACE] == '1'); - } - else { - gr->rank[i].heargu = 1; - gr->rank[i].speakgu = 1; - gr->rank[i].invite = 0; - gr->rank[i].remove = 0; - gr->rank[i].promote = 0; - gr->rank[i].demote = 0; - gr->rank[i].motd = 0; - gr->rank[i].warpeace = 0; - } - - if (gr->rank[i].rankname[0] == 0) - snprintf(gr->rank[i].rankname, 100, "Guild Rank %i", i); - } - } - else { - gr->leader = 0; - gr->databaseID = 0; - gr->minstatus = 0; - memset(gr->name, 0, sizeof(gr->name)); - for (int i = 0; i <= GUILD_MAX_RANK; i++) { - snprintf(gr->rank[i].rankname, 100, "Guild Rank %i", i); - if (i == 0) { - gr->rank[i].heargu = 1; - gr->rank[i].speakgu = 1; - gr->rank[i].invite = 1; - gr->rank[i].remove = 1; - gr->rank[i].promote = 1; - gr->rank[i].demote = 1; - gr->rank[i].motd = 1; - gr->rank[i].warpeace = 1; - } - else { - gr->rank[i].heargu = 0; - gr->rank[i].speakgu = 0; - gr->rank[i].invite = 0; - gr->rank[i].remove = 0; - gr->rank[i].promote = 0; - gr->rank[i].demote = 0; - gr->rank[i].motd = 0; - - gr->rank[i].warpeace = 0; - } - } - } - mysql_free_result(result); - return true; - } - else { - cerr << "Error in GetGuildRank query '" << query << "' " << errbuf << endl; - safe_delete_array(query); - return false; - } - - return false; + return results.RowCount(); } - - - -*/ - - - - - - diff --git a/common/guild_base.h b/common/guild_base.h index f27afb3d0..b59244480 100644 --- a/common/guild_base.h +++ b/common/guild_base.h @@ -54,6 +54,7 @@ public: bool SetGuild(uint32 charid, uint32 guild_id, uint8 rank); bool SetGuildRank(uint32 charid, uint8 rank); bool SetBankerFlag(uint32 charid, bool is_banker); + bool ForceRankUpdate(uint32 charid); bool GetAltFlag(uint32 CharID); bool SetAltFlag(uint32 charid, bool is_alt); bool GetBankerFlag(uint32 CharID); @@ -108,7 +109,7 @@ protected: bool DBSetAltFlag(uint32 charid, bool is_alt); bool DBSetTributeFlag(uint32 charid, bool enabled); bool DBSetPublicNote(uint32 charid, const char *note); - bool _RunQuery(char *&query, int len, const char *errmsg); + bool QueryWithLogging(std::string query, const char *errmsg); // void DBSetPublicNote(uint32 guild_id,char* charname, char* note); bool LocalDeleteGuild(uint32 guild_id); diff --git a/common/guilds.cpp b/common/guilds.cpp index 22b2ca240..ddd5d9a59 100644 --- a/common/guilds.cpp +++ b/common/guilds.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "MiscFunctions.h" +#include "misc_functions.h" #include "guilds.h" #include "database.h" #include "eq_packet_structs.h" @@ -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 similarity index 87% rename from common/Item.cpp rename to common/item.cpp index ff02d1574..f92bc6a67 100644 --- a/common/Item.cpp +++ b/common/item.cpp @@ -17,8 +17,8 @@ */ #include "debug.h" -#include "StringUtil.h" -#include "Item.h" +#include "string_util.h" +#include "item.h" #include "database.h" #include "misc.h" #include "races.h" @@ -654,6 +654,110 @@ int16 Inventory::FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size, boo return INVALID_INDEX; } +// This is a mix of HasSpaceForItem and FindFreeSlot..due to existing coding behavior, it was better to add a new helper function... +int16 Inventory::FindFreeSlotForTradeItem(const ItemInst* inst) { + // Do not arbitrarily use this function..it is designed for use with Client::ResetTrade() and Client::FinishTrade(). + // If you have a need, use it..but, understand it is not a compatible replacement for Inventory::FindFreeSlot(). + // + // I'll probably implement a bitmask in the new inventory system to avoid having to adjust stack bias -U + + if (!inst || !inst->GetID()) + return INVALID_INDEX; + + // step 1: find room for bags (caller should really ask for slots for bags first to avoid sending them to cursor..and bag item loss) + if (inst->IsType(ItemClassContainer)) { + for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) + if (!m_inv[free_slot]) + return free_slot; + + return MainCursor; // return cursor since bags do not stack and will not fit inside other bags..yet...) + } + + // step 2: find partial room for stackables + if (inst->IsStackable()) { + for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) { + const ItemInst* main_inst = m_inv[free_slot]; + + if (!main_inst) + continue; + + if ((main_inst->GetID() == inst->GetID()) && (main_inst->GetCharges() < main_inst->GetItem()->StackSize)) + return free_slot; + } + + for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) { + const ItemInst* main_inst = m_inv[free_slot]; + + if (!main_inst) + continue; + + if (main_inst->IsType(ItemClassContainer)) { // if item-specific containers already have bad items, we won't fix it here... + for (uint8 free_bag_slot = SUB_BEGIN; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++free_bag_slot) { + const ItemInst* sub_inst = main_inst->GetItem(free_bag_slot); + + if (!sub_inst) + continue; + + if ((sub_inst->GetID() == inst->GetID()) && (sub_inst->GetCharges() < sub_inst->GetItem()->StackSize)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + } + } + + // step 3a: find room for container-specific items (ItemClassArrow) + if (inst->GetItem()->ItemType == ItemTypeArrow) { + for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) { + const ItemInst* main_inst = m_inv[free_slot]; + + if (!main_inst || (main_inst->GetItem()->BagType != BagTypeQuiver) || !main_inst->IsType(ItemClassContainer)) + continue; + + for (uint8 free_bag_slot = SUB_BEGIN; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++free_bag_slot) + if (!main_inst->GetItem(free_bag_slot)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + + // step 3b: find room for container-specific items (ItemClassSmallThrowing) + if (inst->GetItem()->ItemType == ItemTypeSmallThrowing) { + for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) { + const ItemInst* main_inst = m_inv[free_slot]; + + if (!main_inst || (main_inst->GetItem()->BagType != BagTypeBandolier) || !main_inst->IsType(ItemClassContainer)) + continue; + + for (uint8 free_bag_slot = SUB_BEGIN; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++free_bag_slot) + if (!main_inst->GetItem(free_bag_slot)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + + // step 4: just find an empty slot + for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) { + const ItemInst* main_inst = m_inv[free_slot]; + + if (!main_inst) + return free_slot; + } + + for (int16 free_slot = EmuConstants::GENERAL_BEGIN; free_slot <= EmuConstants::GENERAL_END; ++free_slot) { + const ItemInst* main_inst = m_inv[free_slot]; + + if (main_inst && main_inst->IsType(ItemClassContainer)) { + if ((main_inst->GetItem()->BagSize < inst->GetItem()->Size) || (main_inst->GetItem()->BagType == BagTypeBandolier) || (main_inst->GetItem()->BagType == BagTypeQuiver)) + continue; + + for (uint8 free_bag_slot = SUB_BEGIN; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++free_bag_slot) + if (!main_inst->GetItem(free_bag_slot)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + + //return INVALID_INDEX; // everything else pushes to the cursor + return MainCursor; +} + // Opposite of below: Get parent bag slot_id from a slot inside of bag int16 Inventory::CalcSlotId(int16 slot_id) { int16 parent_slot_id = INVALID_INDEX; @@ -807,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) { @@ -1284,6 +1412,8 @@ ItemInst::ItemInst(const Item_Struct* item, int16 charges) { m_scaledItem = nullptr; m_evolveInfo = nullptr; m_scaling = false; + m_ornamenticon = 0; + m_ornamentidfile = 0; } ItemInst::ItemInst(SharedDatabase *db, uint32 item_id, int16 charges) { @@ -1306,6 +1436,8 @@ ItemInst::ItemInst(SharedDatabase *db, uint32 item_id, int16 charges) { m_scaledItem = nullptr; m_evolveInfo = nullptr; m_scaling = false; + m_ornamenticon = 0; + m_ornamentidfile = 0; } ItemInst::ItemInst(ItemInstTypes use_type) { @@ -1323,6 +1455,8 @@ ItemInst::ItemInst(ItemInstTypes use_type) { m_scaledItem = nullptr; m_evolveInfo = nullptr; m_scaling = false; + m_ornamenticon = 0; + m_ornamentidfile = 0; } // Make a copy of an ItemInst object @@ -1373,6 +1507,8 @@ ItemInst::ItemInst(const ItemInst& copy) m_evolveInfo = nullptr; m_scaling = copy.m_scaling; + m_ornamenticon = copy.m_ornamenticon; + m_ornamentidfile = copy.m_ornamentidfile; } // Clean up container contents @@ -1476,6 +1612,16 @@ int8 ItemInst::AvailableAugmentSlot(int32 augtype) const return (i < EmuConstants::ITEM_COMMON_SIZE) ? i : INVALID_INDEX; } +bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const { + if (m_item->ItemClass != ItemClassCommon || !m_item) + return false; + + if ((!GetItem(slot) && m_item->AugSlotVisible[slot]) && augtype == -1 || (m_item->AugSlotType[slot] && ((1 << (m_item->AugSlotType[slot] - 1)) & augtype))) { + return true; + } + return false; +} + // Retrieve item inside container ItemInst* ItemInst::GetItem(uint8 index) const { @@ -1636,6 +1782,67 @@ ItemInst* ItemInst::GetAugment(uint8 slot) const return nullptr; } +ItemInst* ItemInst::GetOrnamentationAug(int ornamentationAugtype) const +{ + for (int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + if (GetAugment(i) && m_item->AugSlotType[i] == ornamentationAugtype) { + const char *item_IDFile = GetAugment(i)->GetItem()->IDFile; + if (strncmp(item_IDFile, "IT64", strlen(item_IDFile)) == 0 || strncmp(item_IDFile, "IT63", strlen(item_IDFile)) == 0) + continue; + + return this->GetAugment(i); + } + } + + return nullptr; +} + +bool ItemInst::CanTransform(const Item_Struct *ItemToTry, const Item_Struct *Container, bool AllowAll) { + if (!ItemToTry || !Container) return false; + + if (ItemToTry->ItemType == ItemTypeArrow || strnlen(Container->CharmFile, 30) == 0) + return false; + + if (AllowAll && strncasecmp(Container->CharmFile, "ITEMTRANSFIGSHIELD", 18) && strncasecmp(Container->CharmFile, "ITEMTransfigBow", 15)) { + switch (ItemToTry->ItemType) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 35: + case 45: + return true; + } + } + + static std::map types; + types["itemtransfig1hp"] = 2; + types["itemtransfig1hs"] = 0; + types["itemtransfig2hb"] = 4; + types["itemtransfig2hp"] = 35; + types["itemtransfig2hs"] = 1; + types["itemtransfigblunt"] = 3; + types["itemtransfigbow"] = 5; + types["itemtransfighth"] = 45; + types["itemtransfigshield"] = 8; + types["itemtransfigslashing"] = 0; + + auto i = types.find(MakeLowerString(Container->CharmFile)); + if (i != types.end() && i->second == ItemToTry->ItemType) + return true; + + static std::map typestwo; + typestwo["itemtransfigblunt"] = 4; + typestwo["itemtransfigslashing"] = 1; + + i = typestwo.find(MakeLowerString(Container->CharmFile)); + if (i != typestwo.end() && i->second == ItemToTry->ItemType) + return true; + + return false; +} + uint32 ItemInst::GetAugmentItemID(uint8 slot) const { uint32 id = NO_ITEM; diff --git a/common/Item.h b/common/item.h similarity index 91% rename from common/Item.h rename to common/item.h index ef59ddd6f..cce166eb0 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); } @@ -172,6 +186,7 @@ public: // Locate an available inventory slot int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false); + int16 FindFreeSlotForTradeItem(const ItemInst* inst); // Calculate slot_id for an item within a bag static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id @@ -182,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); @@ -228,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; @@ -273,6 +297,7 @@ public: inline bool IsAugmentable() const { return m_item->AugSlotType[0]!=0 || m_item->AugSlotType[1]!=0 || m_item->AugSlotType[2]!=0 || m_item->AugSlotType[3]!=0 || m_item->AugSlotType[4]!=0; } bool AvailableWearSlot(uint32 aug_wear_slots) const; int8 AvailableAugmentSlot(int32 augtype) const; + bool IsAugmentSlotAvailable(int32 augtype, uint8 slot) const; inline int32 GetAugmentType() const { return m_item->AugType; } inline bool IsExpendable() const { return ((m_item->Click.Type == ET_Expendable ) || (m_item->ItemType == ItemTypePotion)); } @@ -304,7 +329,9 @@ public: void DeleteAugment(uint8 slot); ItemInst* RemoveAugment(uint8 index); bool IsAugmented(); - + ItemInst* GetOrnamentationAug(int ornamentationAugtype) const; + static bool CanTransform(const Item_Struct *ItemToTry, const Item_Struct *Container, bool AllowAll = false); + // Has attack/delay? bool IsWeapon() const; bool IsAmmo() const; @@ -366,6 +393,10 @@ public: void SetActivated(bool activated) { m_activated = activated; } int8 GetEvolveLvl() const { return m_evolveLvl; } void SetScaling(bool v) { m_scaling = v; } + uint32 GetOrnamentationIcon() const { return m_ornamenticon; } + void SetOrnamentIcon(uint32 ornament_icon) { m_ornamenticon = ornament_icon; } + uint32 GetOrnamentationIDFile() const { return m_ornamentidfile; } + void SetOrnamentationIDFile(uint32 ornament_idfile) { m_ornamentidfile = ornament_idfile; } void Initialize(SharedDatabase *db = nullptr); void ScaleItem(); @@ -410,6 +441,8 @@ protected: Item_Struct* m_scaledItem; EvolveInfo* m_evolveInfo; bool m_scaling; + uint32 m_ornamenticon; + uint32 m_ornamentidfile; // // Items inside of this item (augs or contents); diff --git a/common/item_fieldlist.h b/common/item_fieldlist.h index c4d54d70f..fb3e4b41e 100644 --- a/common/item_fieldlist.h +++ b/common/item_fieldlist.h @@ -169,6 +169,7 @@ F(bardlevel) F(questitemflag) F(svcorruption) F(purity) +F(evolvinglevel) F(backstabdmg) F(dsmitigation) F(heroic_str) diff --git a/common/item_struct.h b/common/item_struct.h index c91634a6e..26d3623bd 100644 --- a/common/item_struct.h +++ b/common/item_struct.h @@ -217,6 +217,7 @@ struct Item_Struct { // Begin SoF Fields int32 SVCorruption; uint32 Purity; + uint8 EvolvingLevel; uint32 BackstabDmg; uint32 DSMitigation; int32 HeroicStr; diff --git a/common/logsys.cpp b/common/logsys.cpp index 7f04d01b8..2b90c8b4a 100644 --- a/common/logsys.cpp +++ b/common/logsys.cpp @@ -22,7 +22,7 @@ #include #include #include "misc.h" -#include "EQPacket.h" +#include "eq_packet.h" #define LOG_CATEGORY(category) #category , diff --git a/common/logsys.h b/common/logsys.h index 3933e7062..84741cdb4 100644 --- a/common/logsys.h +++ b/common/logsys.h @@ -92,8 +92,8 @@ extern void log_raw_packet(LogType type, uint16 seq, const BasePacket *p); class Mob; extern void log_message_mob(LogType type, Mob *who, const char *fmt, ...); #define mlog( type, format, ...) \ - if(IsLoggingEnabled()) \ do { \ + if(IsLoggingEnabled()) \ if(log_type_info[ type ].enabled) { \ log_message_mob(type, this, format, ##__VA_ARGS__); \ } \ @@ -150,16 +150,16 @@ extern void log_raw_packet(LogType type, uint16 seq, const BasePacket *p); class Mob; extern void log_hex_mob(LogType type, Mob *who, const char *data, uint32 length); #define mhex( type, data, len) \ - if(IsLoggingEnabled()) \ do { \ + if(IsLoggingEnabled()) \ if(log_type_info[ type ].enabled) { \ log_hex_mob(type, this, data, len); \ } \ } while(false) extern void log_packet_mob(LogType type, Mob *who, const BasePacket *p); #define mpkt( type, packet) \ - if(IsLoggingEnabled()) \ do { \ + if(IsLoggingEnabled()) \ if(log_type_info[ type ].enabled) { \ log_packet_mob(type, this, packet); \ } \ diff --git a/common/logsys_eqemu.cpp b/common/logsys_eqemu.cpp index e1e75996c..b5f846b4a 100644 --- a/common/logsys_eqemu.cpp +++ b/common/logsys_eqemu.cpp @@ -18,7 +18,7 @@ #include "debug.h" #include "logsys.h" -#include "StringUtil.h" +#include "string_util.h" #include #include diff --git a/common/logtypes.h b/common/logtypes.h index f110f6cc4..c46a333ff 100644 --- a/common/logtypes.h +++ b/common/logtypes.h @@ -60,7 +60,7 @@ LOG_TYPE( UCS, PACKETS, DISABLED) LOG_CATEGORY( QUERYSERV ) LOG_TYPE( QUERYSERV, INIT, ENABLED ) -LOG_TYPE( QUERYSERV, ERROR, ENABLED ) +LOG_TYPE( QUERYSERV, ERROR, ENABLED ) LOG_TYPE( QUERYSERV, CLIENT, DISABLED ) LOG_TYPE( QUERYSERV, TRACE, DISABLED ) LOG_TYPE( QUERYSERV, PACKETS, DISABLED) @@ -108,6 +108,7 @@ LOG_CATEGORY( FACTION ) LOG_CATEGORY( ZONE ) LOG_TYPE( ZONE, GROUND_SPAWNS, DISABLED ) +LOG_TYPE( ZONE, SPAWNS, ENABLED) LOG_TYPE( ZONE, INIT, ENABLED ) LOG_TYPE( ZONE, INIT_ERR, ENABLED ) LOG_TYPE( ZONE, WORLD, ENABLED ) @@ -116,7 +117,7 @@ LOG_TYPE( ZONE, WORLD_TRACE, DISABLED ) LOG_CATEGORY( TASKS ) LOG_TYPE( TASKS, GLOBALLOAD, DISABLED ) -LOG_TYPE( TASKS, CLIENTLOAD, DISABLED ) +LOG_TYPE( TASKS, CLIENTLOAD, DISABLED ) LOG_TYPE( TASKS, UPDATE, DISABLED ) LOG_TYPE( TASKS, CLIENTSAVE, DISABLED ) LOG_TYPE( TASKS, PACKETS, DISABLED ) 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/md5.cpp b/common/md5.cpp index 8e9197e56..724279bba 100644 --- a/common/md5.cpp +++ b/common/md5.cpp @@ -9,7 +9,7 @@ */ #include /* for memcpy() */ #include "../common/md5.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "../common/seperator.h" MD5::MD5() { diff --git a/common/MiscFunctions.cpp b/common/misc_functions.cpp similarity index 95% rename from common/MiscFunctions.cpp rename to common/misc_functions.cpp index cf371adc1..882cc61c7 100644 --- a/common/MiscFunctions.cpp +++ b/common/misc_functions.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "MiscFunctions.h" +#include "misc_functions.h" #include #include #include @@ -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/MiscFunctions.h b/common/misc_functions.h similarity index 98% rename from common/MiscFunctions.h rename to common/misc_functions.h index c057af8d8..abc9747b5 100644 --- a/common/MiscFunctions.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/Mutex.cpp b/common/mutex.cpp similarity index 99% rename from common/Mutex.cpp rename to common/mutex.cpp index 763f10db4..7bea92b0d 100644 --- a/common/Mutex.cpp +++ b/common/mutex.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "../common/Mutex.h" +#include "../common/mutex.h" #include diff --git a/common/Mutex.h b/common/mutex.h similarity index 100% rename from common/Mutex.h rename to common/mutex.h diff --git a/common/MySQLRequestResult.cpp b/common/mysql_request_result.cpp similarity index 93% rename from common/MySQLRequestResult.cpp rename to common/mysql_request_result.cpp index f364bf9b2..e7f147b51 100644 --- a/common/MySQLRequestResult.cpp +++ b/common/mysql_request_result.cpp @@ -1,4 +1,4 @@ -#include "MySQLRequestResult.h" +#include "mysql_request_result.h" MySQLRequestResult::MySQLRequestResult() @@ -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/MySQLRequestResult.h b/common/mysql_request_result.h similarity index 91% rename from common/MySQLRequestResult.h rename to common/mysql_request_result.h index 2983dd4bf..ab84eeb18 100644 --- a/common/MySQLRequestResult.h +++ b/common/mysql_request_result.h @@ -8,7 +8,7 @@ #include #include "types.h" -#include "MySQLRequestRow.h" +#include "mysql_request_row.h" #include #include #include @@ -40,7 +40,7 @@ public: MySQLRequestResult& operator=(MySQLRequestResult&& other); bool Success() const { return m_Success;} - std::string ErrorMessage() const {return std::string(m_ErrorBuffer);} + std::string ErrorMessage() const {return m_ErrorBuffer ? std::string(m_ErrorBuffer) : std::string("");} uint32 ErrorNumber() const {return m_ErrorNumber;} uint32 RowsAffected() const {return m_RowsAffected;} uint32 RowCount() const {return m_RowCount;} diff --git a/common/MySQLRequestRow.cpp b/common/mysql_request_row.cpp similarity index 97% rename from common/MySQLRequestRow.cpp rename to common/mysql_request_row.cpp index f8c2b4c06..9491fd1b2 100644 --- a/common/MySQLRequestRow.cpp +++ b/common/mysql_request_row.cpp @@ -1,4 +1,4 @@ -#include "MySQLRequestRow.h" +#include "mysql_request_row.h" MySQLRequestRow::MySQLRequestRow(const MySQLRequestRow& row) : m_Result(row.m_Result), m_MySQLRow(row.m_MySQLRow) @@ -64,6 +64,5 @@ bool MySQLRequestRow::operator!=(const MySQLRequestRow& rhs) char* MySQLRequestRow::operator[](int index) { - return m_MySQLRow[index]; } diff --git a/common/MySQLRequestRow.h b/common/mysql_request_row.h similarity index 100% rename from common/MySQLRequestRow.h rename to common/mysql_request_row.h 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/opcodemgr.h b/common/opcodemgr.h index 690f5442a..138fadd97 100644 --- a/common/opcodemgr.h +++ b/common/opcodemgr.h @@ -20,7 +20,7 @@ #define OPCODE_MANAGER_H #include "types.h" -#include "Mutex.h" +#include "mutex.h" #include "emu_opcodes.h" #include diff --git a/common/packet_dump.cpp b/common/packet_dump.cpp index a9c70b493..6bb412f6b 100644 --- a/common/packet_dump.cpp +++ b/common/packet_dump.cpp @@ -21,7 +21,7 @@ #include #include "packet_dump.h" -#include "EQPacket.h" +#include "eq_packet.h" #include "../common/servertalk.h" void DumpPacketAscii(const uchar* buf, uint32 size, uint32 cols, uint32 skip) { diff --git a/common/packet_dump_file.cpp b/common/packet_dump_file.cpp index 8e0e3c274..32aa7d984 100644 --- a/common/packet_dump_file.cpp +++ b/common/packet_dump_file.cpp @@ -35,7 +35,7 @@ #include #endif -#include "EQStream.h" +#include "eq_stream.h" #include "packet_dump_file.h" void FileDumpPacketAscii(const char* filename, const uchar* buf, uint32 size, uint32 cols, uint32 skip) { diff --git a/common/patches/Client62.cpp b/common/patches/Client62.cpp deleted file mode 100644 index 8e45217e5..000000000 --- a/common/patches/Client62.cpp +++ /dev/null @@ -1,1089 +0,0 @@ - -#include "../debug.h" -#include "Client62.h" -#include "../opcodemgr.h" -#include "../logsys.h" -#include "../EQStreamIdent.h" -#include "../crc32.h" - -#include "../eq_packet_structs.h" -#include "../MiscFunctions.h" -#include "../StringUtil.h" -#include "../Item.h" -#include "../clientversions.h" -#include "Client62_structs.h" - -namespace Client62 { - -static const char *name = "6.2"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; - -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); - -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); - } -} - - - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "SSRegister.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; -} - -#include "SSDefine.h" - - -/* -// 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 ) - { - 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_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) - { - _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_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->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); - } - - } - - 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_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); - 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_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) - { - 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(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); -} - -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) - { - 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(); -} - -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_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); - } - } - - - *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 -#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) ? "\"" : "" - ); - - for(i=0;i<10;i++) { - if (sub_items[i]) - safe_delete_array(sub_items[i]); - } - - safe_delete_array(instance); - - return serialization; -} - - -} //end namespace Client62 - - - - - - diff --git a/common/patches/RoF.cpp b/common/patches/RoF.cpp deleted file mode 100644 index 7fcce53b2..000000000 --- a/common/patches/RoF.cpp +++ /dev/null @@ -1,5386 +0,0 @@ - -#include "../debug.h" -#include "RoF.h" -#include "../opcodemgr.h" -#include "../logsys.h" -#include "../EQStreamIdent.h" -#include "../crc32.h" - -#include "../eq_packet_structs.h" -#include "../MiscFunctions.h" -#include "../StringUtil.h" -#include "../Item.h" -#include "RoF_structs.h" -#include "../rulesys.h" - -#include -#include - -namespace RoF { - -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); - -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); - } -} - - - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "SSRegister.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; -} - -#include "SSDefine.h" - - -// 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; - - 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); - } - - /* - // 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) - { - 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_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_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 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); - - 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; - 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++) - { - 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(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) - { - 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(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->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 - - outapp->WriteUInt32(64); // Name Length - - uint32 CurrentPosition = outapp->GetWritePosition(); - - outapp->WriteString(emu->name); - - outapp->SetWritePosition(CurrentPosition + 64); - - outapp->WriteUInt32(32); // Last Name Length - - CurrentPosition = outapp->GetWritePosition(); - - outapp->WriteString(emu->last_name); - - outapp->SetWritePosition(CurrentPosition + 32); - - 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->WriteUInt32(structs::MAX_PP_LANGUAGE); - - for(uint32 r = 0; r < MAX_PP_LANGUAGE; r++) - { - outapp->WriteUInt8(emu->languages[r]); - } - - for(uint32 r = 0; r < structs::MAX_PP_LANGUAGE - MAX_PP_LANGUAGE; r++) - { - outapp->WriteUInt8(0); - } - - 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(emu->career_tribute_points); - outapp->WriteUInt32(0); // Unknown - outapp->WriteUInt32(emu->tribute_points); - outapp->WriteUInt32(0); // Unknown - - outapp->WriteUInt8(0); // Unknown - outapp->WriteUInt8(0); // Unknown - - outapp->WriteUInt32(EmuConstants::TRIBUTE_SIZE); - - 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->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 - - // 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(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 - - // Think we need 1 byte of padding at the end - - outapp->WriteUInt8(0); // Unknown - - - _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)); - 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) -{ - // 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) - { - if(emu->spellid[i]) - { - __packet->WriteUInt32(i); - __packet->WriteUInt32(emu->spellid[i]); - __packet->WriteUInt32(emu->ticsremaining[i]); - __packet->WriteUInt32(0); // Unknown - __packet->WriteString(""); - } - } - __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) - { - 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; - - //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; - } - - - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - - emu = (Spawn_Struct *) __emu_buffer; - - //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - - char *Buffer = (char *) in->pBuffer, *BufferStart; - - - int r; - int k; - for(r = 0; r < entrycount; r++, emu++) { - - int PacketSize = 206; - - PacketSize += strlen(emu->name); - PacketSize += strlen(emu->lastName); - - emu->title[0] = 0; - emu->suffix[0] = 0; - - if(strlen(emu->title)) - PacketSize += strlen(emu->title) + 1; - - if(strlen(emu->suffix)) - PacketSize += strlen(emu->suffix) + 1; - - bool ShowName = 1; - if(emu->bodytype >= 66) - { - emu->race = 127; - emu->bodytype = 11; - emu->gender = 0; - ShowName = 0; - } - - float SpawnSize = emu->size; - 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) - { - emu->size = 6; - SpawnSize = 6; - } - } - else - PacketSize += 216; - - if(SpawnSize == 0) - { - SpawnSize = 3; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - Buffer = (char *) outapp->pBuffer; - BufferStart = Buffer; - VARSTRUCT_ENCODE_STRING(Buffer, emu->name); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); - VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - - structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; - - Bitfields->gender = emu->gender; - Bitfields->ispet = emu->is_pet; - Bitfields->afk = emu->afk; - Bitfields->anon = emu->anon; - Bitfields->gm = emu->gm; - Bitfields->sneak = 0; - Bitfields->lfg = emu->lfg; - Bitfields->invis = emu->invis; - Bitfields->linkdead = 0; - Bitfields->showhelm = emu->showhelm; - Bitfields->trader = 0; - Bitfields->targetable = 1; - Bitfields->targetable_with_hotkey = (emu->IsMercenary ? 0 : 1); - Bitfields->showname = ShowName; - - // Not currently found - // Bitfields->statue = 0; - // Bitfields->buyer = 0; - - Buffer += sizeof(structs::Spawn_Struct_Bitfields); - - uint8 OtherData = 0; - - if(strlen(emu->title)) - OtherData = OtherData | 16; - - if(strlen(emu->suffix)) - OtherData = OtherData | 32; - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); - - VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3 - VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 - - // 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. - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype); - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->curHp); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->haircolor); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beardcolor); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor1); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor2); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->hairstyle); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beard); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_heritage); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // unknown8 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11 - - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->walkspeed); - 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) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); - } - else - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); - } - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // pvp - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->StandState); // standstate - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->light); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->flymode); - - VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle ?? - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC ? 0 : 1); // unknown - Must be 1 for guild name to be shown abover players head. - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 - 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)) - { - for(k = 0; k < 9; ++k) - { - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); - } - } - - structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - - for(k = 0; k < 9; k++) { - Equipment[k].equip0 = emu->equipment[k]; - Equipment[k].equip1 = 0; - Equipment[k].equip2 = 0; - Equipment[k].equip3 = 0; - Equipment[k].itemId = 0; - } - - Buffer += (sizeof(structs::EquipStruct) * 9); - } - else - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - } - - - structs::Spawn_Struct_Position *Position = (structs::Spawn_Struct_Position*)Buffer; - - Position->deltaX = emu->deltaX; - Position->deltaHeading = emu->deltaHeading; - Position->deltaY = emu->deltaY; - Position->y = emu->y; - Position->animation = emu->animation; - Position->heading = emu->heading; - Position->x = emu->x; - Position->z = emu->z; - Position->deltaZ = emu->deltaZ; - - Buffer += sizeof(structs::Spawn_Struct_Position); - - if(strlen(emu->title)) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->title); - } - - if(strlen(emu->suffix)) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); - } - - Buffer += 8; - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary); - VARSTRUCT_ENCODE_STRING(Buffer, "0000000000000000"); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); - // 29 zero bytes follow - Buffer += 29; - 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_); - PutFieldN(rank); - 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++) - { - eq->entries[i] = emu->entries[i]; - } - eq->entries[21] = 0; - - 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_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); - } - - 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) - { - 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_InspectRequest) { - ENCODE_LENGTH_EXACT(Inspect_Struct); - SETUP_DIRECT_ENCODE(Inspect_Struct, structs::Inspect_Struct); - OUT(TargetID); - OUT(PlayerID); - 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(); -}*/ - -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; - OUT(Rank); - 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); - - 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; - case 0x01: - emu->command = 0x10; // Leader - break; - case 0x02: - emu->command = 0x07; // Attack - break; - case 0x04: - emu->command = 0x08; // Follow - break; - case 0x05: - emu->command = 0x05; // Guard - break; - case 0x06: - emu->command = 0x09; // Sit. Needs work. This appears to be a toggle between Sit/Stand now. - break; - case 0x0c: - emu->command = 0x0b; // Taunt - break; - case 0x0f: - emu->command = 0x0c; // Hold - break; - case 0x10: - emu->command = 0x1b; // Hold on - break; - case 0x11: - emu->command = 0x1c; // Hold off - break; - case 0x1c: - emu->command = 0x01; // Back - break; - case 0x1d: - emu->command = 0x02; // Leave/Go Away - 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; - - 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 = 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_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) - { - 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)); - } - - 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)); - - 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.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 secondary struct is %i bytes", sizeof(RoF::structs::ItemSecondaryBodyStruct)); - RoF::structs::ItemSecondaryBodyStruct isbs; - memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); - - isbs.augtype = item->AugType; - isbs.augdistiller = 0; - isbs.augrestrict = item->AugRestrict; - - - 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]; - } - - // Increased to 6 max aug slots - isbs.augslots[5].type = 0; - isbs.augslots[5].visible = 1; - isbs.augslots[5].unknown = 0; - - 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(RoF::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)); - } - - //_log(NET__ERROR, "ItemBody tertiary struct is %i bytes", sizeof(RoF::structs::ItemTertiaryBodyStruct)); - RoF::structs::ItemTertiaryBodyStruct itbs; - memset(&itbs, 0, sizeof(RoF::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.unknown3 = 0xffffffff; - itbs.unknown4 = 0; - itbs.no_pet = item->NoPet; - itbs.unknown5 = 0; - - 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; - - 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*)&itbs, sizeof(RoF::structs::ItemTertiaryBodyStruct)); - - // Effect Structures Broken down to allow variable length strings for effect names - int32 effect_unknown = 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)); - - 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(RoF::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 - - //_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)); - - 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(RoF::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 - - //_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)); - - 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(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)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - RoF::structs::WornEffectStruct ifes; - memset(&ifes, 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; - - ss.write((const char*)&ifes, 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)); - } - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - - RoF::structs::WornEffectStruct ises; - memset(&ises, 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; - - ss.write((const char*)&ises, 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)); - } - - 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 */ - ss.write((const char*)&null_term, sizeof(uint8)); - - 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)); - - 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; - - 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(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; -} - -} //end namespace RoF diff --git a/common/patches/SoD.cpp b/common/patches/SoD.cpp deleted file mode 100644 index dcc95ec75..000000000 --- a/common/patches/SoD.cpp +++ /dev/null @@ -1,3562 +0,0 @@ - -#include "../debug.h" -#include "SoD.h" -#include "../opcodemgr.h" -#include "../logsys.h" -#include "../EQStreamIdent.h" -#include "../crc32.h" - -#include "../eq_packet_structs.h" -#include "../MiscFunctions.h" -#include "../StringUtil.h" -#include "../Item.h" -#include "SoD_structs.h" -#include "../rulesys.h" - -#include -#include - -namespace SoD { - -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); - -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); - } -} - - - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "SSRegister.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; -} - -#include "SSDefine.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 ) - { - 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_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) - { - _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 = 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_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_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) - { - 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; - - //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; - } - - - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - - emu = (Spawn_Struct *) __emu_buffer; - - //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - - char *Buffer = (char *) in->pBuffer; - - - int r; - int k; - 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)) - PacketSize += strlen(emu->title) + 1; - - if(strlen(emu->suffix)) - PacketSize += strlen(emu->suffix) + 1; - - if(emu->DestructibleObject) - { - PacketSize = PacketSize - 4; // No bodytype - PacketSize += 53; // Fixed portion - PacketSize += strlen(emu->DestructibleModel) + 1; - PacketSize += strlen(emu->DestructibleName2) + 1; - PacketSize += strlen(emu->DestructibleString) + 1; - } - - bool ShowName = 1; - if(emu->bodytype >= 66) - { - emu->race = 127; - emu->bodytype = 11; - emu->gender = 0; - ShowName = 0; - } - - float SpawnSize = emu->size; - 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) - { - emu->size = 6; - SpawnSize = 6; - } - } - - if(SpawnSize == 0) - { - SpawnSize = 3; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - 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) - { - VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 - } - else - { - VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? - } - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - - structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; - - Bitfields->afk = 0; - Bitfields->linkdead = 0; - Bitfields->gender = emu->gender; - - Bitfields->invis = emu->invis; - Bitfields->sneak = 0; - Bitfields->lfg = emu->lfg; - Bitfields->gm = emu->gm; - Bitfields->anon = emu->anon; - Bitfields->showhelm = emu->showhelm; - Bitfields->targetable = 1; - Bitfields->targetable_with_hotkey = (emu->IsMercenary ? 0 : 1); - Bitfields->statue = 0; - Bitfields->trader = 0; - Bitfields->buyer = 0; - - Bitfields->showname = ShowName; - - if(emu->DestructibleObject) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); - Buffer = Buffer -4; - } - - Bitfields->ispet = emu->is_pet; - - Buffer += sizeof(structs::Spawn_Struct_Bitfields); - - uint8 OtherData = 0; - - if(strlen(emu->title)) - OtherData = OtherData | 0x04; - - if(strlen(emu->suffix)) - OtherData = OtherData | 0x08; - - if(emu->DestructibleObject) - OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); - - if(emu->DestructibleObject) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); - } - else - { - VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3 - } - VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 - - if(emu->DestructibleObject) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); - VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); - VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleString); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleAppearance); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID1); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID2); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID3); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID4); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk2); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk3); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk4); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk5); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk6); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk7); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9); - } - - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->walkspeed); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race); - /* - if(emu->bodytype >=66) - { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname - } - else - { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname - }*/ - - - 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. - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype); - } - else - { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - } - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->curHp); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->haircolor); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beardcolor); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor1); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor2); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->hairstyle); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beard); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_heritage); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); - if(emu->NPC) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); - } - else - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); - } - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // pvp - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->StandState); // standstate - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->light); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->flymode); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // unknown8 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11 - VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown12 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 - 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; - Position->deltaHeading = emu->deltaHeading; - Position->deltaY = emu->deltaY; - Position->y = emu->y; - Position->animation = emu->animation; - Position->heading = emu->heading; - Position->x = emu->x; - Position->z = emu->z; - Position->deltaZ = emu->deltaZ; - - 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)) - { - for(k = 0; k < 9; ++k) - { - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); - } - } - } - else - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - 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)) - { - structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - - for(k = 0; k < 9; k++) { - Equipment[k].equip0 = emu->equipment[k]; - Equipment[k].equip1 = 0; - Equipment[k].itemId = 0; - } - - Buffer += (sizeof(structs::EquipStruct) * 9); - } - if(strlen(emu->title)) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->title); - } - - if(strlen(emu->suffix)) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); - } - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary); //IsMercenary - 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 - { - 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; - 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_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_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) - { - 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) -{ - 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_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_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) - { - 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)); - - 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 diff --git a/common/patches/SoF.cpp b/common/patches/SoF.cpp deleted file mode 100644 index d607ff472..000000000 --- a/common/patches/SoF.cpp +++ /dev/null @@ -1,2867 +0,0 @@ - -#include "../debug.h" -#include "SoF.h" -#include "../opcodemgr.h" -#include "../logsys.h" -#include "../EQStreamIdent.h" -#include "../crc32.h" - -#include "../eq_packet_structs.h" -#include "../StringUtil.h" -#include "../Item.h" -#include "SoF_structs.h" -#include "../rulesys.h" - -#include -#include - -namespace SoF { - -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); - -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); - } -} - - - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "SSRegister.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; -} - -#include "SSDefine.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 ) - { - 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_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) - { - _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_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->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); - - uint32 ofs; - uint32 val; - 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]); - } - } - 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"; - - eq->lastName[len + 0] = ' '; - eq->lastName[len + 1] = code; - eq->lastName[len + 2] = '0' + ((ofs / 1000) % 10); - eq->lastName[len + 3] = '0' + ((ofs / 100) % 10); - eq->lastName[len + 4] = '0' + ((ofs / 10) % 10); - eq->lastName[len + 5] = '0' + (ofs % 10); - eq->lastName[len + 6] = '='; - eq->lastName[len + 7] = '0' + ((val / 100) % 10); - eq->lastName[len + 8] = '0' + ((val / 10) % 10); - eq->lastName[len + 9] = '0' + (val % 10); - eq->lastName[len + 10] = 0x00; - - 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. - } - }*/ - } - - - //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; - - 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 - { - 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(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_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]) - { - 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_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 - { - 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(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; -} - -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 - - - - diff --git a/common/patches/Titanium.cpp b/common/patches/Titanium.cpp deleted file mode 100644 index ea44c3c38..000000000 --- a/common/patches/Titanium.cpp +++ /dev/null @@ -1,1503 +0,0 @@ - -#include "../debug.h" -#include "Titanium.h" -#include "../opcodemgr.h" -#include "../logsys.h" -#include "../EQStreamIdent.h" -#include "../crc32.h" -#include "../races.h" - -#include "../eq_packet_structs.h" -#include "../StringUtil.h" -#include "../Item.h" -#include "Titanium_structs.h" -#include - -namespace Titanium { - -static const char *name = "Titanium"; -static OpcodeManager *opcodes = nullptr; -static Strategy struct_strategy; - -char *SerializeItem(const ItemInst *inst, int16 slot_id, uint32 *length, uint8 depth); - -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); - } -} - - - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "SSRegister.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; -} - -#include "SSDefine.h" - - -/* -// 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 ) - { - 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_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) - { - _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_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; - } - - 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))) { - - 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_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_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) - { - 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; -} - -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]); - } - 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 < 29; r++) { - IN(filters[r]); - } - 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(); -} - -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); - } - } - - - *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 -#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) ? "\"" : "" - ); - - for(i=0;i<10;i++) { - if (sub_items[i]) - safe_delete_array(sub_items[i]); - } - - safe_delete_array(instance); - return serialization; -} - -} //end namespace Titanium - - - - diff --git a/common/patches/Underfoot.cpp b/common/patches/Underfoot.cpp deleted file mode 100644 index b56d3f511..000000000 --- a/common/patches/Underfoot.cpp +++ /dev/null @@ -1,3977 +0,0 @@ - -#include "../debug.h" -#include "Underfoot.h" -#include "../opcodemgr.h" -#include "../logsys.h" -#include "../EQStreamIdent.h" -#include "../crc32.h" - -#include "../eq_packet_structs.h" -#include "../MiscFunctions.h" -#include "../StringUtil.h" -#include "../Item.h" -#include "Underfoot_structs.h" -#include "../rulesys.h" - -#include -#include - -namespace Underfoot -{ - -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); - -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); - } -} - - - -Strategy::Strategy() -: StructStrategy() -{ - //all opcodes default to passthrough. - #include "SSRegister.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; -} - -#include "SSDefine.h" - - -// Converts Server Slot IDs to Underfoot Slot IDs for use in Encodes -static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot) { - uint32 UnderfootSlot = 0; - - 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; - - 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; - } - - 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(); -} - -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 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_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) - { - 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_PlayerProfile) { - SETUP_DIRECT_ENCODE(PlayerProfile_Struct, structs::PlayerProfile_Struct); - - 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) - { - 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_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]) - { - 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_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_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; - } - - - //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); - - emu = (Spawn_Struct *) __emu_buffer; - - //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); - - char *Buffer = (char *) in->pBuffer; - - - int r; - int k; - 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)) - PacketSize += strlen(emu->title) + 1; - - if(strlen(emu->suffix)) - PacketSize += strlen(emu->suffix) + 1; - - if(emu->DestructibleObject) - { - PacketSize = PacketSize - 4; // No bodytype - PacketSize += 53; // Fixed portion - PacketSize += strlen(emu->DestructibleModel) + 1; - PacketSize += strlen(emu->DestructibleName2) + 1; - PacketSize += strlen(emu->DestructibleString) + 1; - } - - bool ShowName = 1; - if(emu->bodytype >= 66) - { - emu->race = 127; - emu->bodytype = 11; - emu->gender = 0; - ShowName = 0; - } - - float SpawnSize = emu->size; - 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) - { - emu->size = 6; - SpawnSize = 6; - } - } - - if(SpawnSize == 0) - { - SpawnSize = 3; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); - 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) - { - VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 - } - else - { - VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? - } - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); - - structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; - - Bitfields->afk = 0; - Bitfields->linkdead = 0; - Bitfields->gender = emu->gender; - - Bitfields->invis = emu->invis; - Bitfields->sneak = 0; - Bitfields->lfg = emu->lfg; - Bitfields->gm = emu->gm; - Bitfields->anon = emu->anon; - Bitfields->showhelm = emu->showhelm; - Bitfields->targetable = 1; - Bitfields->targetable_with_hotkey = (emu->IsMercenary ? 0 : 1); - Bitfields->statue = 0; - Bitfields->trader = 0; - Bitfields->buyer = 0; - - Bitfields->showname = ShowName; - - if(emu->DestructibleObject) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); - Buffer = Buffer -4; - } - - Bitfields->ispet = emu->is_pet; - - Buffer += sizeof(structs::Spawn_Struct_Bitfields); - - uint8 OtherData = 0; - - if(strlen(emu->title)) - OtherData = OtherData | 0x04; - - if(strlen(emu->suffix)) - OtherData = OtherData | 0x08; - - if(emu->DestructibleObject) - OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); - - if(emu->DestructibleObject) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); - } - else - { - VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3 - } - VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 - - if(emu->DestructibleObject) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); - VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); - VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleString); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleAppearance); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID1); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID2); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID3); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID4); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk2); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk3); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk4); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk5); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk6); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk7); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9); - } - - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->walkspeed); - VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race); - /* - if(emu->bodytype >=66) - { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname - } - else - { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname - }*/ - - - 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. - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype); - } - else - { - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); - } - - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->curHp); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->haircolor); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beardcolor); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor1); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor2); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->hairstyle); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beard); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_heritage); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo); - 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) - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); - } - else - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); - } - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // pvp - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->StandState); // standstate - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->light); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->flymode); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // unknown8 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10 - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11 - VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown12 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 - 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; - Position->deltaHeading = emu->deltaHeading; - Position->deltaY = emu->deltaY; - Position->y = emu->y; - Position->animation = emu->animation; - Position->heading = emu->heading; - Position->x = emu->x; - Position->z = emu->z; - Position->deltaZ = emu->deltaZ; - - 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)) - { - for(k = 0; k < 9; ++k) - { - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); - } - } - } - else - { - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary]); - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); - 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)) - { - structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; - - for(k = 0; k < 9; k++) { - Equipment[k].equip0 = emu->equipment[k]; - Equipment[k].equip1 = 0; - Equipment[k].itemId = 0; - } - - Buffer += (sizeof(structs::EquipStruct) * 9); - } - if(strlen(emu->title)) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->title); - } - - if(strlen(emu->suffix)) - { - VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); - } - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; - VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; - VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary); //IsMercenary - 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 - { - 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; - 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_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_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)) - { - 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; - 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; - case 0x01: - emu->command = 0x10; // Leader - break; - case 0x02: - emu->command = 0x07; // Attack - break; - case 0x04: - emu->command = 0x08; // Follow - break; - case 0x05: - emu->command = 0x05; // Guard - break; - case 0x06: - emu->command = 0x09; // Sit. Needs work. This appears to be a toggle between Sit/Stand now. - break; - case 0x0c: - emu->command = 0x0b; // Taunt - break; - case 0x0f: - emu->command = 0x0c; // Hold - break; - case 0x10: - emu->command = 0x1b; // Hold on - break; - case 0x11: - emu->command = 0x1c; // Hold off - break; - case 0x1c: - emu->command = 0x01; // Back - break; - case 0x1d: - emu->command = 0x02; // Leave/Go Away - break; - case 0x15: - emu->command = 0x12; // No Cast - /command - break; - case 0x16: - emu->command = 0x12; // No Cast - Pet Window - break; - case 0x18: - emu->command = 0x13; // Focus - Pet Window - break; - default: - emu->command = eq->command; - } - 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)); - } - - 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)); - - 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.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 */ - ss.write((const char*)&null_term, sizeof(uint8)); - - ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 - // End of Effects - - Underfoot::structs::ItemQuaternaryBodyStruct iqbs; - memset(&iqbs, 0, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); - - 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.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(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; -} - -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 diff --git a/common/patches/client62.cpp b/common/patches/client62.cpp new file mode 100644 index 000000000..c291d2bc4 --- /dev/null +++ b/common/patches/client62.cpp @@ -0,0 +1,1340 @@ +#include "../debug.h" +#include "client62.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../eq_stream_ident.h" +#include "../crc32.h" + +#include "../eq_packet_structs.h" +#include "../misc_functions.h" +#include "../string_util.h" +#include "../item.h" +#include "../clientversions.h" +#include "client62_structs.h" + +namespace Client62 +{ + 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); + + // server to client inventory location converters + static inline int16 ServerToClient62Slot(uint32 ServerSlot); + static inline int16 ServerToClient62CorpseSlot(uint32 ServerCorpse); + + // 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); + } + } + + 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; + } + +#include "ss_define.h" + +// ENCODE methods + EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ; + + EAT_ENCODE(OP_ZoneServerReady); // added ; + + 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_AdventureMerchantSell) + { + 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(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToClient62Slot(emu->inventorySlot); + OUT(success); + + 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) + { + 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(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); + } + + ENCODE(OP_BecomeTrader) + { + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); + + OUT(ID); + OUT(Code); + + FINISH_ENCODE(); + } + + 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); + } + + } + + 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_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 + + 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); + 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); + + 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_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_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_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) + { + 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; + + 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_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 < 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->GetUnscaledItem(); + 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(), + inst->IsScaling() ? inst->GetExp() / 100 : 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 +#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) ? "\"" : "" + ); + + for (i = 0; i < 10; i++) { + if (sub_items[i]) + safe_delete_array(sub_items[i]); + } + + safe_delete_array(instance); + + return serialization; + } + + static inline int16 ServerToClient62Slot(uint32 ServerSlot) + { + //int16 Client62Slot; + if (ServerSlot == INVALID_INDEX) + return INVALID_INDEX; + + 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 diff --git a/common/patches/Client62.h b/common/patches/client62.h similarity index 84% rename from common/patches/Client62.h rename to common/patches/client62.h index f4a70bc90..08d1a9716 100644 --- a/common/patches/Client62.h +++ b/common/patches/client62.h @@ -1,8 +1,8 @@ #ifndef CLIENT62_H_ #define CLIENT62_H_ -#include "../StructStrategy.h" -#include "../Item.h" +#include "../struct_strategy.h" +#include "../item.h" class EQStreamIdentifier; @@ -27,8 +27,8 @@ namespace Client62 { virtual const EQClientVersion ClientVersion() const; //magic macro to declare our opcode processors - #include "SSDeclare.h" - #include "Client62_ops.h" + #include "ss_declare.h" + #include "client62_ops.h" }; diff --git a/common/patches/Client62_constants.h b/common/patches/client62_constants.h similarity index 98% rename from common/patches/Client62_constants.h rename to 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_itemfields.h b/common/patches/client62_itemfields.h similarity index 100% rename from common/patches/Client62_itemfields.h rename to common/patches/client62_itemfields.h diff --git a/common/patches/Client62_ops.h b/common/patches/client62_ops.h similarity index 56% rename from common/patches/Client62_ops.h rename to 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 similarity index 97% rename from common/patches/Client62_structs.h rename to common/patches/client62_structs.h index 27b808620..691eeba21 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]; }; /* @@ -472,8 +472,8 @@ struct GMTrainee_Struct { /*000*/ uint32 npcid; /*004*/ uint32 playerid; - /*008*/ uint32 skills[73]; - /*300*/ uint8 unknown300[148]; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[40]; /*448*/ }; @@ -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]; + }; }; /* @@ -630,7 +678,7 @@ struct RaidLeadershipAA_Struct { static const uint32 MAX_PP_LANGUAGE = 28; static const uint32 MAX_PP_SPELLBOOK = 400; static const uint32 MAX_PP_MEMSPELL = 9; -static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size +static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 240; static const uint32 MAX_GROUP_MEMBERS = 6; struct PlayerProfile_Struct @@ -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/patches.cpp b/common/patches/patches.cpp index febb344d7..212042210 100644 --- a/common/patches/patches.cpp +++ b/common/patches/patches.cpp @@ -2,13 +2,13 @@ #include "../debug.h" #include "patches.h" -#include "Client62.h" -#include "Titanium.h" -#include "Underfoot.h" -#include "SoF.h" -#include "SoD.h" -#include "RoF.h" -//#include "RoF2.h" +#include "client62.h" +#include "titanium.h" +#include "underfoot.h" +#include "sof.h" +#include "sod.h" +#include "rof.h" +//#include "rof2.h" void RegisterAllPatches(EQStreamIdentifier &into) { Client62::Register(into); diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp new file mode 100644 index 000000000..c7efe0264 --- /dev/null +++ b/common/patches/rof.cpp @@ -0,0 +1,5667 @@ +#include "../debug.h" +#include "rof.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../eq_stream_ident.h" +#include "../crc32.h" + +#include "../eq_packet_structs.h" +#include "../misc_functions.h" +#include "../string_util.h" +#include "../item.h" +#include "rof_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace RoF +{ + 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); + + // 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); + + // 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); + } + } + + 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; + } + +#include "ss_define.h" + +// ENCODE methods + 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_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_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + 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(); + }*/ + + 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_InterruptCast) + { + ENCODE_LENGTH_EXACT(InterruptCast_Struct); + SETUP_DIRECT_ENCODE(InterruptCast_Struct, structs::InterruptCast_Struct); + + OUT(spawnid); + OUT(messageid); + + 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 = 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; + 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 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); + + 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) + { + // 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) + { + if (emu->spellid[i]) + { + __packet->WriteUInt32(i); + __packet->WriteUInt32(emu->spellid[i]); + __packet->WriteUInt32(emu->ticsremaining[i]); + __packet->WriteUInt32(0); // Unknown + __packet->WriteString(""); + } + } + __packet->WriteUInt8(0); // Unknown + + 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++) + { + 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(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->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 + + outapp->WriteUInt32(64); // Name Length + + uint32 CurrentPosition = outapp->GetWritePosition(); + + outapp->WriteString(emu->name); + + outapp->SetWritePosition(CurrentPosition + 64); + + outapp->WriteUInt32(32); // Last Name Length + + CurrentPosition = outapp->GetWritePosition(); + + outapp->WriteString(emu->last_name); + + outapp->SetWritePosition(CurrentPosition + 32); + + 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->WriteUInt32(structs::MAX_PP_LANGUAGE); + + for (uint32 r = 0; r < MAX_PP_LANGUAGE; r++) + { + outapp->WriteUInt8(emu->languages[r]); + } + + for (uint32 r = 0; r < structs::MAX_PP_LANGUAGE - MAX_PP_LANGUAGE; r++) + { + outapp->WriteUInt8(0); + } + + 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(emu->career_tribute_points); + outapp->WriteUInt32(0); // Unknown + outapp->WriteUInt32(emu->tribute_points); + outapp->WriteUInt32(0); // Unknown + + outapp->WriteUInt8(0); // Unknown + outapp->WriteUInt8(0); // Unknown + + outapp->WriteUInt32(EmuConstants::TRIBUTE_SIZE); + + 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->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 + + // 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(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 + + // Think we need 1 byte of padding at the end + + outapp->WriteUInt8(0); // Unknown + + _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_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); + 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); + } + } + + 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_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_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; + + //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; + } + + //_log(NET__STRUCTS, "Spawn name is [%s]", emu->name); + + emu = (Spawn_Struct *)__emu_buffer; + + //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); + + char *Buffer = (char *)in->pBuffer, *BufferStart; + + int r; + int k; + for (r = 0; r < entrycount; r++, emu++) { + + int PacketSize = 206; + + PacketSize += strlen(emu->name); + PacketSize += strlen(emu->lastName); + + emu->title[0] = 0; + emu->suffix[0] = 0; + + if (strlen(emu->title)) + PacketSize += strlen(emu->title) + 1; + + if (strlen(emu->suffix)) + PacketSize += strlen(emu->suffix) + 1; + + bool ShowName = 1; + if (emu->bodytype >= 66) + { + emu->race = 127; + emu->bodytype = 11; + emu->gender = 0; + ShowName = 0; + } + + float SpawnSize = emu->size; + 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) + { + emu->size = 6; + SpawnSize = 6; + } + } + else + PacketSize += 216; + + if (SpawnSize == 0) + { + SpawnSize = 3; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); + Buffer = (char *)outapp->pBuffer; + BufferStart = Buffer; + VARSTRUCT_ENCODE_STRING(Buffer, emu->name); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level); + VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + + structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; + + Bitfields->gender = emu->gender; + Bitfields->ispet = emu->is_pet; + Bitfields->afk = emu->afk; + Bitfields->anon = emu->anon; + Bitfields->gm = emu->gm; + Bitfields->sneak = 0; + Bitfields->lfg = emu->lfg; + Bitfields->invis = emu->invis; + Bitfields->linkdead = 0; + Bitfields->showhelm = emu->showhelm; + Bitfields->trader = 0; + Bitfields->targetable = 1; + Bitfields->targetable_with_hotkey = (emu->IsMercenary ? 0 : 1); + Bitfields->showname = ShowName; + + // Not currently found + // Bitfields->statue = 0; + // Bitfields->buyer = 0; + + Buffer += sizeof(structs::Spawn_Struct_Bitfields); + + uint8 OtherData = 0; + + if (strlen(emu->title)) + OtherData = OtherData | 16; + + if (strlen(emu->suffix)) + OtherData = OtherData | 32; + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); + + VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3 + VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 + + // 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. + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype); + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->curHp); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->haircolor); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beardcolor); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor1); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor2); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->hairstyle); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beard); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_heritage); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // unknown8 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11 + + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->walkspeed); + 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) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); + } + else + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); + + /* 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; } // + } + } + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // pvp + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->StandState); // standstate + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->light); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->flymode); + + VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle ?? + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC ? 0 : 1); // unknown - Must be 1 for guild name to be shown abover players head. + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 + 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)) + { + for (k = 0; k < 9; ++k) + { + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); + } + } + + structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; + + for (k = 0; k < 9; k++) { + Equipment[k].equip0 = emu->equipment[k]; + Equipment[k].equip1 = 0; + Equipment[k].equip2 = 0; + Equipment[k].equip3 = 0; + Equipment[k].itemId = 0; + } + + Buffer += (sizeof(structs::EquipStruct) * 9); + } + else + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + } + + structs::Spawn_Struct_Position *Position = (structs::Spawn_Struct_Position*)Buffer; + + Position->deltaX = emu->deltaX; + Position->deltaHeading = emu->deltaHeading; + Position->deltaY = emu->deltaY; + Position->y = emu->y; + Position->animation = emu->animation; + Position->heading = emu->heading; + Position->x = emu->x; + Position->z = emu->z; + Position->deltaZ = emu->deltaZ; + + Buffer += sizeof(structs::Spawn_Struct_Position); + + if (strlen(emu->title)) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->title); + } + + if (strlen(emu->suffix)) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); + } + + Buffer += 8; + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary); + VARSTRUCT_ENCODE_STRING(Buffer, "0000000000000000"); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); + // 29 zero bytes follow + Buffer += 29; + 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; + } + +// 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 = RoFToServerMainInvSlot(eq->slot); + IN(charges); + IN(sell_price); + + 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_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; + + 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_CharacterCreate) + { + DECODE_LENGTH_EXACT(structs::CharCreate_Struct); + SETUP_DIRECT_DECODE(CharCreate_Struct, structs::CharCreate_Struct); + + 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); + + 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 = 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(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_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; + case 0x01: + emu->command = 0x10; // Leader + break; + case 0x02: + emu->command = 0x07; // Attack + break; + case 0x04: + emu->command = 0x08; // Follow + break; + case 0x05: + emu->command = 0x05; // Guard + break; + case 0x06: + emu->command = 0x09; // Sit. Needs work. This appears to be a toggle between Sit/Stand now. + break; + case 0x0c: + emu->command = 0x0b; // Taunt + break; + case 0x0f: + emu->command = 0x0c; // Hold + break; + case 0x10: + emu->command = 0x1b; // Hold on + break; + case 0x11: + emu->command = 0x1c; // Hold off + break; + case 0x1c: + emu->command = 0x01; // Back + break; + case 0x1d: + emu->command = 0x02; // Leave/Go Away + break; + default: + emu->command = eq->command; + } + + 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); + IN(invslot); + emu->window = (uint8)eq->window; + strn0cpy(emu->txtfile, eq->txtfile, sizeof(emu->txtfile)); + + 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_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 + NextItemInstSerialNumber++; + + return NextItemInstSerialNumber; + } + + char* SerializeItem(const ItemInst *inst, int16 slot_id_in, uint32 *length, uint8 depth) + { + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); + 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->GetUnscaledItem(); + //_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.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 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.isEvolving = item->EvolvingLevel > 0 ? 1 : 0; + ss.write((const char*)&hdr, sizeof(RoF::structs::ItemSerializationHeader)); + + if (item->EvolvingLevel > 0) { + RoF::structs::EvolvingItem evotop; + evotop.unknown001 = 0; + evotop.unknown002 = 0; + evotop.unknown003 = 0; + evotop.unknown004 = 0; + evotop.evoLevel = item->EvolvingLevel; + evotop.progress = 95.512; + evotop.Activated = 1; + evotop.evomaxlevel = 7; + ss.write((const char*)&evotop, sizeof(RoF::structs::EvolvingItem)); + } + //ORNAMENT IDFILE / ICON + uint16 ornaIcon = 0; + if (inst->GetOrnamentationAug(ornamentationAugtype)) { + const Item_Struct *aug_weap = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + //Mainhand + ss.write(aug_weap->IDFile, strlen(aug_weap->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + //Offhand + ss.write(aug_weap->IDFile, strlen(aug_weap->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + //Icon + ornaIcon = aug_weap->Icon; + } + else if (inst->GetOrnamentationIDFile() && inst->GetOrnamentationIcon()) { + char tmp[30]; memset(tmp, 0x0, 30); sprintf(tmp, "IT%d", inst->GetOrnamentationIDFile()); + //Mainhand + ss.write(tmp, strlen(tmp)); + ss.write((const char*)&null_term, sizeof(uint8)); + //Offhand + ss.write(tmp, strlen(tmp)); + ss.write((const char*)&null_term, sizeof(uint8)); + ornaIcon = inst->GetOrnamentationIcon(); + } + else { + ss.write((const char*)&null_term, sizeof(uint8)); //no mh + ss.write((const char*)&null_term, sizeof(uint8));//no of + } + + RoF::structs::ItemSerializationHeaderFinish hdrf; + hdrf.ornamentIcon = ornaIcon; + hdrf.unknown061 = 0; + hdrf.unknown062 = 0; + hdrf.unknowna1 = 0xffffffff; + hdrf.unknowna2 = 0; + hdrf.unknown063 = 0; + hdrf.unknowna3 = 0; + hdrf.unknowna4 = 0xffffffff; + hdrf.unknowna5 = 0; + hdrf.ItemClass = item->ItemClass; + ss.write((const char*)&hdrf, sizeof(RoF::structs::ItemSerializationHeaderFinish)); + + 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)); + } + + 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)); + + 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.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.CharmFileID = item->CharmFileID; + 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 secondary struct is %i bytes", sizeof(RoF::structs::ItemSecondaryBodyStruct)); + RoF::structs::ItemSecondaryBodyStruct isbs; + memset(&isbs, 0, sizeof(RoF::structs::ItemSecondaryBodyStruct)); + + isbs.augtype = item->AugType; + isbs.augdistiller = 65535; + isbs.augrestrict = item->AugRestrict; + + 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]; + } + + // Increased to 6 max aug slots + isbs.augslots[5].type = 0; + isbs.augslots[5].visible = 1; + isbs.augslots[5].unknown = 0; + + 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(RoF::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)); + } + + //_log(NET__ERROR, "ItemBody tertiary struct is %i bytes", sizeof(RoF::structs::ItemTertiaryBodyStruct)); + RoF::structs::ItemTertiaryBodyStruct itbs; + memset(&itbs, 0, sizeof(RoF::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.unknown3 = 0xffffffff; + itbs.unknown4 = 0; + itbs.no_pet = item->NoPet; + itbs.unknown5 = 0; + + 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; + + 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*)&itbs, sizeof(RoF::structs::ItemTertiaryBodyStruct)); + + // Effect Structures Broken down to allow variable length strings for effect names + int32 effect_unknown = 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)); + + 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(RoF::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 + + //_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)); + + 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(RoF::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 + + //_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)); + + 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(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)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + RoF::structs::WornEffectStruct ifes; + memset(&ifes, 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; + + ss.write((const char*)&ifes, 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)); + } + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + + RoF::structs::WornEffectStruct ises; + memset(&ises, 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; + + ss.write((const char*)&ises, 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)); + } + + 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 */ + ss.write((const char*)&null_term, sizeof(uint8)); + + 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)); + + 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; + + 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(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); + } + + /* + // 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; + } + + 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; + } + + static inline uint32 ServerToRoFCorpseSlot(uint32 ServerCorpse) + { + //uint32 RoFCorpse; + return (ServerCorpse + 1); + } + + 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 diff --git a/common/patches/RoF.h b/common/patches/rof.h similarity index 87% rename from common/patches/RoF.h rename to common/patches/rof.h index fc2e94c95..220341970 100644 --- a/common/patches/RoF.h +++ b/common/patches/rof.h @@ -1,7 +1,7 @@ #ifndef RoF_H_ #define RoF_H_ -#include "../StructStrategy.h" +#include "../struct_strategy.h" class EQStreamIdentifier; @@ -26,8 +26,8 @@ namespace RoF { virtual const EQClientVersion ClientVersion() const; //magic macro to declare our opcode processors - #include "SSDeclare.h" - #include "RoF_ops.h" + #include "ss_declare.h" + #include "rof_ops.h" }; }; diff --git a/common/patches/RoF_constants.h b/common/patches/rof_constants.h similarity index 98% rename from common/patches/RoF_constants.h rename to 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_itemfields.h b/common/patches/rof_itemfields.h similarity index 100% rename from common/patches/RoF_itemfields.h rename to common/patches/rof_itemfields.h diff --git a/common/patches/RoF_ops.h b/common/patches/rof_ops.h similarity index 95% rename from common/patches/RoF_ops.h rename to common/patches/rof_ops.h index 9e3a91c7e..878870a81 100644 --- a/common/patches/RoF_ops.h +++ b/common/patches/rof_ops.h @@ -1,162 +1,162 @@ - -//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_AugmentInfo) +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 similarity index 97% rename from common/patches/RoF_structs.h rename to common/patches/rof_structs.h index 918bbe68d..075e6633b 100644 --- a/common/patches/RoF_structs.h +++ b/common/patches/rof_structs.h @@ -112,7 +112,7 @@ static const uint32 MAX_NUMBER_GUILDS = 1500; static const uint32 MAX_PP_LANGUAGE = 32; // was 25 static const uint32 MAX_PP_SPELLBOOK = 720; // was 480 static const uint32 MAX_PP_MEMSPELL = 16; // was 12 -static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size +static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 300; static const uint32 MAX_PP_DISCIPLINES = 200; // was 100 static const uint32 MAX_GROUP_MEMBERS = 6; @@ -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,12 +742,33 @@ 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; /*004*/ uint32 playerid; - /*008*/ uint32 skills[73]; - /*300*/ uint8 unknown300[148]; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[40]; /*448*/ }; @@ -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 @@ -2519,10 +2594,10 @@ struct Stun_Struct { // 8 bytes total struct AugmentItem_Struct { /*00*/ uint32 dest_inst_id; // The unique serial number for the item instance that is being augmented -/*04*/ uint32 unknown04; // Seen 0 +/*04*/ uint32 container_index; // Seen 0 /*08*/ ItemSlotStruct container_slot; // Slot of the item being augmented -/*20*/ uint32 unknown20; // Seen 0 -/*24*/ ItemSlotStruct distiller_slot; // Slot of the distiller to use (if one applies) +/*20*/ uint32 augment_index; // Seen 0 +/*24*/ ItemSlotStruct augment_slot; // Slot of the distiller to use (if one applies) /*36*/ int32 augment_action; // Guessed - 0 = augment, 1 = remove with distiller, 3 = delete aug /*36*/ //int32 augment_slot; /*40*/ @@ -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 @@ -4079,7 +4169,8 @@ struct Arrow_Struct { /*070*/ uint8 unknown070; /*071*/ uint8 item_type; /*072*/ uint8 skill; -/*073*/ char model_name[43]; +/*073*/ uint8 unknown073[16]; +/*089*/ char model_name[27]; /*116*/ }; @@ -4106,7 +4197,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 @@ -4306,7 +4397,7 @@ struct ItemSerializationHeader /*030*/ uint16 unknown013; // 0xffff /*032*/ uint32 price; /*036*/ uint32 merchant_slot; //1 if not a merchant item -/*040*/ uint32 unknown020; //0 +/*040*/ uint32 scaled_value; //0 /*044*/ uint32 instance_id; //unique instance id if not merchant item, else is merchant slot /*048*/ uint32 unknown028; //0 /*052*/ uint32 last_cast_time; // Unix Time from PP of last cast for this recast type if recast delay > 0 @@ -4315,8 +4406,24 @@ struct ItemSerializationHeader /*064*/ uint32 unknown044; // 0 /*068*/ uint32 unknown048; // 0 /*072*/ uint32 unknown052; // 0 -/*076*/ uint32 unknown056; // 0 -/*080*/ uint8 unknown060; // 0 + uint8 isEvolving; +}; + +struct EvolvingItem { + uint8 unknown001; + uint8 unknown002; + uint8 unknown003; + uint8 unknown004; + int32 evoLevel; + double progress; + uint8 Activated; + int32 evomaxlevel; + uint8 unknown005[4]; +}; + +struct ItemSerializationHeaderFinish +{ + uint16 ornamentIcon; /*081*/ uint8 unknown061; // 0 - Add Evolving Item struct if this isn't set to 0? /*082*/ uint8 unknown062; // 0 /*083*/ uint32 unknowna1; // 0xffffffff @@ -4585,10 +4692,10 @@ 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 -/*076*/ +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp new file mode 100644 index 000000000..54dd33841 --- /dev/null +++ b/common/patches/sod.cpp @@ -0,0 +1,3687 @@ +#include "../debug.h" +#include "sod.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../eq_stream_ident.h" +#include "../crc32.h" + +#include "../eq_packet_structs.h" +#include "../misc_functions.h" +#include "../string_util.h" +#include "../item.h" +#include "sod_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace SoD +{ + 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); + + // server to client inventory location converters + static inline uint32 ServerToSoDSlot(uint32 ServerSlot); + static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse); + + // 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); + } + } + + 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; + } + +#include "ss_define.h" + +// ENCODE methods + 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_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); + + 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_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + 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); + + 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_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 = 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; + 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) { + _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; + + //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); + + char *Buffer = (char *)in->pBuffer; + + int r; + int k; + 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)) + PacketSize += strlen(emu->title) + 1; + + if (strlen(emu->suffix)) + PacketSize += strlen(emu->suffix) + 1; + + if (emu->DestructibleObject) + { + PacketSize = PacketSize - 4; // No bodytype + PacketSize += 53; // Fixed portion + PacketSize += strlen(emu->DestructibleModel) + 1; + PacketSize += strlen(emu->DestructibleName2) + 1; + PacketSize += strlen(emu->DestructibleString) + 1; + } + + bool ShowName = 1; + if (emu->bodytype >= 66) + { + emu->race = 127; + emu->bodytype = 11; + emu->gender = 0; + ShowName = 0; + } + + float SpawnSize = emu->size; + 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) + { + emu->size = 6; + SpawnSize = 6; + } + } + + if (SpawnSize == 0) + { + SpawnSize = 3; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); + 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) + { + VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 + } + else + { + VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? + } + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + + structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; + + Bitfields->afk = 0; + Bitfields->linkdead = 0; + Bitfields->gender = emu->gender; + + Bitfields->invis = emu->invis; + Bitfields->sneak = 0; + Bitfields->lfg = emu->lfg; + Bitfields->gm = emu->gm; + Bitfields->anon = emu->anon; + Bitfields->showhelm = emu->showhelm; + Bitfields->targetable = 1; + Bitfields->targetable_with_hotkey = (emu->IsMercenary ? 0 : 1); + Bitfields->statue = 0; + Bitfields->trader = 0; + Bitfields->buyer = 0; + + Bitfields->showname = ShowName; + + if (emu->DestructibleObject) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); + Buffer = Buffer - 4; + } + + Bitfields->ispet = emu->is_pet; + + Buffer += sizeof(structs::Spawn_Struct_Bitfields); + + uint8 OtherData = 0; + + if (strlen(emu->title)) + OtherData = OtherData | 0x04; + + if (strlen(emu->suffix)) + OtherData = OtherData | 0x08; + + if (emu->DestructibleObject) + OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); + + if (emu->DestructibleObject) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); + } + else + { + VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3 + } + VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 + + if (emu->DestructibleObject) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); + VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); + VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleString); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleAppearance); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID1); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID2); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID3); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID4); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk2); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk3); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk4); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk5); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk6); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk7); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9); + } + + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->walkspeed); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race); + /* + if(emu->bodytype >=66) + { + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname + } + else + { + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname + }*/ + + + 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. + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype); + } + else + { + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + } + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->curHp); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->haircolor); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beardcolor); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor1); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor2); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->hairstyle); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beard); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_heritage); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_details); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->deity); + if (emu->NPC) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); + } + else + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); + } + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // pvp + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->StandState); // standstate + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->light); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->flymode); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // unknown8 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11 + VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown12 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 + 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; + Position->deltaHeading = emu->deltaHeading; + Position->deltaY = emu->deltaY; + Position->y = emu->y; + Position->animation = emu->animation; + Position->heading = emu->heading; + Position->x = emu->x; + Position->z = emu->z; + Position->deltaZ = emu->deltaZ; + + 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)) + { + for (k = 0; k < 9; ++k) + { + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); + } + } + } + else + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + 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)) + { + structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; + + for (k = 0; k < 9; k++) { + Equipment[k].equip0 = emu->equipment[k]; + Equipment[k].equip1 = 0; + Equipment[k].itemId = 0; + } + + Buffer += (sizeof(structs::EquipStruct) * 9); + } + if (strlen(emu->title)) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->title); + } + + if (strlen(emu->suffix)) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); + } + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary); //IsMercenary + Buffer += 24; // Unknown; + + dest->FastQueuePacket(&outapp, ack_req); + } + + delete in; + } + +// 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 = SoDToServerSlot(eq->slot); + IN(charges); + IN(sell_price); + + 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(); + } + + 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_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_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 = SoDToServerSlot(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); + 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_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_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->GetUnscaledItem(); + //_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.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 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) + { + 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.CharmFileID = item->CharmFileID; + 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; + } + + 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; + } + + static inline uint32 ServerToSoDCorpseSlot(uint32 ServerCorpse) + { + //uint32 SoDCorpse; + return (ServerCorpse + 1); + } + + 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; + } + + static inline uint32 SoDToServerCorpseSlot(uint32 SoDCorpse) + { + //uint32 ServerCorpse; + return (SoDCorpse - 1); + } +} +// end namespace SoD diff --git a/common/patches/SoD.h b/common/patches/sod.h similarity index 87% rename from common/patches/SoD.h rename to common/patches/sod.h index 960cd2afa..0377573c2 100644 --- a/common/patches/SoD.h +++ b/common/patches/sod.h @@ -1,7 +1,7 @@ #ifndef SoD_H_ #define SoD_H_ -#include "../StructStrategy.h" +#include "../struct_strategy.h" class EQStreamIdentifier; @@ -26,8 +26,8 @@ namespace SoD { virtual const EQClientVersion ClientVersion() const; //magic macro to declare our opcode processors - #include "SSDeclare.h" - #include "SoD_ops.h" + #include "ss_declare.h" + #include "sod_ops.h" }; }; diff --git a/common/patches/SoD_constants.h b/common/patches/sod_constants.h similarity index 98% rename from common/patches/SoD_constants.h rename to 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_itemfields.h b/common/patches/sod_itemfields.h similarity index 100% rename from common/patches/SoD_itemfields.h rename to common/patches/sod_itemfields.h diff --git a/common/patches/SoD_ops.h b/common/patches/sod_ops.h similarity index 94% rename from common/patches/SoD_ops.h rename to common/patches/sod_ops.h index d7b87d514..038b42e08 100644 --- a/common/patches/SoD_ops.h +++ b/common/patches/sod_ops.h @@ -1,117 +1,116 @@ - -//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_AugmentInfo) +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 similarity index 97% rename from common/patches/SoD_structs.h rename to common/patches/sod_structs.h index 99a904b86..47b228cb7 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]; }; /* @@ -576,8 +576,8 @@ struct GMTrainee_Struct { /*000*/ uint32 npcid; /*004*/ uint32 playerid; - /*008*/ uint32 skills[73]; - /*300*/ uint8 unknown300[148]; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[40]; /*448*/ }; @@ -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]; + }; }; /** @@ -744,7 +792,7 @@ struct BindStruct { static const uint32 MAX_PP_LANGUAGE = 25; // static const uint32 MAX_PP_SPELLBOOK = 480; // Confirmed 60 pages on Live now static const uint32 MAX_PP_MEMSPELL = 10; //was 9 now 10 on Live -static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size +static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 static const uint32 MAX_GROUP_MEMBERS = 6; static const uint32 MAX_RECAST_TYPES = 20; @@ -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 @@ -3868,7 +3931,7 @@ struct ItemSerializationHeader /*008*/ uint32 slot; /*012*/ uint32 price; /*016*/ uint32 merchant_slot; //1 if not a merchant item -/*020*/ uint32 unknown020; //0 +/*020*/ uint32 scaled_value; //0 /*024*/ uint32 instance_id; //unique instance id if not merchant item, else is merchant slot /*028*/ uint32 unknown028; //0 /*032*/ uint32 last_cast_time; // Unix Time from PP of last cast for this recast type if recast delay > 0 @@ -4109,10 +4172,10 @@ 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 -/*076*/ +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp new file mode 100644 index 000000000..629395cd1 --- /dev/null +++ b/common/patches/sof.cpp @@ -0,0 +1,3009 @@ +#include "../debug.h" +#include "sof.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../eq_stream_ident.h" +#include "../crc32.h" + +#include "../eq_packet_structs.h" +#include "../misc_functions.h" +#include "../string_util.h" +#include "../item.h" +#include "sof_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace SoF +{ + 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); + + // server to client inventory location converters + static inline uint32 ServerToSoFSlot(uint32 ServerSlot); + static inline uint32 ServerToSoFCorpseSlot(uint32 ServerCorpse); + + // 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); + } + } + + 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; + } + +#include "ss_define.h" + +// ENCODE methods + 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_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); + + 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_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + 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_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(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_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 = 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) + { + 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 + { + 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 = 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); + + uint32 ofs; + uint32 val; + 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]); + } + } + 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"; + + eq->lastName[len + 0] = ' '; + eq->lastName[len + 1] = code; + eq->lastName[len + 2] = '0' + ((ofs / 1000) % 10); + eq->lastName[len + 3] = '0' + ((ofs / 100) % 10); + eq->lastName[len + 4] = '0' + ((ofs / 10) % 10); + eq->lastName[len + 5] = '0' + (ofs % 10); + eq->lastName[len + 6] = '='; + eq->lastName[len + 7] = '0' + ((val / 100) % 10); + eq->lastName[len + 8] = '0' + ((val / 10) % 10); + eq->lastName[len + 9] = '0' + (val % 10); + eq->lastName[len + 10] = 0x00; + + 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. + } + }*/ + } + + //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); + } + +// 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 = SoFToServerSlot(eq->slot); + IN(charges); + IN(sell_price); + + 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(); + } + + 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_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->GetUnscaledItem(); + //_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.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 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)); + + 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.CharmFileID = item->CharmFileID; + 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; + } + + 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; + } + + 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); + } +} +// end namespace SoF diff --git a/common/patches/SoF.h b/common/patches/sof.h similarity index 87% rename from common/patches/SoF.h rename to common/patches/sof.h index 615bc1f99..fc68ba334 100644 --- a/common/patches/SoF.h +++ b/common/patches/sof.h @@ -1,7 +1,7 @@ #ifndef SoF_H_ #define SoF_H_ -#include "../StructStrategy.h" +#include "../struct_strategy.h" class EQStreamIdentifier; @@ -26,8 +26,8 @@ namespace SoF { virtual const EQClientVersion ClientVersion() const; //magic macro to declare our opcode processors - #include "SSDeclare.h" - #include "SoF_ops.h" + #include "ss_declare.h" + #include "sof_ops.h" }; }; diff --git a/common/patches/SoF_constants.h b/common/patches/sof_constants.h similarity index 98% rename from common/patches/SoF_constants.h rename to 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_itemfields.h b/common/patches/sof_itemfields.h similarity index 100% rename from common/patches/SoF_itemfields.h rename to common/patches/sof_itemfields.h diff --git a/common/patches/SoF_opcode_list.h b/common/patches/sof_opcode_list.h similarity index 99% rename from common/patches/SoF_opcode_list.h rename to 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 similarity index 93% rename from common/patches/SoF_ops.h rename to common/patches/sof_ops.h index ac8dd2008..132e3dc77 100644 --- a/common/patches/SoF_ops.h +++ b/common/patches/sof_ops.h @@ -1,100 +1,99 @@ - -//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_AugmentInfo) +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 similarity index 97% rename from common/patches/SoF_structs.h rename to common/patches/sof_structs.h index a31d60ef8..9c3238f73 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]; }; /* @@ -554,8 +554,8 @@ struct GMTrainee_Struct { /*000*/ uint32 npcid; /*004*/ uint32 playerid; - /*008*/ uint32 skills[73]; - /*300*/ uint8 unknown300[148]; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[40]; /*448*/ }; @@ -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]; + }; }; /** @@ -722,7 +770,7 @@ struct BindStruct { static const uint32 MAX_PP_LANGUAGE = 25; // static const uint32 MAX_PP_SPELLBOOK = 480; // Confirmed 60 pages on Live now static const uint32 MAX_PP_MEMSPELL = 10; //was 9 now 10 on Live -static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size +static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 static const uint32 MAX_GROUP_MEMBERS = 6; static const uint32 MAX_RECAST_TYPES = 20; @@ -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 @@ -3726,7 +3789,7 @@ struct ItemSerializationHeader uint32 slot; uint32 price; uint32 merchant_slot; //1 if not a merchant item - uint32 unknown020; //0 + uint32 scaled_value; //0 uint32 instance_id; //unique instance id if not merchant item, else is merchant slot uint32 unknown028; //0 uint32 last_cast_time; // Unix Time from PP of last cast for this recast type if recast delay > 0 @@ -3963,10 +4026,10 @@ 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 -/*076*/ +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/patches/SSDeclare.h b/common/patches/ss_declare.h similarity index 100% rename from common/patches/SSDeclare.h rename to common/patches/ss_declare.h diff --git a/common/patches/SSDefine.h b/common/patches/ss_define.h similarity index 100% rename from common/patches/SSDefine.h rename to common/patches/ss_define.h diff --git a/common/patches/SSRegister.h b/common/patches/ss_register.h similarity index 100% rename from common/patches/SSRegister.h rename to common/patches/ss_register.h diff --git a/common/patches/template.cpp b/common/patches/template.cpp index 219e30f71..8c4be992c 100644 --- a/common/patches/template.cpp +++ b/common/patches/template.cpp @@ -2,7 +2,7 @@ #include "TEMPLATE.h" #include "../opcodemgr.h" #include "../logsys.h" -#include "../EQStreamIdent.h" +#include "../eq_stream_ident.h" #include "../eq_packet_structs.h" #include "TEMPLATE_structs.h" @@ -68,7 +68,7 @@ Strategy::Strategy() : StructStrategy() { //all opcodes default to passthrough. - #include "SSRegister.h" + #include "ss_register.h" #include "TEMPLATE_ops.h" } @@ -80,7 +80,7 @@ std::string Strategy::Describe() const { } -#include "SSDefine.h" +#include "ss_define.h" /*ENCODE(OP_PlayerProfile) { diff --git a/common/patches/template.h b/common/patches/template.h index 16f869fc1..1391516b1 100644 --- a/common/patches/template.h +++ b/common/patches/template.h @@ -1,7 +1,7 @@ #ifndef TEMPLATE_H_ #define TEMPLATE_H_ -#include "../StructStrategy.h" +#include "../struct_strategy.h" class EQStreamIdentifier; @@ -25,7 +25,7 @@ namespace TEMPLATE { virtual std::string Describe() const; virtual const EQClientVersion ClientVersion() const; //magic macro to declare our opcodes - #include "SSDeclare.h" + #include "ss_declare.h" #include "TEMPLATE_ops.h" }; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp new file mode 100644 index 000000000..4d400c9b7 --- /dev/null +++ b/common/patches/titanium.cpp @@ -0,0 +1,1767 @@ +#include "../debug.h" +#include "titanium.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../eq_stream_ident.h" +#include "../crc32.h" +#include "../races.h" + +#include "../eq_packet_structs.h" +#include "../string_util.h" +#include "../item.h" +#include "titanium_structs.h" +#include + +namespace Titanium +{ + 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); + + // server to client inventory location converters + static inline int16 ServerToTitaniumSlot(uint32 ServerSlot); + static inline int16 ServerToTitaniumCorpseSlot(uint32 ServerCorpse); + + // 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); + } + } + + 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; + } + +#include "ss_define.h" + +// ENCODE methods + EAT_ENCODE(OP_GuildMemberLevelUpdate); // added ; + + EAT_ENCODE(OP_ZoneServerReady); // added ; + + 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_AdventureMerchantSell) + { + 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(); + } + + ENCODE(OP_ApplyPoison) + { + ENCODE_LENGTH_EXACT(ApplyPoison_Struct); + SETUP_DIRECT_ENCODE(ApplyPoison_Struct, structs::ApplyPoison_Struct); + + eq->inventorySlot = ServerToTitaniumSlot(emu->inventorySlot); + OUT(success); + + 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; 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); + } + + ENCODE(OP_BecomeTrader) + { + ENCODE_LENGTH_EXACT(BecomeTrader_Struct); + SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct); + + OUT(ID); + OUT(Code); + + FINISH_ENCODE(); + } + + 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_DeleteCharge) { ENCODE_FORWARD(OP_MoveItem); } + + ENCODE(OP_DeleteItem) + { + ENCODE_LENGTH_EXACT(DeleteItem_Struct); + SETUP_DIRECT_ENCODE(DeleteItem_Struct, structs::DeleteItem_Struct); + + eq->from_slot = ServerToTitaniumSlot(emu->from_slot); + eq->to_slot = ServerToTitaniumSlot(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) + { + 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 (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 + + 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); + + 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_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_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; + + //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->GetUnscaledItem(); + 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(), + inst->IsScaling() ? inst->GetExp() / 100 : 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 +#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) ? "\"" : "" + ); + + for (i = 0; i<10; i++) { + if (sub_items[i]) + safe_delete_array(sub_items[i]); + } + + 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 diff --git a/common/patches/Titanium.h b/common/patches/titanium.h similarity index 87% rename from common/patches/Titanium.h rename to common/patches/titanium.h index 337556938..421ef319a 100644 --- a/common/patches/Titanium.h +++ b/common/patches/titanium.h @@ -1,7 +1,7 @@ #ifndef Titanium_H_ #define Titanium_H_ -#include "../StructStrategy.h" +#include "../struct_strategy.h" class EQStreamIdentifier; @@ -26,8 +26,8 @@ namespace Titanium { virtual const EQClientVersion ClientVersion() const; //magic macro to declare our opcode processors - #include "SSDeclare.h" - #include "Titanium_ops.h" + #include "ss_declare.h" + #include "titanium_ops.h" }; }; diff --git a/common/patches/Titanium_constants.h b/common/patches/titanium_constants.h similarity index 98% rename from common/patches/Titanium_constants.h rename to 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_itemfields.h b/common/patches/titanium_itemfields.h similarity index 100% rename from common/patches/Titanium_itemfields.h rename to common/patches/titanium_itemfields.h diff --git a/common/patches/Titanium_ops.h b/common/patches/titanium_ops.h similarity index 66% rename from common/patches/Titanium_ops.h rename to 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 similarity index 97% rename from common/patches/Titanium_structs.h rename to common/patches/titanium_structs.h index 1c058fb77..b9d299ab9 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]; }; /* @@ -485,8 +485,8 @@ struct GMTrainee_Struct { /*000*/ uint32 npcid; /*004*/ uint32 playerid; - /*008*/ uint32 skills[73]; - /*300*/ uint8 unknown300[148]; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[40]; /*448*/ }; @@ -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]; + }; }; /** @@ -652,7 +700,7 @@ struct BindStruct { static const uint32 MAX_PP_LANGUAGE = 28; static const uint32 MAX_PP_SPELLBOOK = 400; static const uint32 MAX_PP_MEMSPELL = 9; -static const uint32 MAX_PP_SKILL = _SkillPacketArraySize; // 100 - actual skills buffer size +static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 240; static const uint32 MAX_GROUP_MEMBERS = 6; static const uint32 MAX_RECAST_TYPES = 20; @@ -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 new file mode 100644 index 000000000..c9d726a25 --- /dev/null +++ b/common/patches/underfoot.cpp @@ -0,0 +1,4159 @@ +#include "../debug.h" +#include "underfoot.h" +#include "../opcodemgr.h" +#include "../logsys.h" +#include "../eq_stream_ident.h" +#include "../crc32.h" + +#include "../eq_packet_structs.h" +#include "../misc_functions.h" +#include "../string_util.h" +#include "../item.h" +#include "underfoot_structs.h" +#include "../rulesys.h" + +#include +#include + +namespace Underfoot +{ + 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); + + // server to client inventory location converters + static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot); + static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse); + + // 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); + } + } + + 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; + } + +#include "ss_define.h" + +// ENCODE methods + 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_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_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_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_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_AugmentInfo) + { + ENCODE_LENGTH_EXACT(AugmentInfo_Struct); + SETUP_DIRECT_ENCODE(AugmentInfo_Struct, structs::AugmentInfo_Struct); + + OUT(itemid); + OUT(window); + strn0cpy(eq->augment_info, emu->augment_info, 64); + + 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(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(); + } + + 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); + __packet->WriteUInt8(emu->all_buffs); // 1 = all buffs, 0 = 1 buff + __packet->WriteUInt16(emu->count); + + 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; + } + + __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(""); + } + __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) + { + 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_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_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++; + + //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) { + _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; + + //_log(NET__STRUCTS, "Spawn packet size is %i, entries = %i", in->size, entrycount); + + char *Buffer = (char *)in->pBuffer; + + int r; + int k; + 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)) + PacketSize += strlen(emu->title) + 1; + + if (strlen(emu->suffix)) + PacketSize += strlen(emu->suffix) + 1; + + if (emu->DestructibleObject) + { + PacketSize = PacketSize - 4; // No bodytype + PacketSize += 53; // Fixed portion + PacketSize += strlen(emu->DestructibleModel) + 1; + PacketSize += strlen(emu->DestructibleName2) + 1; + PacketSize += strlen(emu->DestructibleString) + 1; + } + + bool ShowName = 1; + if (emu->bodytype >= 66) + { + emu->race = 127; + emu->bodytype = 11; + emu->gender = 0; + ShowName = 0; + } + + float SpawnSize = emu->size; + 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) + { + emu->size = 6; + SpawnSize = 6; + } + } + + if (SpawnSize == 0) + { + SpawnSize = 3; + } + + EQApplicationPacket *outapp = new EQApplicationPacket(OP_ZoneEntry, PacketSize); + 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) + { + VARSTRUCT_ENCODE_TYPE(float, Buffer, 10); // was int and 0x41200000 + } + else + { + VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height? + } + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC); + + structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer; + + Bitfields->afk = 0; + Bitfields->linkdead = 0; + Bitfields->gender = emu->gender; + + Bitfields->invis = emu->invis; + Bitfields->sneak = 0; + Bitfields->lfg = emu->lfg; + Bitfields->gm = emu->gm; + Bitfields->anon = emu->anon; + Bitfields->showhelm = emu->showhelm; + Bitfields->targetable = 1; + Bitfields->targetable_with_hotkey = (emu->IsMercenary ? 0 : 1); + Bitfields->statue = 0; + Bitfields->trader = 0; + Bitfields->buyer = 0; + + Bitfields->showname = ShowName; + + if (emu->DestructibleObject) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x1d600000); + Buffer = Buffer - 4; + } + + Bitfields->ispet = emu->is_pet; + + Buffer += sizeof(structs::Spawn_Struct_Bitfields); + + uint8 OtherData = 0; + + if (strlen(emu->title)) + OtherData = OtherData | 0x04; + + if (strlen(emu->suffix)) + OtherData = OtherData | 0x08; + + if (emu->DestructibleObject) + OtherData = OtherData | 0xd1; // Live has 0xe1 for OtherData + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData); + + if (emu->DestructibleObject) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); + } + else + { + VARSTRUCT_ENCODE_TYPE(float, Buffer, -1); // unknown3 + } + VARSTRUCT_ENCODE_TYPE(float, Buffer, 0); // unknown4 + + if (emu->DestructibleObject) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleModel); + VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleName2); + VARSTRUCT_ENCODE_STRING(Buffer, emu->DestructibleString); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleAppearance); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk1); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID1); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID2); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID3); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleID4); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk2); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk3); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk4); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk5); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk6); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk7); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->DestructibleUnk8); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->DestructibleUnk9); + } + + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->size); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->face); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->walkspeed); + VARSTRUCT_ENCODE_TYPE(float, Buffer, emu->runspeed); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->race); + /* + if(emu->bodytype >=66) + { + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // showname + } + else + { + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // showname + }*/ + + + 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. + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 1); // This is a properties count field + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->bodytype); + } + else + { + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); + } + + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->curHp); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->haircolor); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beardcolor); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor1); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->eyecolor2); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->hairstyle); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->beard); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_heritage); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->drakkin_tattoo); + 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) + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xFFFFFFFF); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0x00000000); + } + else + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildID); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->guildrank); + } + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->class_); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // pvp + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->StandState); // standstate + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->light); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->flymode); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->equip_chest2); // unknown8 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown9 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown10 + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->helm); // unknown11 + VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown12 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId); + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // unknown13 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown14 - Stance 64 = normal 4 = aggressive 40 = stun/mezzed + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown15 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown16 + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // unknown17 + 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; + Position->deltaHeading = emu->deltaHeading; + Position->deltaY = emu->deltaY; + Position->y = emu->y; + Position->animation = emu->animation; + Position->heading = emu->heading; + Position->x = emu->x; + Position->z = emu->z; + Position->deltaZ = emu->deltaZ; + + 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)) + { + for (k = 0; k < 9; ++k) + { + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color); + } + } + } + else + { + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialPrimary]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->equipment[MaterialSecondary]); + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); + 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)) + { + structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer; + + for (k = 0; k < 9; k++) { + Equipment[k].equip0 = emu->equipment[k]; + Equipment[k].equip1 = 0; + Equipment[k].itemId = 0; + } + + Buffer += (sizeof(structs::EquipStruct) * 9); + } + if (strlen(emu->title)) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->title); + } + + if (strlen(emu->suffix)) + { + VARSTRUCT_ENCODE_STRING(Buffer, emu->suffix); + } + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; + VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // Unknown; + VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->IsMercenary); //IsMercenary + Buffer += 28; // Unknown; + + dest->FastQueuePacket(&outapp, ack_req); + } + + delete in; + } + +// 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 = UnderfootToServerSlot(eq->slot); + IN(charges); + IN(sell_price); + + 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(); + } + + 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); + IN(cs_unknown1); + IN(cs_unknown2); + IN(y_pos); + IN(x_pos); + IN(z_pos); + + 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_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); + + 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_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) + { + case 0x00: + emu->command = 0x04; // Health + break; + case 0x01: + emu->command = 0x10; // Leader + break; + case 0x02: + emu->command = 0x07; // Attack + break; + case 0x04: + emu->command = 0x08; // Follow + break; + case 0x05: + emu->command = 0x05; // Guard + break; + case 0x06: + emu->command = 0x09; // Sit. Needs work. This appears to be a toggle between Sit/Stand now. + break; + case 0x0c: + emu->command = 0x0b; // Taunt + break; + case 0x0f: + emu->command = 0x0c; // Hold + break; + case 0x10: + emu->command = 0x1b; // Hold on + break; + case 0x11: + emu->command = 0x1c; // Hold off + break; + case 0x1c: + emu->command = 0x01; // Back + break; + case 0x1d: + emu->command = 0x02; // Leave/Go Away + break; + case 0x15: + emu->command = 0x12; // No Cast - /command + break; + case 0x16: + emu->command = 0x12; // No Cast - Pet Window + break; + case 0x18: + emu->command = 0x13; // Focus - Pet Window + break; + default: + emu->command = eq->command; + } + OUT(unknown); + + 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 = 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(); + } + + 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 = UnderfootToServerSlot(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 = UnderfootToServerSlot(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 = UnderfootToServerSlot(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) + { + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); + 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->GetUnscaledItem(); + //_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.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 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.isEvolving = item->EvolvingLevel > 0 ? 1 : 0; + ss.write((const char*)&hdr, sizeof(Underfoot::structs::ItemSerializationHeader)); + + if (item->EvolvingLevel > 0) { + Underfoot::structs::EvolvingItem evotop; + evotop.unknown001 = 0; + evotop.unknown002 = 0; + evotop.unknown003 = 0; + evotop.unknown004 = 0; + evotop.evoLevel = item->EvolvingLevel; + evotop.progress = 95.512; + evotop.Activated = 1; + evotop.evomaxlevel = 7; + ss.write((const char*)&evotop, sizeof(Underfoot::structs::EvolvingItem)); + } + //ORNAMENT IDFILE / ICON - + uint16 ornaIcon = 0; + if (inst->GetOrnamentationAug(ornamentationAugtype)) { + const Item_Struct *aug_weap = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + ss.write(aug_weap->IDFile, strlen(aug_weap->IDFile)); + ss.write((const char*)&null_term, sizeof(uint8)); + ornaIcon = aug_weap->Icon; + } + else if (inst->GetOrnamentationIDFile() && inst->GetOrnamentationIcon()) { + char tmp[30]; memset(tmp, 0x0, 30); sprintf(tmp, "IT%d", inst->GetOrnamentationIDFile()); + ss.write(tmp, strlen(tmp)); + ss.write((const char*)&null_term, sizeof(uint8)); + ornaIcon = inst->GetOrnamentationIcon(); + } + else { + ss.write((const char*)&null_term, sizeof(uint8)); //no idfile + } + + Underfoot::structs::ItemSerializationHeaderFinish hdrf; + hdrf.ornamentIcon = ornaIcon; + hdrf.unknown060 = 0; //This is Always 0.. or it breaks shit.. + hdrf.unknown061 = 0; //possibly ornament / special ornament + hdrf.isCopied = 0; //Flag for item to be 'Copied' + hdrf.ItemClass = item->ItemClass; + ss.write((const char*)&hdrf, sizeof(Underfoot::structs::ItemSerializationHeaderFinish)); + + 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.CharmFileID = item->CharmFileID; + 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 */ + ss.write((const char*)&null_term, sizeof(uint8)); + + ss.write((const char*)&effect_unknown, sizeof(int32)); // unknown6 + // End of Effects + + Underfoot::structs::ItemQuaternaryBodyStruct iqbs; + memset(&iqbs, 0, sizeof(Underfoot::structs::ItemQuaternaryBodyStruct)); + + 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.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(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; + } + + static inline uint32 ServerToUnderfootSlot(uint32 ServerSlot) + { + uint32 UnderfootSlot = 0; + + 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; + + return UnderfootSlot; + } + + static inline uint32 ServerToUnderFootCorpseSlot(uint32 ServerCorpse) + { + //uint32 UnderfootCorpse; + return (ServerCorpse + 1); + } + + 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); + } +} +// end namespace Underfoot diff --git a/common/patches/Underfoot.h b/common/patches/underfoot.h similarity index 87% rename from common/patches/Underfoot.h rename to common/patches/underfoot.h index fb66a3488..b14d4a420 100644 --- a/common/patches/Underfoot.h +++ b/common/patches/underfoot.h @@ -1,7 +1,7 @@ #ifndef Underfoot_H_ #define Underfoot_H_ -#include "../StructStrategy.h" +#include "../struct_strategy.h" class EQStreamIdentifier; @@ -26,8 +26,8 @@ namespace Underfoot { virtual const EQClientVersion ClientVersion() const; //magic macro to declare our opcode processors - #include "SSDeclare.h" - #include "Underfoot_ops.h" + #include "ss_declare.h" + #include "underfoot_ops.h" }; }; diff --git a/common/patches/Underfoot_constants.h b/common/patches/underfoot_constants.h similarity index 98% rename from common/patches/Underfoot_constants.h rename to 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_itemfields.h b/common/patches/underfoot_itemfields.h similarity index 100% rename from common/patches/Underfoot_itemfields.h rename to common/patches/underfoot_itemfields.h diff --git a/common/patches/Underfoot_ops.h b/common/patches/underfoot_ops.h similarity index 94% rename from common/patches/Underfoot_ops.h rename to common/patches/underfoot_ops.h index 7aa9caf50..cfb4d4fe9 100644 --- a/common/patches/Underfoot_ops.h +++ b/common/patches/underfoot_ops.h @@ -1,128 +1,128 @@ - -//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_AugmentInfo) +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 similarity index 96% rename from common/patches/Underfoot_structs.h rename to common/patches/underfoot_structs.h index ee7286dab..45c47875d 100644 --- a/common/patches/Underfoot_structs.h +++ b/common/patches/underfoot_structs.h @@ -519,7 +519,11 @@ struct CastSpell_Struct uint32 spell_id; uint32 inventoryslot; // slot for clicky item, 0xFFFF = normal cast uint32 target_id; - uint32 cs_unknown[5]; + uint32 cs_unknown1; + uint32 cs_unknown2; + float y_pos; + float x_pos; + float z_pos; }; /* @@ -564,7 +568,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 +593,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; @@ -600,8 +623,8 @@ struct GMTrainee_Struct { /*000*/ uint32 npcid; /*004*/ uint32 playerid; - /*008*/ uint32 skills[73]; - /*300*/ uint8 unknown300[148]; + /*008*/ uint32 skills[PACKET_SKILL_ARRAY_SIZE]; + /*408*/ uint8 unknown408[40]; /*448*/ }; @@ -735,14 +758,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,9 +837,9 @@ 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_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size static const uint32 MAX_PP_AA_ARRAY = 300; //was 299 static const uint32 MAX_GROUP_MEMBERS = 6; static const uint32 MAX_RECAST_TYPES = 20; @@ -879,7 +950,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 +1576,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 +1610,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 +2170,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 +3208,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 +3247,7 @@ struct Split_Struct */ struct NewCombine_Struct { /*00*/ int16 container_slot; -/*02*/ char unknown02[2]; +/*02*/ int16 guildtribute_slot; /*04*/ }; @@ -3605,6 +3683,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 +3829,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 @@ -3912,25 +4005,42 @@ struct ItemVerifyReply_Struct { struct ItemSerializationHeader { -/*000*/ uint32 stacksize; -/*004*/ uint32 unknown004; -/*008*/ uint32 slot; -/*012*/ uint32 price; -/*016*/ uint32 merchant_slot; //1 if not a merchant item -/*020*/ uint32 unknown020; //0 -/*024*/ uint32 instance_id; //unique instance id if not merchant item, else is merchant slot -/*028*/ uint32 unknown028; //0 -/*032*/ uint32 last_cast_time; // Unix Time from PP of last cast for this recast type if recast delay > 0 -/*036*/ uint32 charges; //Total Charges an item has (-1 for unlimited) -/*040*/ uint32 inst_nodrop; // 1 if the item is no drop (attuned items) -/*044*/ uint32 unknown044; //0 -/*048*/ uint32 unknown048; //0 -/*052*/ uint32 unknown052; //0 -/*056*/ uint32 unknown056; //0 -/*060*/ uint8 unknown060; //0 -/*061*/ uint8 unknown061; //0 - Add Evolving Item struct if this isn't set to 0? -/*062*/ uint8 unknown062; // New to Underfoot -/*063*/ uint8 ItemClass; //0, 1, or 2 + /*000*/ uint32 stacksize; + /*004*/ uint32 unknown004; + /*008*/ uint32 slot; + /*012*/ uint32 price; + /*016*/ uint32 merchant_slot; //1 if not a merchant item + /*020*/ uint32 scaled_value; //0 + /*024*/ uint32 instance_id; //unique instance id if not merchant item, else is merchant slot + /*028*/ uint32 unknown028; //0 + /*032*/ uint32 last_cast_time; // Unix Time from PP of last cast for this recast type if recast delay > 0 + /*036*/ uint32 charges; //Total Charges an item has (-1 for unlimited) + /*040*/ uint32 inst_nodrop; // 1 if the item is no drop (attuned items) + /*044*/ uint32 unknown044; //0 + /*048*/ uint32 unknown048; //0 + /*052*/ uint32 unknown052; //0 + /*056*/ uint8 isEvolving; //0 // If 1 Add evolving item data in between Header and HeaderFinish +}; + +struct EvolvingItem { + uint8 unknown001; + uint8 unknown002; + uint8 unknown003; + uint8 unknown004; + int32 evoLevel; + double progress; + uint8 Activated; + int32 evomaxlevel; + uint8 unknown02[4]; +}; + +struct ItemSerializationHeaderFinish +{ + uint16 ornamentIcon; + /*060*/ uint8 unknown060; //0 + /*061*/ uint8 unknown061; //0 - + /*062*/ uint8 isCopied; // New to Underfoot // Copied flag on item + /*063*/ uint8 ItemClass; //0, 1, or 2 }; struct ItemBodyStruct @@ -4161,10 +4271,10 @@ 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 -/*076*/ +/*000*/ uint32 itemid; // id of the solvent needed +/*004*/ uint32 window; // window to display the information in +/*008*/ char augment_info[64]; // total packet length 76, all the rest were always 00 +/*072*/ uint32 unknown072; }; struct VeteranRewardItem diff --git a/common/perl_EQDB.cpp b/common/perl_eqdb.cpp similarity index 99% rename from common/perl_EQDB.cpp rename to common/perl_eqdb.cpp index 960809871..8e7fb1138 100644 --- a/common/perl_EQDB.cpp +++ b/common/perl_eqdb.cpp @@ -30,7 +30,7 @@ typedef const char Const_char; #ifdef EMBPERL #include "../common/debug.h" #include "../common/useperl.h" -#include "EQDB.h" +#include "eqdb.h" #ifdef THIS /* this macro seems to leak out on some systems */ #undef THIS diff --git a/common/perl_EQDBRes.cpp b/common/perl_eqdb_res.cpp similarity index 99% rename from common/perl_EQDBRes.cpp rename to common/perl_eqdb_res.cpp index 15979c3a7..b4f22affb 100644 --- a/common/perl_EQDBRes.cpp +++ b/common/perl_eqdb_res.cpp @@ -30,7 +30,7 @@ typedef const char Const_char; #ifdef EMBPERL #include "../common/debug.h" #include "../common/useperl.h" -#include "EQDBRes.h" +#include "eqdb_res.h" XS(XS_EQDBRes_num_rows); /* prototype to pass -Wmissing-prototypes */ diff --git a/common/ProcLauncher.cpp b/common/proc_launcher.cpp similarity index 99% rename from common/ProcLauncher.cpp rename to common/proc_launcher.cpp index da0cd81e9..0ea0d9636 100644 --- a/common/ProcLauncher.cpp +++ b/common/proc_launcher.cpp @@ -20,7 +20,7 @@ #include #include "debug.h" -#include "ProcLauncher.h" +#include "proc_launcher.h" #ifdef _WINDOWS #include #else diff --git a/common/ProcLauncher.h b/common/proc_launcher.h similarity index 100% rename from common/ProcLauncher.h rename to common/proc_launcher.h diff --git a/common/ptimer.cpp b/common/ptimer.cpp index 8e9f03dcf..c9b25b1a5 100644 --- a/common/ptimer.cpp +++ b/common/ptimer.cpp @@ -20,7 +20,7 @@ #include "timer.h" #include "ptimer.h" #include "database.h" -#include "StringUtil.h" +#include "string_util.h" #include #include #include @@ -127,96 +127,75 @@ PersistentTimer::PersistentTimer(uint32 char_id, pTimerType type, uint32 in_star } bool PersistentTimer::Load(Database *db) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - uint32 qlen = 0; - uint32 qcount = 0; - - qlen = MakeAnyLenString(&query, "SELECT start,duration,enable " - " FROM timers WHERE char_id=%lu AND type=%u", (unsigned long)_char_id, _type); #ifdef DEBUG_PTIMERS printf("Loading timer: char %lu of type %u\n", (unsigned long)_char_id, _type); #endif - - if (!db->RunQuery(query, qlen, errbuf, &result)) { - safe_delete_array(query); + std::string query = StringFormat("SELECT start, duration, enable " + "FROM timers WHERE char_id=%lu AND type=%u", + (unsigned long)_char_id, _type); + auto results = db->QueryDatabase(query); + if (!results.Success()) { #if EQDEBUG > 5 - LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", errbuf); + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", results.ErrorMessage().c_str()); #endif - return(false); + return false; } - safe_delete_array(query); - bool res = false; - qcount = mysql_num_rows(result); - if(qcount == 1 && (row = mysql_fetch_row(result)) ) { - start_time = strtoul(row[0], nullptr, 10); - timer_time = strtoul(row[1], nullptr, 10); - enabled = (row[2][0] == '1'); + if (results.RowCount() != 1) + return false; - res = true; - } - mysql_free_result(result); + auto row = results.begin(); - return(res); + start_time = strtoul(row[0], nullptr, 10); + timer_time = strtoul(row[1], nullptr, 10); + enabled = (row[2][0] == '1'); + + return true; } bool PersistentTimer::Store(Database *db) { if(Expired(db, false)) //dont need to store expired timers. - return(true); - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 qlen = 0; - - qlen = MakeAnyLenString(&query, "REPLACE INTO timers " - " (char_id,type,start,duration,enable) " - " VALUES(%lu,%u,%lu,%lu,%d)", - (unsigned long)_char_id, _type, (unsigned long)start_time, (unsigned long)timer_time, enabled?1:0); + return true; + std::string query = StringFormat("REPLACE INTO timers " + " (char_id, type, start, duration, enable) " + " VALUES (%lu, %u, %lu, %lu, %d)", + (unsigned long)_char_id, _type, (unsigned long)start_time, + (unsigned long)timer_time, enabled ? 1: 0); #ifdef DEBUG_PTIMERS - printf("Storing timer: char %lu of type %u: '%s'\n", (unsigned long)_char_id, _type, query); + printf("Storing timer: char %lu of type %u: '%s'\n", (unsigned long)_char_id, _type, query.c_str()); #endif - - if (!db->RunQuery(query, qlen, errbuf)) { - safe_delete_array(query); + auto results = db->QueryDatabase(query); + if (!results.Success()) { #if EQDEBUG > 5 - LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Store, error: %s", errbuf); + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Store, error: %s", results.ErrorMessage().c_str()); #endif - return(false); + return false; } - safe_delete_array(query); - return(true); + return true; } bool PersistentTimer::Clear(Database *db) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 qlen = 0; - - qlen = MakeAnyLenString(&query, "DELETE FROM timers " - " WHERE char_id=%lu AND type=%u ", - (unsigned long)_char_id, _type); + std::string query = StringFormat("DELETE FROM timers " + "WHERE char_id = %lu AND type = %u ", + (unsigned long)_char_id, _type); #ifdef DEBUG_PTIMERS - printf("Clearing timer: char %lu of type %u: '%s'\n", (unsigned long)_char_id, _type, query); + printf("Clearing timer: char %lu of type %u: '%s'\n", (unsigned long)_char_id, _type, query.c_str()); #endif - if (!db->RunQuery(query, qlen, errbuf)) { - safe_delete_array(query); + auto results = db->QueryDatabase(query); + if (!results.Success()) { #if EQDEBUG > 5 - LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", errbuf); + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", results.ErrorMessage().c_str()); #endif - return(false); + return false; } - safe_delete_array(query); - return(true); + return true; } @@ -300,61 +279,47 @@ PTimerList::~PTimerList() { bool PTimerList::Load(Database *db) { - std::map::iterator s; - s = _list.begin(); - while(s != _list.end()) { - if(s->second != nullptr) - delete s->second; - ++s; - } + + for (auto timerIterator = _list.begin(); timerIterator != _list.end(); ++timerIterator) + if(timerIterator->second != nullptr) + delete timerIterator->second; _list.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - char *query = 0; - uint32 qlen = 0; - uint32 qcount = 0; - - qlen = MakeAnyLenString(&query, "SELECT type,start,duration,enable " - " FROM timers WHERE char_id=%lu", (unsigned long)_char_id); - #ifdef DEBUG_PTIMERS printf("Loading all timers for char %lu\n", (unsigned long)_char_id); #endif - - if (!db->RunQuery(query, qlen, errbuf, &result)) { - safe_delete_array(query); + std::string query = StringFormat("SELECT type, start, duration, enable " + "FROM timers WHERE char_id = %lu", + (unsigned long)_char_id); + auto results = db->QueryDatabase(query); + if (!results.Success()) { #if EQDEBUG > 5 - LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", errbuf); + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Load, error: %s", results.ErrorMessage().c_str()); #endif - return(false); + return false; } - safe_delete_array(query); pTimerType type; uint32 start_time, timer_time; bool enabled; PersistentTimer *cur; - qcount = mysql_num_rows(result); - while((row = mysql_fetch_row(result)) ) { + + for (auto row = results.begin(); row != results.end(); ++row) { type = atoi(row[0]); start_time = strtoul(row[1], nullptr, 10); timer_time = strtoul(row[2], nullptr, 10); enabled = (row[3][0] == '1'); //if it expired allready, dont bother. - cur = new PersistentTimer(_char_id, type, start_time, timer_time, enabled); if(!cur->Expired(nullptr)) _list[type] = cur; else delete cur; } - mysql_free_result(result); - return(true); + return true; } bool PTimerList::Store(Database *db) { @@ -381,27 +346,19 @@ bool PTimerList::Store(Database *db) { bool PTimerList::Clear(Database *db) { _list.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 qlen = 0; - - qlen = MakeAnyLenString(&query, "DELETE FROM timers " - " WHERE char_id=%lu ", (unsigned long)_char_id); - + std::string query = StringFormat("DELETE FROM timers WHERE char_id=%lu ", (unsigned long)_char_id); #ifdef DEBUG_PTIMERS - printf("Storing all timers for char %lu: '%s'\n", (unsigned long)_char_id, query); + printf("Storing all timers for char %lu: '%s'\n", (unsigned long)_char_id, query.c_str()); #endif - - if (!db->RunQuery(query, qlen, errbuf)) { - safe_delete_array(query); + auto results = db->QueryDatabase(query); + if (!results.Success()) { #if EQDEBUG > 5 - LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", errbuf); + LogFile->write(EQEMuLog::Error, "Error in PersistentTimer::Clear, error: %s", results.ErrorMessage().c_str()); #endif - return(false); + return false; } - safe_delete_array(query); - return(true); + return true; } void PTimerList::Start(pTimerType type, uint32 duration) { @@ -479,26 +436,19 @@ void PTimerList::ToVector(std::vector< std::pair } bool PTimerList::ClearOffline(Database *db, uint32 char_id, pTimerType type) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 qlen = 0; - qlen = MakeAnyLenString(&query, "DELETE FROM timers WHERE char_id=%lu AND type=%u ",(unsigned long)char_id, type); + std::string query = StringFormat("DELETE FROM timers WHERE char_id=%lu AND type=%u ",(unsigned long)char_id, type); #ifdef DEBUG_PTIMERS - printf("Clearing timer (offline): char %lu of type %u: '%s'\n", (unsigned long)char_id, type, query); + printf("Clearing timer (offline): char %lu of type %u: '%s'\n", (unsigned long)char_id, type, query.c_str()); #endif - - if (!db->RunQuery(query, qlen, errbuf)) { - safe_delete_array(query); + auto results = db->QueryDatabase(query); + if (!results.Success()) { #if EQDEBUG > 5 - LogFile->write(EQEMuLog::Error, "Error in PTimerList::ClearOffline, error: %s", errbuf); + LogFile->write(EQEMuLog::Error, "Error in PTimerList::ClearOffline, error: %s", results.ErrorMessage().c_str()); #endif - return(false); + return false; } - safe_delete_array(query); - - return(true); - + return true; } diff --git a/common/rulesys.cpp b/common/rulesys.cpp index 1f4ee8edf..95f22754c 100644 --- a/common/rulesys.cpp +++ b/common/rulesys.cpp @@ -19,7 +19,7 @@ #include "rulesys.h" #include "logsys.h" #include "database.h" -#include "StringUtil.h" +#include "string_util.h" #include #include @@ -266,10 +266,6 @@ void RuleManager::SaveRules(Database *db, const char *ruleset) { bool RuleManager::LoadRules(Database *db, const char *ruleset) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; int rsid = GetRulesetID(db, ruleset); if(rsid < 0) { @@ -282,24 +278,19 @@ bool RuleManager::LoadRules(Database *db, const char *ruleset) { m_activeRuleset = rsid; m_activeName = ruleset; - if (db->RunQuery(query, MakeAnyLenString(&query, - "SELECT rule_name, rule_value" - " FROM rule_values" - " WHERE ruleset_id=%d", rsid), errbuf, &result)) + std::string query = StringFormat("SELECT rule_name, rule_value FROM rule_values WHERE ruleset_id=%d", rsid); + auto results = db->QueryDatabase(query); + if (!results.Success()) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - if(!SetRule(row[0], row[1], nullptr, false)) - _log(RULES__ERROR, "Unable to interpret rule record for %s", row[0]); - } - mysql_free_result(result); - } else { - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error in LoadRules query %s: %s", query, errbuf); - return(false); + LogFile->write(EQEMuLog::Error, "Error in LoadRules query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - return(true); + for(auto row = results.begin(); row != results.end(); ++row) + if(!SetRule(row[0], row[1], nullptr, false)) + _log(RULES__ERROR, "Unable to interpret rule record for %s", row[0]); + + return true; } void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) { @@ -317,127 +308,96 @@ void RuleManager::_SaveRule(Database *db, RuleType type, uint16 index) { break; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!db->RunQuery(query, MakeAnyLenString(&query, - "REPLACE INTO rule_values (ruleset_id, rule_name, rule_value) " - " VALUES(%d, '%s', '%s')", - m_activeRuleset, _GetRuleName(type, index), vstr),errbuf)) - { - _log(RULES__ERROR, "Fauled to set rule in the database: %s: %s", query,errbuf); - } - safe_delete_array(query); + std::string query = StringFormat("REPLACE INTO rule_values " + "(ruleset_id, rule_name, rule_value) " + " VALUES(%d, '%s', '%s')", + m_activeRuleset, _GetRuleName(type, index), vstr); + auto results = db->QueryDatabase(query); + if (!results.Success()) + _log(RULES__ERROR, "Fauled to set rule in the database: %s: %s", query.c_str(), results.ErrorMessage().c_str()); + } int RuleManager::GetRulesetID(Database *db, const char *rulesetname) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; uint32 len = strlen(rulesetname); char* rst = new char[2*len+1]; db->DoEscapeString(rst, rulesetname, len); - int res = -1; + std::string query = StringFormat("SELECT ruleset_id FROM rule_sets WHERE name='%s'", rst); + safe_delete_array(rst); + auto results = db->QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadRules query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return -1; + } - if (db->RunQuery(query, MakeAnyLenString(&query, - "SELECT ruleset_id" - " FROM rule_sets" - " WHERE name='%s'", rst), errbuf, &result)) - { - if((row = mysql_fetch_row(result))) { - res = atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadRules query %s: %s", query, errbuf); - } - safe_delete_array(query); - safe_delete_array(rst); + if (results.RowCount() == 0) + return -1; - return(res); + auto row = results.begin(); + + return atoi(row[0]); } int RuleManager::_FindOrCreateRuleset(Database *db, const char *ruleset) { - int res; - res = GetRulesetID(db, ruleset); + int res = GetRulesetID(db, ruleset); if(res >= 0) - return(res); //found and existing one... + return res; //found and existing one... uint32 len = strlen(ruleset); char* rst = new char[2*len+1]; db->DoEscapeString(rst, ruleset, len); - uint32 new_id; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!db->RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO rule_sets (ruleset_id, name) " - " VALUES(0, '%s')", - rst),errbuf,nullptr,nullptr,&new_id)) + std::string query = StringFormat("INSERT INTO rule_sets (ruleset_id, name) VALUES(0, '%s')", rst); + safe_delete_array(rst); + auto results = db->QueryDatabase(query); + if (!results.Success()) { - _log(RULES__ERROR, "Fauled to create rule set in the database: %s: %s", query,errbuf); - res = -1; - } else { - res = new_id; + _log(RULES__ERROR, "Fauled to create rule set in the database: %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return -1; } - safe_delete_array(query); - return(res); + return results.LastInsertedID(); } std::string RuleManager::GetRulesetName(Database *db, int id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - std::string res; - - if (db->RunQuery(query, MakeAnyLenString(&query, - "SELECT name" - " FROM rule_sets" - " WHERE ruleset_id=%d", id), errbuf, &result)) + std::string query = StringFormat("SELECT name FROM rule_sets WHERE ruleset_id=%d", id); + auto results = db->QueryDatabase(query); + if (!results.Success()) { - if((row = mysql_fetch_row(result))) { - res = row[0]; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadRules query %s: %s", query, errbuf); + LogFile->write(EQEMuLog::Error, "Error in LoadRules query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return ""; } - safe_delete_array(query); - return(res); + if (results.RowCount() == 0) + return ""; + + auto row = results.begin(); + + return row[0]; } bool RuleManager::ListRulesets(Database *db, std::map &into) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; //start out with the default set which is always present. into[0] = "default"; - if (db->RunQuery(query, MakeAnyLenString(&query, - "SELECT ruleset_id,name" - " FROM rule_sets"), errbuf, &result)) + std::string query = "SELECT ruleset_id, name FROM rule_sets"; + auto results = db->QueryDatabase(query); + if (results.Success()) { - while((row = mysql_fetch_row(result))) { - into[ atoi(row[0]) ] = row[1]; - } - mysql_free_result(result); - safe_delete_array(query); - } else { - LogFile->write(EQEMuLog::Error, "Error in ListRulesets query %s: %s", query, errbuf); - safe_delete_array(query); - return(false); + LogFile->write(EQEMuLog::Error, "Error in ListRulesets query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - return(true); + + for (auto row = results.begin(); row != results.end(); ++row) + into[ atoi(row[0]) ] = row[1]; + + return true; } int32 RuleManager::GetIntRule(RuleManager::IntType t) const diff --git a/common/ruletypes.h b/common/ruletypes.h index 91ade257b..8ff077809 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -100,6 +100,7 @@ RULE_BOOL ( Character, KeepLevelOverMax, false) // Don't delevel a character tha RULE_INT ( Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update RULE_INT ( Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well. RULE_INT ( Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225. +RULE_INT ( Character, OrnamentationAugmentType, 20) //Ornamentation Augment Type RULE_CATEGORY_END() RULE_CATEGORY( Mercs ) @@ -123,7 +124,6 @@ RULE_INT ( Guild, PlayerCreationLimit, 1) // Only allow use of the UF+ window i RULE_INT ( Guild, PlayerCreationRequiredStatus, 0) // Required admin status. RULE_INT ( Guild, PlayerCreationRequiredLevel, 0) // Required Level of the player attempting to create the guild. RULE_INT ( Guild, PlayerCreationRequiredTime, 0) // Required Time Entitled On Account (in Minutes) to create the guild. - RULE_CATEGORY_END() RULE_CATEGORY( Skills ) @@ -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 ) @@ -212,10 +215,6 @@ RULE_REAL ( Map, FixPathingZMaxDeltaMoving, 20 ) //at runtime while pathing: max RULE_REAL ( Map, FixPathingZMaxDeltaWaypoint, 20 ) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply. RULE_REAL ( Map, FixPathingZMaxDeltaSendTo, 20 ) //at runtime in SendTo: max change in Z to allow the BestZ code to apply. RULE_REAL ( Map, FixPathingZMaxDeltaLoading, 45 ) //while loading each waypoint: max change in Z to allow the BestZ code to apply. -RULE_BOOL ( Map, UseClosestZ, false) // Move mobs to the nearest Z above or below, rather than just the nearest below. - // Only set UseClosestZ true if all your .map files generated from EQGs were created - // with azone2. - // RULE_INT ( Map, FindBestZHeightAdjust, 1) // Adds this to the current Z before seeking the best Z position RULE_CATEGORY_END() @@ -248,7 +247,6 @@ RULE_INT ( Pathing, CullNodesFromStart, 1) // Checks LOS from Start point to se RULE_INT ( Pathing, CullNodesFromEnd, 1) // Checks LOS from End point to second to last node for this many nodes and removes last node if there is LOS RULE_REAL ( Pathing, CandidateNodeRangeXY, 400) // When searching for path start/end nodes, only nodes within this range will be considered. RULE_REAL ( Pathing, CandidateNodeRangeZ, 10) // When searching for path start/end nodes, only nodes within this range will be considered. - RULE_CATEGORY_END() RULE_CATEGORY( Watermap ) @@ -324,7 +322,9 @@ 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_BOOL ( Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning. RULE_CATEGORY_END() RULE_CATEGORY( Combat ) @@ -419,6 +419,7 @@ RULE_INT ( Combat, ArcheryBonusChance, 50) RULE_INT ( Combat, BerserkerFrenzyStart, 35) RULE_INT ( Combat, BerserkerFrenzyEnd, 45) RULE_BOOL ( Combat, OneProcPerWeapon, true) //If enabled, One proc per weapon per round +RULE_BOOL ( Combat, ProjectileDmgOnImpact, true) //If enabled, projectiles (ie arrows) will hit on impact, instead of instantly. RULE_CATEGORY_END() RULE_CATEGORY( NPC ) @@ -437,6 +438,7 @@ RULE_BOOL ( NPC, SmartLastFightingDelayMoving, true) RULE_BOOL ( NPC, ReturnNonQuestNoDropItems, false) // Returns NO DROP items on NPCs that don't have an EVENT_TRADE sub in their script RULE_INT ( NPC, StartEnrageValue, 9) // % HP that an NPC will begin to enrage RULE_BOOL ( NPC, LiveLikeEnrage, false) // If set to true then only player controlled pets will enrage +RULE_BOOL ( NPC, EnableMeritBasedFaction, false) // If set to true, faction will given in the same way as experience (solo/group/raid) RULE_CATEGORY_END() RULE_CATEGORY ( Aggro ) @@ -504,7 +506,6 @@ RULE_INT ( Merchant, PricePenaltyPct, 4) // Determines maximum price penalty fro RULE_REAL( Merchant, ChaBonusMod, 3.45) // Determines CHA cap, from 104 CHA. 3.45 is 132 CHA at apprehensive. 0.34 is 400 CHA at apprehensive. RULE_REAL ( Merchant, ChaPenaltyMod, 1.52) // Determines CHA bottom, up to 102 CHA. 1.52 is 37 CHA at apprehensive. 0.98 is 0 CHA at apprehensive. RULE_BOOL ( Merchant, EnableAltCurrencySell, true) // Enables the ability to resell items to alternate currency merchants - RULE_CATEGORY_END() RULE_CATEGORY ( Bazaar ) @@ -558,20 +559,40 @@ RULE_INT ( Console, SessionTimeOut, 600000 ) // Amount of time in ms for the con RULE_CATEGORY_END() RULE_CATEGORY( QueryServ ) -RULE_BOOL( QueryServ, PlayerChatLogging, false) // Logs Player Chat +RULE_BOOL( QueryServ, PlayerLogChat, false) // Logs Player Chat RULE_BOOL( QueryServ, PlayerLogTrades, false) // Logs Player Trades RULE_BOOL( QueryServ, PlayerLogHandins, false) // Logs Player Handins RULE_BOOL( QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills RULE_BOOL( QueryServ, PlayerLogDeletes, false) // Logs Player Deletes RULE_BOOL( QueryServ, PlayerLogMoves, false) // Logs Player Moves -RULE_BOOL( QueryServ, MerchantLogTransactions, false) // Logs Merchant Transactions +RULE_BOOL( QueryServ, PlayerLogMerchantTransactions, false) // Logs Merchant Transactions RULE_BOOL( QueryServ, PlayerLogPCCoordinates, false) // Logs Player Coordinates with certain events +RULE_BOOL( QueryServ, PlayerLogDropItem, false) // Logs Player Drop Item +RULE_BOOL( QueryServ, PlayerLogZone, false) // Logs Player Zone Events +RULE_BOOL( QueryServ, PlayerLogDeaths, false) // Logs Player Deaths +RULE_BOOL( QueryServ, PlayerLogConnectDisconnect, false) // Logs Player Connect Disconnect State +RULE_BOOL( QueryServ, PlayerLogLevels, false) // Logs Player Leveling/Deleveling +RULE_BOOL( QueryServ, PlayerLogAARate, false) // Logs Player AA Experience Rates +RULE_BOOL( QueryServ, PlayerLogQGlobalUpdate, false) // Logs Player QGlobal Updates +RULE_BOOL( QueryServ, PlayerLogTaskUpdates, false) // Logs Player Task Updates +RULE_BOOL( QueryServ, PlayerLogKeyringAddition, false) // Log PLayer Keyring additions +RULE_BOOL( QueryServ, PlayerLogAAPurchases, false) // Log Player AA Purchases +RULE_BOOL( QueryServ, PlayerLogTradeSkillEvents, false) // Log Player Tradeskill Transactions +RULE_BOOL( QueryServ, PlayerLogIssuedCommandes, false ) // Log Player Issued Commands +RULE_BOOL( QueryServ, PlayerLogMoneyTransactions, false) // Log Player Money Transaction/Splits +RULE_BOOL( QueryServ, PlayerLogAlternateCurrencyTransactions, false) // Log Ploayer Alternate Currency Transactions RULE_CATEGORY_END() RULE_CATEGORY( Inventory ) RULE_BOOL ( Inventory, EnforceAugmentRestriction, true) // Forces augment slot restrictions RULE_BOOL ( Inventory, EnforceAugmentUsability, true) // Forces augmented item usability RULE_BOOL ( Inventory, EnforceAugmentWear, true) // Forces augment wear slot validation +RULE_BOOL ( Inventory, DeleteTransformationMold, true) //False if you want mold to last forever +RULE_BOOL ( Inventory, AllowAnyWeaponTransformation, false) //Weapons can use any weapon transformation +RULE_CATEGORY_END() + +RULE_CATEGORY( Client ) +RULE_BOOL( Client, UseLiveFactionMessage, false) // Allows players to see faction adjustments like Live RULE_CATEGORY_END() #undef RULE_CATEGORY diff --git a/common/servertalk.h b/common/servertalk.h index e7998362e..470ca73e1 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,12 +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_QSMerchantLogTransactions 0x4015 +#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_CZSignalNPC 0x4017 +#define ServerOP_CZSetEntityVariableByNPCTypeID 0x4018 #define ServerOP_WIRemoteCall 0x5001 #define ServerOP_WIRemoteCallResponse 0x5002 @@ -192,6 +197,7 @@ #define ServerOP_WIClientSession 0x5004 #define ServerOP_WIClientSessionResponse 0x5005 +/* Query Serv Generic Packet Flag/Type Enumeration */ enum { QSG_LFGuild = 0 }; enum { QSG_LFGuild_PlayerMatches = 0, QSG_LFGuild_UpdatePlayerInfo, QSG_LFGuild_RequestPlayerInfo, QSG_LFGuild_UpdateGuildInfo, QSG_LFGuild_GuildMatches, QSG_LFGuild_RequestGuildInfo }; @@ -350,6 +356,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]; }; @@ -854,6 +861,11 @@ struct ServerRaidMessage_Struct { char message[0]; }; +struct ServerRaidMOTD_Struct { + uint32 rid; + char motd[0]; +}; + struct ServerLFGMatchesRequest_Struct { uint32 FromID; uint8 QuerierLevel; @@ -1096,6 +1108,11 @@ struct CZClientSignal_Struct { uint32 data; }; +struct CZNPCSignal_Struct { + uint32 npctype_id; + uint32 data; +}; + struct CZClientSignalByName_Struct { char Name[64]; uint32 data; @@ -1122,6 +1139,7 @@ struct QSPlayerLogTrade_Struct { uint32 char2_id; MoneyUpdate_Struct char2_money; uint16 char2_count; + uint16 _detail_count; QSTradeItems_Struct items[0]; }; @@ -1145,6 +1163,7 @@ struct QSPlayerLogHandin_Struct { uint32 npc_id; MoneyUpdate_Struct npc_money; uint16 npc_count; + uint16 _detail_count; QSHandinItems_Struct items[0]; }; @@ -1225,16 +1244,30 @@ struct QSMerchantLogTransaction_Struct { QSTransactionItems_Struct items[0]; }; +struct QSGeneralQuery_Struct { + char QueryString[0]; +}; + struct CZMessagePlayer_Struct { uint32 Type; char CharName[64]; 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 579f2f41d..3ce92cac4 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -1,14 +1,15 @@ #include #include #include +#include #include "shareddb.h" #include "mysql.h" -#include "Item.h" +#include "item.h" #include "classes.h" #include "rulesys.h" #include "seperator.h" -#include "StringUtil.h" +#include "string_util.h" #include "eq_packet_structs.h" #include "guilds.h" #include "extprofile.h" @@ -47,702 +48,676 @@ 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))) { - 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; + 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; } - safe_delete_array(query); - return ret; + 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 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); - - // 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); + // Update/Insert item + std::string query = StringFormat("REPLACE INTO inventory " + "(charid, slotid, itemid, charges, instnodrop, custom_data, color, " + "augslot1, augslot2, augslot3, augslot4, augslot5, ornamenticon, ornamentidfile) " + "VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, " + "%lu, %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], (unsigned long)inst->GetOrnamentationIcon(), (unsigned long)inst->GetOrnamentationIDFile()); + auto results = QueryDatabase(query); // Save bag contents, if slot supports bag contents - if (inst && inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) { + 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 - - 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); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "UpdateInventorySlot query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); 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; + } + + 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]); + } } } - mysql_free_result(result); - ret = true; - } - else { - LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", errbuf); + 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, ornamenticon, ornamentidfile " + "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; + uint32 ornament_icon = (uint32)atoul(row[11]); + uint32 ornament_idfile = (uint32)atoul(row[12]); - 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); - } - } + const Item_Struct* item = GetItem(item_id); + + 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; + } + + int16 put_slot_id = INVALID_INDEX; + + ItemInst* inst = CreateBaseItem(item, charges); + + 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; } - 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); + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); 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); - } - // 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); - } - - 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); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: charid %i has an invalid item_id %i in inventory slot %i", - char_id, item_id, slot_id); + value.push_back(v); } } - mysql_free_result(result); + if (ornament_icon > 0) + inst->SetOrnamentIcon(ornament_icon); - // 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"); + if (ornament_idfile > 0) + inst->SetOrnamentationIDFile(ornament_idfile); + + 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); + } } - safe_delete_array(query); - return ret; + // 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; + std::string query = StringFormat("SELECT slotid, itemid, charges, color, augslot1, " + "augslot2, augslot3, augslot4, augslot5, instnodrop, custom_data, ornamenticon, ornamentidfile " + "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; + } - 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 (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]); - 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); - } + 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; + uint32 ornament_icon = (uint32)atoul(row[11]); + uint32 ornament_idfile = (uint32)atoul(row[12]); + + 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; } - } - if (color > 0) - inst->SetColor(color); - inst->SetCharges(charges); + char v = data_str[i]; + if(use_id) + idAsString.push_back(v); + else + value.push_back(v); - 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); + + if (ornament_icon > 0) + inst->SetOrnamentIcon(ornament_icon); + + if (ornament_idfile > 0) + inst->SetOrnamentationIDFile(ornament_idfile); + + 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 - ret = GetSharedBank(account_id, inv, false); - } - 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"); } - safe_delete_array(query); - return ret; + // 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() { @@ -778,9 +753,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; @@ -808,220 +780,230 @@ 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.EvolvingLevel = (uint8)atoul(row[ItemField::evolvinglevel]); + 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) { @@ -1058,54 +1040,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) { @@ -1122,57 +1098,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; + + 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); } - if(!row[3]) { - 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; + 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(current_id != 0) { - hash.insert(current_id, faction); - } + if(!row[3]) + 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; + } + + 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() { @@ -1208,104 +1179,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) { @@ -1359,72 +1232,43 @@ 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)) { + std::string query = StringFormat( + "UPDATE `character_corpses` SET `is_buried` = 1 WHERE `is_buried` = 0 AND " + "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(time_of_death)) > %d AND NOT time_of_death = 0", + (RuleI(Character, CorpseDecayTimeMS) / 1000)); + auto results = QueryDatabase(query); + if (!results.Success()) + return -1; - 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); - 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; -} - -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); + std::string query = StringFormat( + "DELETE FROM `character_corpses` WHERE (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(time_of_death)) > %d " + "AND NOT time_of_death = 0", (RuleI(Character, CorpseDecayTimeMS) / 1000)); + auto results = QueryDatabase(query); + if (!results.Success()) return -1; - } - safe_delete_array(query); - 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() { @@ -1459,30 +1303,24 @@ 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); + 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; + } - 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; + 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]); - 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); + 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; } } @@ -1566,32 +1404,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"; - - const char *ERR_MYSQLERROR = "Error in LoadDamageShieldTypes: %s %s"; - - 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); + 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; } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); + + 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]); } + } const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { @@ -1599,200 +1425,195 @@ 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; + 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; } - return ret; + + 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); - - 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; - } - - ++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]); - - 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]); - - for(y=0; y< 4;y++) - sp[tempid].components[y]=atoi(row[58+y]); - - for(y=0; y< 4;y++) - sp[tempid].component_counts[y]=atoi(row[62+y]); - - for(y=0; y< 4;y++) - sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); - - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].formula[y]=atoi(row[70+y]); - - sp[tempid].goodEffect=atoi(row[83]); - sp[tempid].Activated=atoi(row[84]); - sp[tempid].resisttype=atoi(row[85]); - - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].effectid[y]=atoi(row[86+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]); - - for(y=0; y < PLAYER_CLASS_COUNT;y++) - sp[tempid].classes[y]=atoi(row[104+y]); - - sp[tempid].CastingAnim=atoi(row[120]); - sp[tempid].SpellAffectIndex=atoi(row[123]); - sp[tempid].disallow_sit=atoi(row[124]); - - 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; - } - mysql_free_result(result); - - LoadDamageShieldTypes(sp, max_spells); - } else { - _log(SPELLS__LOAD_ERR, "Error in LoadSpells query '%s' %s", query, errbuf); - 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; } + + 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; + } + + int tempid = 0; + int counter = 0; + + 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)); + + 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]); + + for(y=0; y< 4;y++) + sp[tempid].components[y]=atoi(row[58+y]); + + for(y=0; y< 4;y++) + sp[tempid].component_counts[y]=atoi(row[62+y]); + + for(y=0; y< 4;y++) + sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); + + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].formula[y]=atoi(row[70+y]); + + sp[tempid].goodEffect=atoi(row[83]); + sp[tempid].Activated=atoi(row[84]); + sp[tempid].resisttype=atoi(row[85]); + + for(y=0; y< EFFECT_COUNT;y++) + sp[tempid].effectid[y]=atoi(row[86+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]); + + for(y=0; y < PLAYER_CLASS_COUNT;y++) + sp[tempid].classes[y]=atoi(row[104+y]); + + sp[tempid].CastingAnim=atoi(row[120]); + sp[tempid].SpellAffectIndex=atoi(row[123]); + sp[tempid].disallow_sit=atoi(row[124]); + sp[tempid].diety_agnostic=atoi(row[125]); + + 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].ldon_trap = atoi(row[165]) != 0; + 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].sneak = atoi(row[196]) != 0; + sp[tempid].not_extendable = atoi(row[197]) != 0; + sp[tempid].suspendable = atoi(row[200]) != 0; + sp[tempid].viral_range = atoi(row[201]); + sp[tempid].no_block = atoi(row[205]); + 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() { @@ -1804,7 +1625,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"); @@ -1825,53 +1646,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) { @@ -1907,155 +1727,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; - - 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); + 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 (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))); - } - - 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])); - } - - if(current_entry > 128) { - continue; - } - - 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); + 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; } + + uint32 current_id = 0; + uint32 current_entry = 0; + + 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))); + + 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])); + } + + if(current_entry > 128) + continue; + + 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))); + } 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; - } - - 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); + 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()); } + + uint32 current_id = 0; + uint32 current_entry = 0; + + 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))); + + 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))); + } bool SharedDatabase::LoadLoot() { @@ -2110,104 +1921,68 @@ 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); + 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; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", message->text, 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); } 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); + 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; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", message->text, botid), errbuf)) { - std::cerr << "Error in SetBotInspectMessage query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); } bool SharedDatabase::VerifyToken(std::string token, int& status) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool res = false; status = 0; if(token.length() > 64) { token = token.substr(0, 64); } token = EscapeString(token); - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT status FROM tokens WHERE token='%s'", token.c_str()), errbuf, &result)) { - safe_delete_array(query); - - row = mysql_fetch_row(result); - if(row) { - status = atoi(row[0]); - res = true; - } - - mysql_free_result(result); + std::string query = StringFormat("SELECT status FROM tokens WHERE token='%s'", token.c_str()); + auto results = QueryDatabase(query); + if(!results.Success()) + { + std::cerr << "Error in SharedDatabase::VerifyToken query '" << query << "' " << results.ErrorMessage() << std::endl; + return false; } - else { - std::cerr << "Error in SharedDatabase::VerifyToken query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + if(results.RowCount() != 1) { + return false; } - - return res; -} \ No newline at end of file + + auto row = results.begin(); + status = atoi(row[0]); + return true; +} diff --git a/common/shareddb.h b/common/shareddb.h index 4dd7a0261..c21229389 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -6,7 +6,7 @@ #include "database.h" #include "skills.h" #include "spdat.h" -#include "Item.h" +#include "item.h" #include "base_data.h" #include "fixed_memory_hash_set.h" #include "fixed_memory_variable_hash_set.h" @@ -40,13 +40,9 @@ 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 +53,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..a589129ed 100644 --- a/common/skills.h +++ b/common/skills.h @@ -30,7 +30,7 @@ ** ** (ref: eqstr_us.txt [05-10-2013]) */ -enum SkillUseTypes : uint32 +enum SkillUseTypes { /*13855*/ Skill1HBlunt = 0, /*13856*/ Skill1HSlashing, @@ -108,6 +108,7 @@ enum SkillUseTypes : uint32 /*13869*/ SkillBerserking, /*13902*/ SkillTaunt, /*05837*/ SkillFrenzy, // This appears to be the only listed one not grouped with the others +/*00000*/ _EmuSkillCount // move to last position of active enumeration labels // SoF+ specific skills // /*03670*/ SkillRemoveTraps, @@ -124,7 +125,6 @@ enum SkillUseTypes : uint32 // Support values // /*-----*/ _SkillServerArraySize = _SkillCount_RoF2, // Should reflect last client '_SkillCount' -/*-----*/ _SkillPacketArraySize = 100, // Currently supported clients appear to iterate full 100 dword buffer range // Superfluous additions to SkillUseTypes..server-use only // /*-----*/ ExtSkillGenericTradeskill = 100 @@ -152,7 +152,7 @@ enum SkillUseTypes : uint32 NOTE: Disregard this until it is sorted out I changed (tradeskill==75) to ExtSkillGenericTradeskill in tradeskills.cpp for both instances. If it's a pseudo-enumeration of - an AA ability, then use the 'ExtSkill' ('ExtentedSkill') prefix with a value >= 100. (current implementation) + an AA ability, then use the 'ExtSkill' ('ExtendedSkill') prefix with a value >= 100. (current implementation) We probably need to recode ALL of the skill checks to use the new Skill2HPiercing and ensure that the animation value is properly changed in the patch files. As far as earlier clients using this new skill, it can be done, but we just need to ensure @@ -163,6 +163,7 @@ enum SkillUseTypes : uint32 In addition to the above re-coding, we're probably going to need to rework the database pp blob to reserve space for the current 100-dword buffer allocation. This way, we can just add new ones without having to rework it each time. + (Wasn't done for this in particular..but, thanks Akkadius!) -U */ @@ -171,6 +172,9 @@ enum SkillUseTypes : uint32 // temporary until it can be sorted out... #define HIGHEST_SKILL SkillFrenzy +// server profile does not reflect this yet..so, prefixed with 'PACKET_' +#define PACKET_SKILL_ARRAY_SIZE 100 + // TODO: add string return for skill names /* @@ -260,4 +264,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/spdat.cpp b/common/spdat.cpp index 8e93f4e1f..1ff8f2696 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -74,7 +74,7 @@ #include "spdat.h" #include "packet_dump.h" #include "moremath.h" -#include "Item.h" +#include "item.h" #include "skills.h" #include "bodytypes.h" #include "classes.h" diff --git a/common/spdat.h b/common/spdat.h index 40f5e658f..ed8a86e20 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -119,7 +119,7 @@ typedef enum { /* 29 */ // NOT USED /* 30 */ // NOT USED /* 31 */ // NOT USED -/* 32 */ ST_AECaster2 = 0x20, //ae caster hatelist maybe? +/* 32 */ ST_AETargetHateList = 0x20, /* 33 */ ST_HateList = 0x21, /* 34 */ ST_LDoNChest_Cursed = 0x22, /* 35 */ ST_Muramite = 0x23, //only works on special muramites @@ -131,10 +131,13 @@ typedef enum { /* 41 */ ST_Group = 0x29, /* 42 */ ST_Directional = 0x2a, //ae around this target between two angles /* 43 */ ST_GroupClientAndPet = 0x2b, -/* 44 */ //ST_Beam = 0x2c, //like directional but facing in front of you always -/* 45 */ //ST_Ring = 0x2d, // Like a mix of PB ae + rain spell(has ae duration) +/* 44 */ ST_Beam = 0x2c, +/* 45 */ ST_Ring = 0x2d, /* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target /* 47 */ ST_PetMaster = 0x2f, // uses the master as target +/* 48 */ // UNKNOWN +/* 49 */ // NOT USED +/* 50 */ ST_TargetAENoPlayersPets = 0x32, } SpellTargetType; typedef enum { @@ -621,6 +624,8 @@ typedef enum { // number. note that the id field is counted as 0, this way the numbers // here match the numbers given to sep in the loading function net.cpp // +#define SPELL_LOAD_FIELD_COUNT 231 + struct SPDat_Spell_Struct { /* 000 */ int id; // not used @@ -671,7 +676,7 @@ struct SPDat_Spell_Struct /* 122 */ //uint32 TravelType; /* 123 */ uint16 SpellAffectIndex; /* 124 */ int8 disallow_sit; // 124: high-end Yaulp spells (V, VI, VII, VIII [Rk 1, 2, & 3], & Gallenite's Bark of Fury -/* 125 */ // 125: Words of the Skeptic +/* 125 */ int8 diety_agnostic;// 125: Words of the Skeptic /* 126 */ int8 deities[16]; // Deity check. 201 - 216 per http://www.eqemulator.net/wiki/wikka.php?wakka=DeityList // -1: Restrict to Deity; 1: Restrict to Deity, but only used on non-Live (Test Server "Blessing of ...") spells; 0: Don't restrict /* 142 */ // 142: between 0 & 100 @@ -695,6 +700,7 @@ struct SPDat_Spell_Struct /* 162 */ int bonushate; /* 163 */ /* 164 */ // for most spells this appears to mimic ResistDiff +/* 165 */ bool ldon_trap; //Flag found on all LDON trap / chest related spells. /* 166 */ int EndurCost; /* 167 */ int8 EndurTimerIndex; /* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast @@ -707,9 +713,14 @@ struct SPDat_Spell_Struct /* 178 */ int pvpresistcalc; /* 179 */ int pvpresistcap; /* 180 */ int spell_category; -/* 181 */ +/* 181 */ //unknown - likely buff duration related +/* 182 */ //unknown - likely buff duration related +/* 183 */ +/* 184 */ /* 185 */ int8 can_mgb; // 0=no, -1 or 1 = yes /* 186 */ int dispel_flag; +/* 187 */ //int npc_category; +/* 188 */ //int npc_usefulness; /* 189 */ int MinResist; /* 190 */ int MaxResist; /* 191 */ uint8 viral_targets; @@ -717,14 +728,16 @@ struct SPDat_Spell_Struct /* 193 */ int NimbusEffect; /* 194 */ float directional_start; //Cone Start Angle: /* 195 */ float directional_end; // Cone End Angle: -/* 196 */ +/* 196 */ bool sneak; // effect can only be used if sneaking (rogue 'Daggerfall' ect) /* 197 */ bool not_extendable; /* 198- 199 */ /* 200 */ bool suspendable; // buff is suspended in suspended buff zones /* 201 */ int viral_range; /* 202 */ /* 203 */ //int songcap; // individual song cap (how live currently does it, not implemented) -/* 204 - 206 */ +/* 204 */ +/* 205 */ bool no_block; +/* 206 */ /* 207 */ int spellgroup; /* 208 */ int rank; //increments AA effects with same name /* 209 */ int powerful_flag; // Need more investigation to figure out what to call this, for now we know -1 makes charm spells not break before their duration is complete, it does alot more though diff --git a/common/StringUtil.cpp b/common/string_util.cpp similarity index 93% rename from common/StringUtil.cpp rename to common/string_util.cpp index 80c92fa88..2fcb59e05 100644 --- a/common/StringUtil.cpp +++ b/common/string_util.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "StringUtil.h" +#include "string_util.h" #include // for strncpy #include @@ -53,10 +53,11 @@ const std::string vStringFormat(const char* format, va_list args) return ""; } else if ((unsigned int)characters_used > output.capacity()) { - output.resize(characters_used+1); + output.resize(characters_used + 1); va_copy(tmpargs,args); characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs); va_end(tmpargs); + output.resize(characters_used); if (characters_used < 0) { // We shouldn't have a format error by this point, but I can't imagine what error we @@ -72,6 +73,8 @@ const std::string vStringFormat(const char* format, va_list args) characters_used = vsnprintf(&output[0], output.capacity(), format, tmpargs); va_end(tmpargs); + output.resize(characters_used); + if (characters_used < 0) { // We shouldn't have a format error by this point, but I can't imagine what error we // could have by this point. Still, return empty string; @@ -380,6 +383,42 @@ std::string EscapeString(const std::string &s) { return ret; } +std::string EscapeString(const char *src, size_t sz) { + std::string ret; + + for(size_t i = 0; i < sz; ++i) { + char c = src[i]; + switch(c) { + case '\x00': + ret += "\\x00"; + break; + case '\n': + ret += "\\n"; + break; + case '\r': + ret += "\\r"; + break; + case '\\': + ret += "\\\\"; + break; + case '\'': + ret += "\\'"; + break; + case '\"': + ret += "\\\""; + break; + case '\x1a': + ret += "\\x1a"; + break; + default: + ret.push_back(c); + break; + } + } + + return ret; +} + bool isAlphaNumeric(const char *text) { for (unsigned int charIndex=0; charIndex @@ -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/StructStrategy.h b/common/struct_strategy.h similarity index 100% rename from common/StructStrategy.h rename to common/struct_strategy.h diff --git a/common/TCPBasicServer.h b/common/tcp_basic_server.h similarity index 89% rename from common/TCPBasicServer.h rename to common/tcp_basic_server.h index 3a01051d3..1d317bc8f 100644 --- a/common/TCPBasicServer.h +++ b/common/tcp_basic_server.h @@ -1,8 +1,8 @@ #ifndef TCPBASICSERVER_H_ #define TCPBASICSERVER_H_ -#include "TCPServer.h" -#include "TCPConnection.h" +#include "tcp_server.h" +#include "tcp_connection.h" class TCPBasicServer : public TCPServer { public: diff --git a/common/TCPConnection.cpp b/common/tcp_connection.cpp similarity index 99% rename from common/TCPConnection.cpp rename to common/tcp_connection.cpp index 1e5f49600..ea57a6301 100644 --- a/common/TCPConnection.cpp +++ b/common/tcp_connection.cpp @@ -22,7 +22,7 @@ #include #include -#include "TCPConnection.h" +#include "tcp_connection.h" #include "../common/servertalk.h" #include "../common/timer.h" #include "../common/packet_dump.h" diff --git a/common/TCPConnection.h b/common/tcp_connection.h similarity index 99% rename from common/TCPConnection.h rename to common/tcp_connection.h index 5321c5e3a..2e5b7e883 100644 --- a/common/TCPConnection.h +++ b/common/tcp_connection.h @@ -44,9 +44,9 @@ #endif #include "types.h" -#include "Mutex.h" +#include "mutex.h" #include "queue.h" -#include "MiscFunctions.h" +#include "misc_functions.h" class BaseTCPServer; class ServerPacket; diff --git a/common/TCPServer.cpp b/common/tcp_server.cpp similarity index 99% rename from common/TCPServer.cpp rename to common/tcp_server.cpp index 2e3abfe2f..415f76fa1 100644 --- a/common/TCPServer.cpp +++ b/common/tcp_server.cpp @@ -1,5 +1,5 @@ #include "debug.h" -#include "TCPServer.h" +#include "tcp_server.h" #include #include #include diff --git a/common/TCPServer.h b/common/tcp_server.h similarity index 100% rename from common/TCPServer.h rename to common/tcp_server.h 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/common/version.h b/common/version.h index 432d8157f..43b4f8b10 100644 --- a/common/version.h +++ b/common/version.h @@ -23,6 +23,14 @@ #define EQEMU_PROTOCOL_VERSION "0.3.10" #define CURRENT_VERSION "1.0.0" + +/* + Everytime a Database SQL is added to Github, + increment CURRENT_BINARY_DATABASE_VERSION number and make sure you update the manifest + Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt +*/ + +#define CURRENT_BINARY_DATABASE_VERSION 9059 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/common/worldconn.cpp b/common/worldconn.cpp index 42375cb8a..73946d6f5 100644 --- a/common/worldconn.cpp +++ b/common/worldconn.cpp @@ -26,7 +26,7 @@ #include #include "worldconn.h" -#include "EQEmuConfig.h" +#include "eqemu_config.h" #include "md5.h" #include "database.h" #include "servertalk.h" diff --git a/common/worldconn.h b/common/worldconn.h index 08ceafb37..83d9a9cd7 100644 --- a/common/worldconn.h +++ b/common/worldconn.h @@ -19,7 +19,7 @@ #ifndef WORLDCONNECTION_H #define WORLDCONNECTION_H -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include class ServerPacket; diff --git a/common/XMLParser.cpp b/common/xml_parser.cpp similarity index 99% rename from common/XMLParser.cpp rename to common/xml_parser.cpp index f6f64ed13..7f84e0d47 100644 --- a/common/XMLParser.cpp +++ b/common/xml_parser.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "debug.h" -#include "XMLParser.h" +#include "xml_parser.h" XMLParser::XMLParser() { ParseOkay = false; diff --git a/common/XMLParser.h b/common/xml_parser.h similarity index 100% rename from common/XMLParser.h rename to common/xml_parser.h diff --git a/common/ZoneNumbers.h b/common/zone_numbers.h similarity index 100% rename from common/ZoneNumbers.h rename to common/zone_numbers.h diff --git a/dependencies/luabind/CMakeLists.txt b/dependencies/luabind/CMakeLists.txt index 9f3288d4a..5efdf562f 100644 --- a/dependencies/luabind/CMakeLists.txt +++ b/dependencies/luabind/CMakeLists.txt @@ -21,7 +21,7 @@ SET(lb_sources ) SET(lb_headers - + ) ADD_LIBRARY(luabind ${lb_sources} ${lb_headers}) @@ -29,6 +29,11 @@ ADD_LIBRARY(luabind ${lb_sources} ${lb_headers}) IF(UNIX) ADD_DEFINITIONS(-fPIC) + set_source_files_properties(${lb_sources} PROPERTY COMPILE_FLAGS -Wno-deprecated-declarations) ENDIF(UNIX) +IF(MSVC) + set_source_files_properties(${lb_sources} PROPERTY COMPILE_FLAGS " /W0 " ) +ENDIF(MSVC) + SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) diff --git a/dependencies/luabind/luabind/detail/object_rep.hpp b/dependencies/luabind/luabind/detail/object_rep.hpp index dafa6532a..93b3e39d6 100644 --- a/dependencies/luabind/luabind/detail/object_rep.hpp +++ b/dependencies/luabind/luabind/detail/object_rep.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace luabind { namespace detail { diff --git a/eqlaunch/CMakeLists.txt b/eqlaunch/CMakeLists.txt index 922522d86..376bc8147 100644 --- a/eqlaunch/CMakeLists.txt +++ b/eqlaunch/CMakeLists.txt @@ -3,12 +3,12 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) SET(eqlaunch_sources eqlaunch.cpp worldserver.cpp - ZoneLaunch.cpp + zone_launch.cpp ) SET(eqlaunch_headers worldserver.h - ZoneLaunch.h + zone_launch.h ) ADD_EXECUTABLE(eqlaunch ${eqlaunch_sources} ${eqlaunch_headers}) diff --git a/eqlaunch/eqlaunch.cpp b/eqlaunch/eqlaunch.cpp index dafceaf30..fbac09541 100644 --- a/eqlaunch/eqlaunch.cpp +++ b/eqlaunch/eqlaunch.cpp @@ -17,13 +17,13 @@ */ #include "../common/debug.h" -#include "../common/ProcLauncher.h" -#include "../common/EQEmuConfig.h" +#include "../common/proc_launcher.h" +#include "../common/eqemu_config.h" #include "../common/servertalk.h" #include "../common/platform.h" #include "../common/crash.h" #include "worldserver.h" -#include "ZoneLaunch.h" +#include "zone_launch.h" #include #include #include diff --git a/eqlaunch/worldserver.cpp b/eqlaunch/worldserver.cpp index c6e44fccd..1754b6564 100644 --- a/eqlaunch/worldserver.cpp +++ b/eqlaunch/worldserver.cpp @@ -16,12 +16,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "worldserver.h" #include "../common/servertalk.h" -#include "ZoneLaunch.h" -#include "../common/EQEmuConfig.h" -#include "../common/StringUtil.h" - +#include "../common/eqemu_config.h" +#include "../common/string_util.h" +#include "worldserver.h" +#include "zone_launch.h" WorldServer::WorldServer(std::map &zones, const char *name, const EQEmuConfig *config) : WorldConnection(EmuTCPConnection::packetModeLauncher, config->SharedKey.c_str()), diff --git a/eqlaunch/ZoneLaunch.cpp b/eqlaunch/zone_launch.cpp similarity index 98% rename from eqlaunch/ZoneLaunch.cpp rename to eqlaunch/zone_launch.cpp index e1332fdce..fee133da7 100644 --- a/eqlaunch/ZoneLaunch.cpp +++ b/eqlaunch/zone_launch.cpp @@ -16,11 +16,10 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - - -#include "ZoneLaunch.h" +#include "../common/debug.h" +#include "../common/eqemu_config.h" +#include "zone_launch.h" #include "worldserver.h" -#include "../common/EQEmuConfig.h" //static const uint32 ZONE_RESTART_DELAY = 10000; //static const uint32 ZONE_TERMINATE_WAIT = 10000; diff --git a/eqlaunch/ZoneLaunch.h b/eqlaunch/zone_launch.h similarity index 98% rename from eqlaunch/ZoneLaunch.h rename to eqlaunch/zone_launch.h index 2694e5bd5..3dc0fd697 100644 --- a/eqlaunch/ZoneLaunch.h +++ b/eqlaunch/zone_launch.h @@ -18,7 +18,7 @@ #ifndef ZONELAUNCH_H_ #define ZONELAUNCH_H_ -#include "../common/ProcLauncher.h" +#include "../common/proc_launcher.h" #include "../common/timer.h" #include diff --git a/loginserver/CMakeLists.txt b/loginserver/CMakeLists.txt index f09d3043a..f588cbc48 100644 --- a/loginserver/CMakeLists.txt +++ b/loginserver/CMakeLists.txt @@ -1,37 +1,37 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) SET(eqlogin_sources - Client.cpp - ClientManager.cpp - Config.cpp - DatabaseMySQL.cpp - DatabasePostgreSQL.cpp - ErrorLog.cpp - Main.cpp - ServerManager.cpp - WorldServer.cpp + client.cpp + client_manager.cpp + config.cpp + database_mysql.cpp + database_postgresql.cpp + error_log.cpp + main.cpp + server_manager.cpp + world_server.cpp ) IF(MSVC OR MINGW) ADD_DEFINITIONS(-DNOMINMAX) - SET(eqlogin_sources ${eqlogin_sources} Encryption.cpp) + SET(eqlogin_sources ${eqlogin_sources} encryption.cpp) ENDIF(MSVC OR MINGW) SET(eqlogin_headers - Client.h - ClientManager.h - Config.h - Database.h - DatabaseMySQL.h - DatabasePostgreSQL.h - Encryption.h - EQCryptoAPI.h - ErrorLog.h - LoginServer.h - LoginStructures.h - Options.h - ServerManager.h - WorldServer.h + client.h + client_manager.h + config.h + database.h + database_mysql.h + database_postgresql.h + encryption.h + eq_crypto_api.h + error_log.h + login_server.h + login_structures.h + options.h + server_manager.h + world_server.h ) IF(UNIX) diff --git a/loginserver/Client.cpp b/loginserver/client.cpp similarity index 98% rename from loginserver/Client.cpp rename to loginserver/client.cpp index 9fd96118a..9883c059d 100644 --- a/loginserver/Client.cpp +++ b/loginserver/client.cpp @@ -15,11 +15,11 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "Client.h" -#include "ErrorLog.h" -#include "LoginServer.h" -#include "LoginStructures.h" -#include "../common/MiscFunctions.h" +#include "client.h" +#include "error_log.h" +#include "login_server.h" +#include "login_structures.h" +#include "../common/misc_functions.h" extern ErrorLog *server_log; extern LoginServer server; diff --git a/loginserver/Client.h b/loginserver/client.h similarity index 96% rename from loginserver/Client.h rename to loginserver/client.h index 1cb248344..3248aefb5 100644 --- a/loginserver/Client.h +++ b/loginserver/client.h @@ -20,10 +20,10 @@ #include "../common/debug.h" #include "../common/opcodemgr.h" -#include "../common/EQStreamType.h" -#include "../common/EQStreamFactory.h" +#include "../common/eq_stream_type.h" +#include "../common/eq_stream_factory.h" #ifndef WIN32 -#include "EQCryptoAPI.h" +#include "eq_crypto_api.h" #endif #include diff --git a/loginserver/ClientManager.cpp b/loginserver/client_manager.cpp similarity index 98% rename from loginserver/ClientManager.cpp rename to loginserver/client_manager.cpp index cbf720f27..d6f6b760f 100644 --- a/loginserver/ClientManager.cpp +++ b/loginserver/client_manager.cpp @@ -15,9 +15,9 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "ClientManager.h" -#include "ErrorLog.h" -#include "LoginServer.h" +#include "client_manager.h" +#include "error_log.h" +#include "login_server.h" extern ErrorLog *server_log; extern LoginServer server; diff --git a/loginserver/ClientManager.h b/loginserver/client_manager.h similarity index 95% rename from loginserver/ClientManager.h rename to loginserver/client_manager.h index 287a9212d..4c3920f04 100644 --- a/loginserver/ClientManager.h +++ b/loginserver/client_manager.h @@ -20,9 +20,9 @@ #include "../common/debug.h" #include "../common/opcodemgr.h" -#include "../common/EQStreamType.h" -#include "../common/EQStreamFactory.h" -#include "Client.h" +#include "../common/eq_stream_type.h" +#include "../common/eq_stream_factory.h" +#include "client.h" #include using namespace std; diff --git a/loginserver/Config.cpp b/loginserver/config.cpp similarity index 99% rename from loginserver/Config.cpp rename to loginserver/config.cpp index 44906b889..8815622d2 100644 --- a/loginserver/Config.cpp +++ b/loginserver/config.cpp @@ -16,8 +16,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "Config.h" -#include "ErrorLog.h" +#include "config.h" +#include "error_log.h" extern ErrorLog *server_log; /** diff --git a/loginserver/Config.h b/loginserver/config.h similarity index 100% rename from loginserver/Config.h rename to loginserver/config.h diff --git a/loginserver/Database.h b/loginserver/database.h similarity index 100% rename from loginserver/Database.h rename to loginserver/database.h diff --git a/loginserver/DatabaseMySQL.cpp b/loginserver/database_mysql.cpp similarity index 98% rename from loginserver/DatabaseMySQL.cpp rename to loginserver/database_mysql.cpp index 7d7b20970..924538f38 100644 --- a/loginserver/DatabaseMySQL.cpp +++ b/loginserver/database_mysql.cpp @@ -16,12 +16,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "Database.h" +#include "database.h" #ifdef EQEMU_MYSQL_ENABLED -#include "DatabaseMySQL.h" -#include "ErrorLog.h" -#include "LoginServer.h" +#include "database_mysql.h" +#include "error_log.h" +#include "login_server.h" extern ErrorLog *server_log; extern LoginServer server; diff --git a/loginserver/DatabaseMySQL.h b/loginserver/database_mysql.h similarity index 99% rename from loginserver/DatabaseMySQL.h rename to loginserver/database_mysql.h index 05af22315..4249cb614 100644 --- a/loginserver/DatabaseMySQL.h +++ b/loginserver/database_mysql.h @@ -18,7 +18,7 @@ #ifndef EQEMU_DATABASEMYSQL_H #define EQEMU_DATABASEMYSQL_H -#include "Database.h" +#include "database.h" #ifdef EQEMU_MYSQL_ENABLED #include diff --git a/loginserver/DatabasePostgreSQL.cpp b/loginserver/database_postgresql.cpp similarity index 98% rename from loginserver/DatabasePostgreSQL.cpp rename to loginserver/database_postgresql.cpp index 9effac49b..e57679f64 100644 --- a/loginserver/DatabasePostgreSQL.cpp +++ b/loginserver/database_postgresql.cpp @@ -16,12 +16,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "Database.h" +#include "database.h" #ifdef EQEMU_POSTGRESQL_ENABLED -#include "DatabasePostgreSQL.h" -#include "ErrorLog.h" -#include "LoginServer.h" +#include "database_postgresql.h" +#include "error_log.h" +#include "login_server.h" extern ErrorLog *server_log; extern LoginServer server; diff --git a/loginserver/DatabasePostgreSQL.h b/loginserver/database_postgresql.h similarity index 99% rename from loginserver/DatabasePostgreSQL.h rename to loginserver/database_postgresql.h index 8d2685fcc..0dfbe53aa 100644 --- a/loginserver/DatabasePostgreSQL.h +++ b/loginserver/database_postgresql.h @@ -18,7 +18,7 @@ #ifndef EQEMU_DATABASEPOSTGRESQL_H #define EQEMU_DATABASEPOSTGRESQL_H -#include "Database.h" +#include "database.h" #ifdef EQEMU_POSTGRESQL_ENABLED #include diff --git a/loginserver/Encryption.cpp b/loginserver/encryption.cpp similarity index 98% rename from loginserver/Encryption.cpp rename to loginserver/encryption.cpp index bd947593b..5585487b1 100644 --- a/loginserver/Encryption.cpp +++ b/loginserver/encryption.cpp @@ -16,8 +16,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "Encryption.h" -#include "ErrorLog.h" +#include "encryption.h" +#include "error_log.h" #include extern ErrorLog *server_log; diff --git a/loginserver/Encryption.h b/loginserver/encryption.h similarity index 100% rename from loginserver/Encryption.h rename to loginserver/encryption.h diff --git a/loginserver/EQCryptoAPI.h b/loginserver/eq_crypto_api.h similarity index 100% rename from loginserver/EQCryptoAPI.h rename to loginserver/eq_crypto_api.h diff --git a/loginserver/ErrorLog.cpp b/loginserver/error_log.cpp similarity index 99% rename from loginserver/ErrorLog.cpp rename to loginserver/error_log.cpp index db5b6c89a..03021f6c7 100644 --- a/loginserver/ErrorLog.cpp +++ b/loginserver/error_log.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -#include "ErrorLog.h" +#include "error_log.h" const char *eqLogTypes[_log_largest_type] = { diff --git a/loginserver/ErrorLog.h b/loginserver/error_log.h similarity index 98% rename from loginserver/ErrorLog.h rename to loginserver/error_log.h index 224eeb352..ad54e12b7 100644 --- a/loginserver/ErrorLog.h +++ b/loginserver/error_log.h @@ -23,7 +23,7 @@ #include #include -#include "../common/Mutex.h" +#include "../common/mutex.h" /** * Dictates the log type specified in ErrorLog for Log(...) diff --git a/loginserver/LoginServer.h b/loginserver/login_server.h similarity index 86% rename from loginserver/LoginServer.h rename to loginserver/login_server.h index 54e62e551..f83154b8f 100644 --- a/loginserver/LoginServer.h +++ b/loginserver/login_server.h @@ -18,15 +18,15 @@ #ifndef EQEMU_LOGINSERVER_H #define EQEMU_LOGINSERVER_H -#include "ErrorLog.h" -#include "Config.h" -#include "Database.h" -#include "DatabaseMySQL.h" -#include "DatabasePostgreSQL.h" -#include "Encryption.h" -#include "Options.h" -#include "ServerManager.h" -#include "ClientManager.h" +#include "error_log.h" +#include "config.h" +#include "database.h" +#include "database_mysql.h" +#include "database_postgresql.h" +#include "encryption.h" +#include "options.h" +#include "server_manager.h" +#include "client_manager.h" /** * Login server struct, contains every variable for the server that needs to exist diff --git a/loginserver/LoginStructures.h b/loginserver/login_structures.h similarity index 100% rename from loginserver/LoginStructures.h rename to loginserver/login_structures.h diff --git a/loginserver/Main.cpp b/loginserver/main.cpp similarity index 99% rename from loginserver/Main.cpp rename to loginserver/main.cpp index 54a3e9ba4..820cc9113 100644 --- a/loginserver/Main.cpp +++ b/loginserver/main.cpp @@ -18,11 +18,11 @@ #include "../common/debug.h" #include "../common/types.h" #include "../common/opcodemgr.h" -#include "../common/EQStreamFactory.h" +#include "../common/eq_stream_factory.h" #include "../common/timer.h" #include "../common/platform.h" #include "../common/crash.h" -#include "LoginServer.h" +#include "login_server.h" #include #include #include diff --git a/loginserver/Options.h b/loginserver/options.h similarity index 100% rename from loginserver/Options.h rename to loginserver/options.h diff --git a/loginserver/ServerManager.cpp b/loginserver/server_manager.cpp similarity index 98% rename from loginserver/ServerManager.cpp rename to loginserver/server_manager.cpp index baa21aae2..6a1b57d6f 100644 --- a/loginserver/ServerManager.cpp +++ b/loginserver/server_manager.cpp @@ -15,10 +15,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "ServerManager.h" -#include "LoginServer.h" -#include "ErrorLog.h" -#include "LoginStructures.h" +#include "server_manager.h" +#include "login_server.h" +#include "error_log.h" +#include "login_structures.h" #include extern ErrorLog *server_log; diff --git a/loginserver/ServerManager.h b/loginserver/server_manager.h similarity index 93% rename from loginserver/ServerManager.h rename to loginserver/server_manager.h index cf15522db..e2453701d 100644 --- a/loginserver/ServerManager.h +++ b/loginserver/server_manager.h @@ -19,13 +19,13 @@ #define EQEMU_SERVERMANAGER_H #include "../common/debug.h" -#include "../common/EQStreamFactory.h" -#include "../common/EmuTCPConnection.h" -#include "../common/EmuTCPServer.h" +#include "../common/eq_stream_factory.h" +#include "../common/emu_tcp_connection.h" +#include "../common/emu_tcp_server.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" -#include "WorldServer.h" -#include "Client.h" +#include "world_server.h" +#include "client.h" #include /** diff --git a/loginserver/WorldServer.cpp b/loginserver/world_server.cpp similarity index 99% rename from loginserver/WorldServer.cpp rename to loginserver/world_server.cpp index 16b9addec..c91f1b702 100644 --- a/loginserver/WorldServer.cpp +++ b/loginserver/world_server.cpp @@ -15,10 +15,10 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "WorldServer.h" -#include "ErrorLog.h" -#include "LoginServer.h" -#include "LoginStructures.h" +#include "world_server.h" +#include "error_log.h" +#include "login_server.h" +#include "login_structures.h" extern ErrorLog *server_log; extern LoginServer server; diff --git a/loginserver/WorldServer.h b/loginserver/world_server.h similarity index 97% rename from loginserver/WorldServer.h rename to loginserver/world_server.h index 2b8b6ff61..ff790e103 100644 --- a/loginserver/WorldServer.h +++ b/loginserver/world_server.h @@ -19,9 +19,9 @@ #define EQEMU_WORLDSERVER_H #include "../common/debug.h" -#include "../common/EQStreamFactory.h" -#include "../common/EmuTCPConnection.h" -#include "../common/EmuTCPServer.h" +#include "../common/eq_stream_factory.h" +#include "../common/emu_tcp_connection.h" +#include "../common/emu_tcp_server.h" #include "../common/servertalk.h" #include "../common/packet_dump.h" #include diff --git a/queryserv/database.cpp b/queryserv/database.cpp index 14d4c34a9..4f7fdf10f 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -29,6 +29,7 @@ #include #include #include +#include // Disgrace: for windows compile #ifdef _WINDOWS @@ -43,7 +44,7 @@ #include "database.h" #include "../common/eq_packet_structs.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "../common/servertalk.h" Database::Database () @@ -96,226 +97,268 @@ Database::~Database() { } -bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_len) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `value` from `variables` where `varname`='%s'", varname), errbuf, &result)) { - - _log(UCS__ERROR, "Unable to get message count from database. %s %s", query, errbuf); - - safe_delete_array(query); - - return false; - } - - safe_delete_array(query); - - if (mysql_num_rows(result) != 1) { - - mysql_free_result(result); - - return false; - } - - row = mysql_fetch_row(result); - - snprintf(varvalue, varvalue_len, "%s", row[0]); - - mysql_free_result(result); - - return true; -} - - void Database::AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - char *S1 = new char[strlen(from) * 2 + 1]; - char *S2 = new char[strlen(to) * 2 + 1]; - char *S3 = new char[strlen(message) * 2 + 1]; - DoEscapeString(S1, from, strlen(from)); - DoEscapeString(S2, to, strlen(to)); - DoEscapeString(S3, message, strlen(message)); + char *escapedFrom = new char[strlen(from) * 2 + 1]; + char *escapedTo = new char[strlen(to) * 2 + 1]; + char *escapedMessage = new char[strlen(message) * 2 + 1]; + DoEscapeString(escapedFrom, from, strlen(from)); + DoEscapeString(escapedTo, to, strlen(to)); + DoEscapeString(escapedMessage, message, strlen(message)); - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_speech` SET `from`='%s', `to`='%s', `message`='%s', `minstatus`='%i', `guilddbid`='%i', `type`='%i'", S1, S2, S3, minstatus, guilddbid, type), errbuf, 0, 0)) { - _log(NET__WORLD, "Failed Speech Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + std::string query = StringFormat("INSERT INTO `qs_player_speech` " + "SET `from` = '%s', `to` = '%s', `message`='%s', " + "`minstatus`='%i', `guilddbid`='%i', `type`='%i'", + escapedFrom, escapedTo, escapedMessage, minstatus, guilddbid, type); + safe_delete_array(escapedFrom); + safe_delete_array(escapedTo); + safe_delete_array(escapedMessage); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Speech Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - safe_delete_array(query); - safe_delete_array(S1); - safe_delete_array(S2); - safe_delete_array(S3); + } -void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 Items) { +void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record` SET `time`=NOW(), " - "`char1_id`='%i', `char1_pp`='%i', `char1_gp`='%i', `char1_sp`='%i', `char1_cp`='%i', `char1_items`='%i', " - "`char2_id`='%i', `char2_pp`='%i', `char2_gp`='%i', `char2_sp`='%i', `char2_cp`='%i', `char2_items`='%i'", - QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, QS->char1_money.silver, QS->char1_money.copper, QS->char1_count, - QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, QS->char2_money.silver, QS->char2_money.copper, QS->char2_count), - errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Trade Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), " + "`char1_id` = '%i', `char1_pp` = '%i', `char1_gp` = '%i', " + "`char1_sp` = '%i', `char1_cp` = '%i', `char1_items` = '%i', " + "`char2_id` = '%i', `char2_pp` = '%i', `char2_gp` = '%i', " + "`char2_sp` = '%i', `char2_cp` = '%i', `char2_items` = '%i'", + QS->char1_id, QS->char1_money.platinum, QS->char1_money.gold, + QS->char1_money.silver, QS->char1_money.copper, QS->char1_count, + QS->char2_id, QS->char2_money.platinum, QS->char2_money.gold, + QS->char2_money.silver, QS->char2_money.copper, QS->char2_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Trade Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_trade_record_entries` SET `event_id`='%i', " - "`from_id`='%i', `from_slot`='%i', `to_id`='%i', `to_slot`='%i', `item_id`='%i', " - "`charges`='%i', `aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].from_id, QS->items[i].from_slot, QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id, - QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Trade Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } + if(detailCount == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < detailCount; i++) { + query = StringFormat("INSERT INTO `qs_player_trade_record_entries` SET `event_id` = '%i', " + "`from_id` = '%i', `from_slot` = '%i', `to_id` = '%i', `to_slot` = '%i', " + "`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', " + "`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].from_id, QS->items[i].from_slot, + QS->items[i].to_id, QS->items[i].to_slot, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Trade Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + +} + +void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 detailCount) { + + std::string query = StringFormat("INSERT INTO `qs_player_handin_record` SET `time` = NOW(), " + "`quest_id` = '%i', `char_id` = '%i', `char_pp` = '%i', " + "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " + "`char_items` = '%i', `npc_id` = '%i', `npc_pp` = '%i', " + "`npc_gp` = '%i', `npc_sp` = '%i', `npc_cp` = '%i', " + "`npc_items`='%i'", + QS->quest_id, QS->char_id, QS->char_money.platinum, + QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, + QS->char_count, QS->npc_id, QS->npc_money.platinum, + QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, + QS->npc_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Handin Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + if(detailCount == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < detailCount; i++) { + query = StringFormat("INSERT INTO `qs_player_handin_record_entries` SET `event_id` = '%i', " + "`action_type` = '%s', `char_slot` = '%i', `item_id` = '%i', " + "`charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', " + "`aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].action_type, QS->items[i].char_slot, + QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, + QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Handin Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + } + +} + +void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 members){ + + std::string query = StringFormat("INSERT INTO `qs_player_npc_kill_record` " + "SET `npc_id` = '%i', `type` = '%i', " + "`zone_id` = '%i', `time` = NOW()", + QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed NPC Kill Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + if(members == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for (int i = 0; i < members; i++) { + query = StringFormat("INSERT INTO `qs_player_npc_kill_record_entries` " + "SET `event_id` = '%i', `char_id` = '%i'", + lastIndex, QS->Chars[i].char_id); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed NPC Kill Log Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } + } + } -void Database::LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 Items) { +void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 items) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record` SET `time`=NOW(), `quest_id`='%i', " - "`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i', " - "`npc_id`='%i', `npc_pp`='%i', `npc_gp`='%i', `npc_sp`='%i', `npc_cp`='%i', `npc_items`='%i'", - QS->quest_id, QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count, - QS->npc_id, QS->npc_money.platinum, QS->npc_money.gold, QS->npc_money.silver, QS->npc_money.copper, QS->npc_count), - errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Handin Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + std::string query = StringFormat("INSERT INTO `qs_player_delete_record` SET `time` = NOW(), " + "`char_id` = '%i', `stack_size` = '%i', `char_items` = '%i'", + QS->char_id, QS->stack_size, QS->char_count, QS->char_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Delete Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_handin_record_entries` SET `event_id`='%i', " - "`action_type`='%s', `char_slot`='%i', `item_id`='%i', `charges`='%i', " - "`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].action_type, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, - QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Handin Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } - } - } + if(items == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < items; i++) { + query = StringFormat("INSERT INTO `qs_player_delete_record_entries` SET `event_id` = '%i', " + "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " + "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, + QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Delete Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + } -void Database::LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record` SET `npc_id`='%i', `type`='%i', `zone_id`='%i', `time`=NOW()", QS->s1.NPCID, QS->s1.Type, QS->s1.ZoneID), errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed NPC Kill Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); +void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 items) { + /* These are item moves */ + + std::string query = StringFormat("INSERT INTO `qs_player_move_record` SET `time` = NOW(), " + "`char_id` = '%i', `from_slot` = '%i', `to_slot` = '%i', " + "`stack_size` = '%i', `char_items` = '%i', `postaction` = '%i'", + QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, + QS->char_count, QS->postaction); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Move Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - if(Members > 0){ - for (int i = 0; i < Members; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_npc_kill_record_entries` SET `event_id`='%i', `char_id`='%i'", lastid, QS->Chars[i].char_id, errbuf, 0, 0))) { - _log(NET__WORLD, "Failed NPC Kill Log Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } - } - } + if(items == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < items; i++) { + query = StringFormat("INSERT INTO `qs_player_move_record_entries` SET `event_id` = '%i', " + "`from_slot` = '%i', `to_slot` = '%i', `item_id` = '%i', `charges` = '%i', " + "`aug_1` = '%i', `aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Move Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + } -void Database::LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record` SET `time`=NOW(), " - "`char_id`='%i', `stack_size`='%i', `char_items`='%i'", - QS->char_id, QS->stack_size, QS->char_count, QS->char_count), - errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Delete Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); +void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 items) { + /* Merchant transactions are from the perspective of the merchant, not the player -U */ + std::string query = StringFormat("INSERT INTO `qs_merchant_transaction_record` SET `time` = NOW(), " + "`zone_id` = '%i', `merchant_id` = '%i', `merchant_pp` = '%i', " + "`merchant_gp` = '%i', `merchant_sp` = '%i', `merchant_cp` = '%i', " + "`merchant_items` = '%i', `char_id` = '%i', `char_pp` = '%i', " + "`char_gp` = '%i', `char_sp` = '%i', `char_cp` = '%i', " + "`char_items` = '%i'", + QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, + QS->merchant_money.gold, QS->merchant_money.silver, + QS->merchant_money.copper, QS->merchant_count, QS->char_id, + QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, + QS->char_money.copper, QS->char_count); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Transaction Log Record Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); } - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_delete_record_entries` SET `event_id`='%i', " - "`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', " - "`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, - QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Delete Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } - } - } + if(items == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for(int i = 0; i < items; i++) { + query = StringFormat("INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id` = '%i', " + "`char_slot` = '%i', `item_id` = '%i', `charges` = '%i', `aug_1` = '%i', " + "`aug_2` = '%i', `aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, + QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, + QS->items[i].aug_5); + results = QueryDatabase(query); + if(!results.Success()) { + _log(QUERYSERV__ERROR, "Failed Transaction Log Record Entry Insert: %s", results.ErrorMessage().c_str()); + _log(QUERYSERV__ERROR, "%s", query.c_str()); + } + + } + } -void Database::LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items) { +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 *queryBuffer = new char[pack->ReadUInt32() + 1]; + pack->ReadString(queryBuffer); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record` SET `time`=NOW(), " - "`char_id`='%i', `from_slot`='%i', `to_slot`='%i', `stack_size`='%i', `char_items`='%i', `postaction`='%i'", - QS->char_id, QS->from_slot, QS->to_slot, QS->stack_size, QS->char_count, QS->postaction), - errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Move Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); + 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()); } - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_player_move_record_entries` SET `event_id`='%i', " - "`from_slot`='%i', `to_slot`='%i', `item_id`='%i', `charges`='%i', " - "`aug_1`='%i', `aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", lastid, - QS->items[i].from_slot, QS->items[i].to_slot, QS->items[i].item_id, QS->items[i].charges, - QS->items[i].aug_1, QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Move Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } - } - } + safe_delete(pack); + safe_delete(queryBuffer); } - -void Database::LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items) { - // Merchant transactions are from the perspective of the merchant, not the player -U - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 lastid = 0; - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record` SET `time`=NOW(), " - "`zone_id`='%i', `merchant_id`='%i', `merchant_pp`='%i', `merchant_gp`='%i', `merchant_sp`='%i', `merchant_cp`='%i', `merchant_items`='%i', " - "`char_id`='%i', `char_pp`='%i', `char_gp`='%i', `char_sp`='%i', `char_cp`='%i', `char_items`='%i'", - QS->zone_id, QS->merchant_id, QS->merchant_money.platinum, QS->merchant_money.gold, QS->merchant_money.silver, QS->merchant_money.copper, QS->merchant_count, - QS->char_id, QS->char_money.platinum, QS->char_money.gold, QS->char_money.silver, QS->char_money.copper, QS->char_count), - errbuf, 0, 0, &lastid)) { - _log(NET__WORLD, "Failed Transaction Log Record Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } - - if(Items > 0) { - for(int i = 0; i < Items; i++) { - if(!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `qs_merchant_transaction_record_entries` SET `event_id`='%i', " - "`char_slot`='%i', `item_id`='%i', `charges`='%i', `aug_1`='%i', " - "`aug_2`='%i', `aug_3`='%i', `aug_4`='%i', `aug_5`='%i'", - lastid, QS->items[i].char_slot, QS->items[i].item_id, QS->items[i].charges, QS->items[i].aug_1, - QS->items[i].aug_2, QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5, - errbuf, 0, 0))) { - _log(NET__WORLD, "Failed Transaction Log Record Entry Insert: %s", errbuf); - _log(NET__WORLD, "%s", query); - } - } - } -} - diff --git a/queryserv/database.h b/queryserv/database.h index 61664f90d..a25d91611 100644 --- a/queryserv/database.h +++ b/queryserv/database.h @@ -42,14 +42,14 @@ public: bool Connect(const char* host, const char* user, const char* passwd, const char* database,uint32 port); ~Database(); - bool GetVariable(const char* varname, char* varvalue, uint16 varvalue_len); void AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type); - void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 Items); - void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 Items); + void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount); + void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount); void LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members); void LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items); void LogPlayerMove(QSPlayerLogMove_Struct* QS, uint32 Items); void LogMerchantTransaction(QSMerchantLogTransaction_Struct* QS, uint32 Items); + void GeneralQueryReceive(ServerPacket *pack); protected: void HandleMysqlError(uint32 errnum); private: diff --git a/queryserv/lfguild.cpp b/queryserv/lfguild.cpp index 37f80bf28..c135d4991 100644 --- a/queryserv/lfguild.cpp +++ b/queryserv/lfguild.cpp @@ -2,7 +2,7 @@ #include "lfguild.h" #include "database.h" #include "worldserver.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "../common/packet_dump.h" #include "../common/rulesys.h" @@ -22,7 +22,7 @@ PlayerLookingForGuild::PlayerLookingForGuild(char *Name, char *Comments, uint32 GuildLookingForPlayers::GuildLookingForPlayers(char *Name, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Timezone, uint32 TimePosted) { - this->Name = Name; + this->Name = Name; this->Comments = Comments; this->FromLevel = FromLevel; this->ToLevel = ToLevel; @@ -34,37 +34,27 @@ GuildLookingForPlayers::GuildLookingForPlayers(char *Name, char *Comments, uint3 bool LFGuildManager::LoadDatabase() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!database.RunQuery(query,MakeAnyLenString(&query, "SELECT `type`,`name`,`comment`, `fromlevel`, `tolevel`, `classes`, `aacount`, `timezone`, `timeposted` FROM `lfguild`"),errbuf,&result)){ - - _log(QUERYSERV__ERROR, "Failed to load LFGuild info from database. %s %s", query, errbuf); - safe_delete_array(query); - + std::string query = "SELECT `type`,`name`,`comment`, " + "`fromlevel`, `tolevel`, `classes`, " + "`aacount`, `timezone`, `timeposted` FROM `lfguild`"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + _log(QUERYSERV__ERROR, "Failed to load LFGuild info from database. %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - while((row = mysql_fetch_row(result))) { - + for (auto row = results.begin(); row != results.end(); ++row) { uint32 type = atoul(row[0]); if(type == 0) { PlayerLookingForGuild p(row[1], row[2], atoul(row[3]), atoul(row[5]), atoul(row[6]), atoul(row[7]), atoul(row[8])); Players.push_back(p); + continue; } - else - { - GuildLookingForPlayers g(row[1], row[2], atoul(row[3]), atoul(row[4]), atoul(row[5]), atoul(row[6]), atoul(row[7]), atoul(row[8])); - Guilds.push_back(g); - } - } - mysql_free_result(result); + GuildLookingForPlayers g(row[1], row[2], atoul(row[3]), atoul(row[4]), atoul(row[5]), atoul(row[6]), atoul(row[7]), atoul(row[8])); + Guilds.push_back(g); + } return true; } @@ -242,37 +232,31 @@ void LFGuildManager::SendGuildMatches(uint32 FromZoneID, uint32 FromInstanceID, void LFGuildManager::TogglePlayer(uint32 FromZoneID, uint32 FromInstanceID, char *From, uint32 Class, uint32 Level, uint32 AAPoints, char *Comments, uint32 Toggle, uint32 TimeZone) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - std::list::iterator it; - - for(it = Players.begin(); it != Players.end(); ++it) - { - if(!strcasecmp((*it).Name.c_str(), From)) - { + for(auto it = Players.begin(); it != Players.end(); ++it) + if(!strcasecmp((*it).Name.c_str(), From)) { Players.erase(it); - break; } - } - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `lfguild` WHERE `type` = 0 AND `name` = '%s'", From), errbuf, 0, 0)) - _log(QUERYSERV__ERROR, "Error removing player from LFGuild table, query was %s, %s", query, errbuf); - - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM `lfguild` WHERE `type` = 0 AND `name` = '%s'", From); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(QUERYSERV__ERROR, "Error removing player from LFGuild table, query was %s, %s", query.c_str(), results.ErrorMessage().c_str()); uint32 Now = time(nullptr); - if(Toggle == 1) - { + if(Toggle == 1) { PlayerLookingForGuild p(From, Comments, Level, Class, AAPoints, TimeZone, Now); Players.push_back(p); - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `lfguild` (`type`, `name`, `comment`, `fromlevel`, `tolevel`, `classes`, `aacount`, `timezone`, `timeposted`) VALUES(0, '%s', '%s', %u, 0, %u, %u, %u, %u)", From, Comments, Level, Class, AAPoints, TimeZone, Now), errbuf, 0, 0)) - _log(QUERYSERV__ERROR, "Error inserting player into LFGuild table, query was %s, %s", query, errbuf); - - safe_delete_array(query); + query = StringFormat("INSERT INTO `lfguild` " + "(`type`, `name`, `comment`, `fromlevel`, `tolevel`, " + "`classes`, `aacount`, `timezone`, `timeposted`) " + "VALUES (0, '%s', '%s', %u, 0, %u, %u, %u, %u)", + From, Comments, Level, Class, AAPoints, TimeZone, Now); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(QUERYSERV__ERROR, "Error inserting player into LFGuild table, query was %s, %s", query.c_str(), results.ErrorMessage().c_str()); } ServerPacket *pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(From) + strlen(Comments) + 30); @@ -289,29 +273,21 @@ void LFGuildManager::TogglePlayer(uint32 FromZoneID, uint32 FromInstanceID, char worldserver->SendPacket(pack); safe_delete(pack); - } void LFGuildManager::ToggleGuild(uint32 FromZoneID, uint32 FromInstanceID, char *From, char* GuildName, char *Comments, uint32 FromLevel, uint32 ToLevel, uint32 Classes, uint32 AACount, uint32 Toggle, uint32 TimeZone) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - std::list::iterator it; - - for(it = Guilds.begin(); it != Guilds.end(); ++it) - { + for(auto it = Guilds.begin(); it != Guilds.end(); ++it) if(!strcasecmp((*it).Name.c_str(), GuildName)) { Guilds.erase(it); break; } - } - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `lfguild` WHERE `type` = 1 AND `name` = '%s'", GuildName), errbuf, 0, 0)) - _log(QUERYSERV__ERROR, "Error removing guild from LFGuild table, query was %s, %s", query, errbuf); - - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM `lfguild` WHERE `type` = 1 AND `name` = '%s'", GuildName); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(QUERYSERV__ERROR, "Error removing guild from LFGuild table, query was %s, %s", query.c_str(), results.ErrorMessage().c_str()); uint32 Now = time(nullptr); @@ -319,12 +295,19 @@ void LFGuildManager::ToggleGuild(uint32 FromZoneID, uint32 FromInstanceID, char { GuildLookingForPlayers g(GuildName, Comments, FromLevel, ToLevel, Classes, AACount, TimeZone, Now); Guilds.push_back(g); - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `lfguild` (`type`, `name`, `comment`, `fromlevel`, `tolevel`, `classes`, `aacount`, `timezone`, `timeposted`) VALUES(1, '%s', '%s', %u, %u, %u, %u, %u, %u)", GuildName, Comments, FromLevel, ToLevel, Classes, AACount, TimeZone, Now), errbuf, 0, 0)) - _log(QUERYSERV__ERROR, "Error inserting guild into LFGuild table, query was %s, %s", query, errbuf); - safe_delete_array(query); + query = StringFormat("INSERT INTO `lfguild` " + "(`type`, `name`, `comment`, `fromlevel`, `tolevel`, " + "`classes`, `aacount`, `timezone`, `timeposted`) " + "VALUES (1, '%s', '%s', %u, %u, %u, %u, %u, %u)", + GuildName, Comments, FromLevel, ToLevel, + Classes, AACount, TimeZone, Now); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(QUERYSERV__ERROR, "Error inserting guild into LFGuild table, query was %s, %s", query.c_str(), results.ErrorMessage().c_str()); } + ServerPacket *pack = new ServerPacket(ServerOP_LFGuildUpdate, strlen(GuildName) + strlen(Comments) + 30); pack->WriteString(GuildName); @@ -343,36 +326,30 @@ void LFGuildManager::ToggleGuild(uint32 FromZoneID, uint32 FromInstanceID, char void LFGuildManager::ExpireEntries() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - std::list::iterator it; - std::list::iterator it2; - - for(it = Players.begin(); it != Players.end(); ++it) + for(auto it = Players.begin(); it != Players.end(); ++it) { - if((*it).TimePosted + 604800 <= (uint32)time(nullptr)) - { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE from `lfguild` WHERE `type` = 0 AND `name` = '%s'", (*it).Name.c_str()), errbuf, 0, 0)) - _log(QUERYSERV__ERROR, "Error expiring player LFGuild entry, query was %s, %s", query, errbuf); + if((*it).TimePosted + 604800 > (uint32)time(nullptr)) + continue; - safe_delete_array(query); + std::string query = StringFormat("DELETE from `lfguild` WHERE `type` = 0 AND `name` = '%s'", (*it).Name.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(QUERYSERV__ERROR, "Error expiring player LFGuild entry, query was %s, %s", query.c_str(), results.ErrorMessage().c_str()); - it = Players.erase(it); - } - } + it = Players.erase(it); + } - for(it2 = Guilds.begin(); it2 != Guilds.end(); ++it2) + for(auto it2 = Guilds.begin(); it2 != Guilds.end(); ++it2) { - if((*it2).TimePosted + 2592000 <= time(nullptr)) - { - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE from `lfguild` WHERE `type` = 1 AND `name` = '%s'", (*it2).Name.c_str()), errbuf, 0, 0)) - _log(QUERYSERV__ERROR, "Error removing guild LFGuild entry, query was %s, %s", query, errbuf); + if((*it2).TimePosted + 2592000 > time(nullptr)) + continue; - safe_delete_array(query); + std::string query = StringFormat("DELETE from `lfguild` WHERE `type` = 1 AND `name` = '%s'", (*it2).Name.c_str()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + _log(QUERYSERV__ERROR, "Error removing guild LFGuild entry, query was %s, %s", query.c_str(), results.ErrorMessage().c_str()); - it2 = Guilds.erase(it2); - } + it2 = Guilds.erase(it2); } } diff --git a/queryserv/queryserv.cpp b/queryserv/queryserv.cpp index 30dc9970e..22ddb87ee 100644 --- a/queryserv/queryserv.cpp +++ b/queryserv/queryserv.cpp @@ -19,7 +19,7 @@ #include "../common/debug.h" #include "../common/opcodemgr.h" -#include "../common/EQStreamFactory.h" +#include "../common/eq_stream_factory.h" #include "../common/rulesys.h" #include "../common/servertalk.h" #include "../common/platform.h" @@ -33,56 +33,47 @@ volatile bool RunLoops = true; -uint32 MailMessagesSent = 0; -uint32 ChatMessagesSent = 0; - TimeoutManager timeout_manager; - Database database; LFGuildManager lfguildmanager; std::string WorldShortName; - const queryservconfig *Config; - WorldServer *worldserver = 0; - -void CatchSignal(int sig_num) { - - RunLoops = false; - +void CatchSignal(int sig_num) { + RunLoops = false; if(worldserver) worldserver->Disconnect(); } int main() { RegisterExecutablePlatform(ExePlatformQueryServ); - set_exception_handler(); - - Timer LFGuildExpireTimer(60000); - + set_exception_handler(); + Timer LFGuildExpireTimer(60000); Timer InterserverTimer(INTERSERVER_TIMER); // does auto-reconnect + /* Load XML from eqemu_config.xml + + 127.0.0.1 + 3306 + user + password + dbname + + */ + _log(QUERYSERV__INIT, "Starting EQEmu QueryServ."); - if (!queryservconfig::LoadConfig()) { - _log(QUERYSERV__INIT, "Loading server configuration failed."); - return 1; } - Config = queryservconfig::get(); - - if(!load_log_settings(Config->LogSettingsFile.c_str())) - _log(QUERYSERV__INIT, "Warning: Unable to read %s", Config->LogSettingsFile.c_str()); - else - _log(QUERYSERV__INIT, "Log settings loaded from %s", Config->LogSettingsFile.c_str()); - - WorldShortName = Config->ShortName; + Config = queryservconfig::get(); + WorldShortName = Config->ShortName; _log(QUERYSERV__INIT, "Connecting to MySQL..."); - + + /* MySQL Connection */ if (!database.Connect( Config->QSDatabaseHost.c_str(), Config->QSDatabaseUsername.c_str(), @@ -93,6 +84,12 @@ int main() { return 1; } + /* Initialize Logging */ + if (!load_log_settings(Config->LogSettingsFile.c_str())) + _log(QUERYSERV__INIT, "Warning: Unable to read %s", Config->LogSettingsFile.c_str()); + else + _log(QUERYSERV__INIT, "Log settings loaded from %s", Config->LogSettingsFile.c_str()); + if (signal(SIGINT, CatchSignal) == SIG_ERR) { _log(QUERYSERV__ERROR, "Could not set signal handler"); return 1; @@ -102,16 +99,15 @@ int main() { return 1; } + /* Initial Connection to Worldserver */ worldserver = new WorldServer; + worldserver->Connect(); - worldserver->Connect(); - + /* Load Looking For Guild Manager */ lfguildmanager.LoadDatabase(); - while(RunLoops) { - - Timer::SetCurrentTime(); - + while(RunLoops) { + Timer::SetCurrentTime(); if(LFGuildExpireTimer.Check()) lfguildmanager.ExpireEntries(); @@ -119,10 +115,8 @@ int main() { if (worldserver->TryReconnect() && (!worldserver->Connected())) worldserver->AsyncConnect(); } - worldserver->Process(); - - timeout_manager.CheckTimeouts(); - + worldserver->Process(); + timeout_manager.CheckTimeouts(); Sleep(100); } } diff --git a/queryserv/queryservconfig.h b/queryserv/queryservconfig.h index e28f6b02a..b48ecdecd 100644 --- a/queryserv/queryservconfig.h +++ b/queryserv/queryservconfig.h @@ -20,7 +20,7 @@ #ifndef __queryservconfig_H #define __queryservconfig_H -#include "../common/EQEmuConfig.h" +#include "../common/eqemu_config.h" class queryservconfig : public EQEmuConfig { public: diff --git a/queryserv/worldserver.cpp b/queryserv/worldserver.cpp index bb7523592..fc87929ca 100644 --- a/queryserv/worldserver.cpp +++ b/queryserv/worldserver.cpp @@ -56,122 +56,107 @@ void WorldServer::OnConnected() void WorldServer::Process() { - WorldConnection::Process(); - + WorldConnection::Process(); if (!Connected()) return; - ServerPacket *pack = 0; - + ServerPacket *pack = 0; while((pack = tcpc.PopPacket())) { - _log(QUERYSERV__TRACE, "Received Opcode: %4X", pack->opcode); - - switch(pack->opcode) - { + _log(QUERYSERV__TRACE, "Received Opcode: %4X", pack->opcode); + switch(pack->opcode) { case 0: { break; } - case ServerOP_KeepAlive: - { + case ServerOP_KeepAlive: { break; } - case ServerOP_Speech: - { - Server_Speech_Struct *SSS = (Server_Speech_Struct*)pack->pBuffer; - + case ServerOP_Speech: { + Server_Speech_Struct *SSS = (Server_Speech_Struct*)pack->pBuffer; std::string tmp1 = SSS->from; - std::string tmp2 = SSS->to; - + std::string tmp2 = SSS->to; database.AddSpeech(tmp1.c_str(), tmp2.c_str(), SSS->message, SSS->minstatus, SSS->guilddbid, SSS->type); break; } - case ServerOP_QSPlayerLogTrades: - { + case ServerOP_QSPlayerLogTrades: { QSPlayerLogTrade_Struct *QS = (QSPlayerLogTrade_Struct*)pack->pBuffer; - uint32 Items = QS->char1_count + QS->char2_count; - database.LogPlayerTrade(QS, Items); + database.LogPlayerTrade(QS, QS->_detail_count); break; } - case ServerOP_QSPlayerLogHandins: - { + case ServerOP_QSPlayerLogHandins: { QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)pack->pBuffer; - uint32 Items = QS->char_count + QS->npc_count; - database.LogPlayerHandin(QS, Items); + database.LogPlayerHandin(QS, QS->_detail_count); break; } - case ServerOP_QSPlayerLogNPCKills: - { + case ServerOP_QSPlayerLogNPCKills: { QSPlayerLogNPCKill_Struct *QS = (QSPlayerLogNPCKill_Struct*)pack->pBuffer; uint32 Members = pack->size - sizeof(QSPlayerLogNPCKill_Struct); if (Members > 0) Members = Members / sizeof(QSPlayerLogNPCKillsPlayers_Struct); database.LogPlayerNPCKill(QS, Members); break; } - case ServerOP_QSPlayerLogDeletes: - { + case ServerOP_QSPlayerLogDeletes: { QSPlayerLogDelete_Struct *QS = (QSPlayerLogDelete_Struct*)pack->pBuffer; uint32 Items = QS->char_count; database.LogPlayerDelete(QS, Items); break; } - case ServerOP_QSPlayerLogMoves: - { + case ServerOP_QSPlayerLogMoves: { QSPlayerLogMove_Struct *QS = (QSPlayerLogMove_Struct*)pack->pBuffer; uint32 Items = QS->char_count; database.LogPlayerMove(QS, Items); break; } - case ServerOP_QSMerchantLogTransactions: - { + case ServerOP_QSPlayerLogMerchantTransactions: { QSMerchantLogTransaction_Struct *QS = (QSMerchantLogTransaction_Struct*)pack->pBuffer; uint32 Items = QS->char_count + QS->merchant_count; database.LogMerchantTransaction(QS, Items); - break; + break; } - case ServerOP_QueryServGeneric: - { - // The purpose of ServerOP_QueryServerGeneric is so that we don't have to add code to world just to relay packets - // each time we add functionality to queryserv. - // - // A ServerOP_QueryServGeneric packet has the following format: - // - // uint32 SourceZoneID - // uint32 SourceInstanceID - // char OriginatingCharacterName[0] // Null terminated name of the character this packet came from. This could be just - // // an empty string if it has no meaning in the context of a particular packet. - // uint32 Type - // - // The 'Type' field is a 'sub-opcode'. A value of 0 is used for the LFGuild packets. The next feature to be added - // to queryserv would use 1, etc. - // - // Obviously, any fields in the packet following the 'Type' will be unique to the particular type of packet. The - // 'Generic' in the name of this ServerOP code relates to the four header fields. + case ServerOP_QueryServGeneric: { + /* + The purpose of ServerOP_QueryServerGeneric is so that we don't have to add code to world just to relay packets + each time we add functionality to queryserv. + + A ServerOP_QueryServGeneric packet has the following format: + + uint32 SourceZoneID + uint32 SourceInstanceID + char OriginatingCharacterName[0] + - Null terminated name of the character this packet came from. This could be just + - an empty string if it has no meaning in the context of a particular packet. + uint32 Type + + The 'Type' field is a 'sub-opcode'. A value of 0 is used for the LFGuild packets. The next feature to be added + to queryserv would use 1, etc. + + Obviously, any fields in the packet following the 'Type' will be unique to the particular type of packet. The + 'Generic' in the name of this ServerOP code relates to the four header fields. + */ + char From[64]; pack->SetReadPosition(8); pack->ReadString(From); uint32 Type = pack->ReadUInt32(); - switch(Type) - { - case QSG_LFGuild: - { - lfguildmanager.HandlePacket(pack); + switch(Type) { + case QSG_LFGuild:{ + lfguildmanager.HandlePacket(pack); break; } - default: _log(QUERYSERV__ERROR, "Received unhandled ServerOP_QueryServGeneric", Type); break; } - break; } - - + case ServerOP_QSSendQuery: { + /* Process all packets here */ + database.GeneralQueryReceive(pack); + break; + } } - } - + } safe_delete(pack); return; } diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index 1c15b0a8e..8cc554441 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -19,7 +19,7 @@ #include #include "../common/debug.h" #include "../common/shareddb.h" -#include "../common/EQEmuConfig.h" +#include "../common/eqemu_config.h" #include "../common/platform.h" #include "../common/crash.h" #include "../common/rulesys.h" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4c4f32f0d..1c13ae26c 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,18 +7,23 @@ 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 - atobool_test.h - hextoi_32_64_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/atobool_test.h b/tests/atobool_test.h index ce02df02d..5cc9ad872 100644 --- a/tests/atobool_test.h +++ b/tests/atobool_test.h @@ -20,7 +20,7 @@ #define __EQEMU_TESTS_ATOBOOL_H #include "cppunit/cpptest.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" class atoboolTest : public Test::Suite { typedef void(atoboolTest::*TestFunction)(void); 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/fixed_memory_test.h b/tests/fixed_memory_test.h index 089b8f7c0..fcff0c9e8 100644 --- a/tests/fixed_memory_test.h +++ b/tests/fixed_memory_test.h @@ -21,7 +21,7 @@ #include "cppunit/cpptest.h" #include "../common/fixed_memory_hash_set.h" -#include "../common/Item.h" +#include "../common/item.h" class FixedMemoryHashTest : public Test::Suite { typedef void(FixedMemoryHashTest::*TestFunction)(void); diff --git a/tests/hextoi_32_64_test.h b/tests/hextoi_32_64_test.h index eda5e7fc2..1ad6c9d24 100644 --- a/tests/hextoi_32_64_test.h +++ b/tests/hextoi_32_64_test.h @@ -20,7 +20,7 @@ #define __EQEMU_TESTS_HEXTOI_32_64_H #include "cppunit/cpptest.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" class hextoi_32_64_Test : public Test::Suite { typedef void(hextoi_32_64_Test::*TestFunction)(void); diff --git a/tests/main.cpp b/tests/main.cpp index fd0862edb..d64dfead4 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -26,6 +26,9 @@ #include "fixed_memory_variable_test.h" #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 { @@ -38,6 +41,9 @@ int main() { tests.add(new FixedMemoryVariableHashTest()); 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 new file mode 100644 index 000000000..facb0cc72 --- /dev/null +++ b/tests/string_util_test.h @@ -0,0 +1,85 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2013 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_STRING_UTIL_H +#define __EQEMU_TESTS_STRING_UTIL_H + +#include "cppunit/cpptest.h" +#include "../common/string_util.h" + +class StringUtilTest : public Test::Suite { + typedef void(StringUtilTest::*TestFunction)(void); +public: + StringUtilTest() { + TEST_ADD(StringUtilTest::StringFormatTest); + TEST_ADD(StringUtilTest::EscapeStringTest); + TEST_ADD(StringUtilTest::EscapeStringMemoryTest); + } + + ~StringUtilTest() { + } + + private: + void StringFormatTest() { + const char* fmt = "Test: %c %d %4.2f"; + char c = 'a'; + int i = 2014; + float f = 3.1416; + + auto s = StringFormat(fmt, c, i, f); + TEST_ASSERT_EQUALS(s.length(), 17); + TEST_ASSERT(s.compare("Test: a 2014 3.14") == 0); + } + + void EscapeStringTest() { + std::string t; + t.resize(10); + t[0] = 'a'; + t[1] = 'b'; + t[2] = 'c'; + t[3] = '\x00'; + t[4] = '\n'; + t[5] = '\r'; + t[6] = '\\'; + t[7] = '\''; + t[8] = '\"'; + t[9] = '\x1a'; + + auto s = EscapeString(t); + TEST_ASSERT(s.compare("abc\\x00\\n\\r\\\\\\'\\\"\\x1a") == 0); + } + + void EscapeStringMemoryTest() { + char t[10] = { 0 }; + t[0] = 'a'; + t[1] = 'b'; + t[2] = 'c'; + t[3] = '\x00'; + t[4] = '\n'; + t[5] = '\r'; + t[6] = '\\'; + t[7] = '\''; + t[8] = '\"'; + t[9] = '\x1a'; + + auto s = EscapeString(t, 10); + TEST_ASSERT(s.compare("abc\\x00\\n\\r\\\\\\'\\\"\\x1a") == 0); + } +}; + +#endif diff --git a/ucs/chatchannel.cpp b/ucs/chatchannel.cpp index a33ad65db..56b7eb5b9 100644 --- a/ucs/chatchannel.cpp +++ b/ucs/chatchannel.cpp @@ -20,7 +20,7 @@ #include "chatchannel.h" #include "clientlist.h" #include "database.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include extern Database database; diff --git a/ucs/clientlist.cpp b/ucs/clientlist.cpp index c2b887ff0..8a60fc749 100644 --- a/ucs/clientlist.cpp +++ b/ucs/clientlist.cpp @@ -18,15 +18,15 @@ */ #include "../common/debug.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "clientlist.h" #include "database.h" #include "chatchannel.h" -#include "../common/EQStreamFactory.h" -#include "../common/EmuTCPConnection.h" -#include "../common/EmuTCPServer.h" +#include "../common/eq_stream_factory.h" +#include "../common/emu_tcp_connection.h" +#include "../common/emu_tcp_server.h" #include #include #include @@ -1329,7 +1329,7 @@ void Client::SendChannelMessage(std::string Message) } } - if(RequiredChannel) + if(RequiredChannel) { if(RuleB(Chat, EnableAntiSpam)) { if(!RequiredChannel->IsModerated() || RequiredChannel->HasVoice(GetName()) || RequiredChannel->IsOwner(GetName()) || @@ -1384,7 +1384,7 @@ void Client::SendChannelMessage(std::string Message) else GeneralChannelMessage("Channel " + ChannelName + " is moderated and you have not been granted a voice."); } - + } } void Client::SendChannelMessageByNumber(std::string Message) { @@ -1883,11 +1883,12 @@ void Client::ChannelModerate(std::string CommandString) { RequiredChannel->SetModerated(!RequiredChannel->IsModerated()); - if(!RequiredChannel->IsClientInChannel(this)) - if(RequiredChannel->IsModerated()) + if (!RequiredChannel->IsClientInChannel(this)) { + if (RequiredChannel->IsModerated()) GeneralChannelMessage("Channel " + ChannelName + " is now moderated."); else GeneralChannelMessage("Channel " + ChannelName + " is no longer moderated."); + } } diff --git a/ucs/clientlist.h b/ucs/clientlist.h index b8a1609ea..266986542 100644 --- a/ucs/clientlist.h +++ b/ucs/clientlist.h @@ -21,8 +21,8 @@ #define CHATSERVER_CLIENTLIST_H #include "../common/opcodemgr.h" -#include "../common/EQStreamType.h" -#include "../common/EQStreamFactory.h" +#include "../common/eq_stream_type.h" +#include "../common/eq_stream_factory.h" #include "../common/rulesys.h" #include "chatchannel.h" #include diff --git a/ucs/database.cpp b/ucs/database.cpp index 4e41a3180..8130585d1 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -43,8 +43,8 @@ #include "database.h" #include "../common/eq_packet_structs.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "chatchannel.h" extern Clientlist *CL; @@ -102,208 +102,143 @@ Database::~Database() { } -void Database::GetAccountStatus(Client *c) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `status`, `hideme`, `karma`, `revoked` from `account` where `id`='%i' limit 1", - c->GetAccountID()),errbuf,&result)){ - - _log(UCS__ERROR, "Unable to get account status for character %s, error %s", c->GetName().c_str(), errbuf); - - safe_delete_array(query); +void Database::GetAccountStatus(Client *client) { + std::string query = StringFormat("SELECT `status`, `hideme`, `karma`, `revoked` " + "FROM `account` WHERE `id` = '%i' LIMIT 1", + client->GetAccountID()); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(UCS__ERROR, "Unable to get account status for character %s, error %s", client->GetName().c_str(), results.ErrorMessage().c_str()); return; } - _log(UCS__TRACE, "GetAccountStatus Query: %s", query); - safe_delete_array(query); - if(mysql_num_rows(result) != 1) + _log(UCS__TRACE, "GetAccountStatus Query: %s", query.c_str()); + + if(results.RowCount() != 1) { _log(UCS__ERROR, "Error in GetAccountStatus"); - mysql_free_result(result); return; } - row = mysql_fetch_row(result); + auto row = results.begin(); - c->SetAccountStatus(atoi(row[0])); - c->SetHideMe(atoi(row[1]) != 0); - c->SetKarma(atoi(row[2])); - c->SetRevoked((atoi(row[3])==1?true:false)); + client->SetAccountStatus(atoi(row[0])); + client->SetHideMe(atoi(row[1]) != 0); + client->SetKarma(atoi(row[2])); + client->SetRevoked((atoi(row[3])==1?true:false)); + + _log(UCS__TRACE, "Set account status to %i, hideme to %i and karma to %i for %s", client->GetAccountStatus(), client->GetHideMe(), client->GetKarma(), client->GetName().c_str()); - _log(UCS__TRACE, "Set account status to %i, hideme to %i and karma to %i for %s", c->GetAccountStatus(), c->GetHideMe(), c->GetKarma(), c->GetName().c_str()); - mysql_free_result(result); } -int Database::FindAccount(const char *CharacterName, Client *c) { +int Database::FindAccount(const char *characterName, Client *client) { - _log(UCS__TRACE, "FindAccount for character %s", CharacterName); + _log(UCS__TRACE, "FindAccount for character %s", characterName); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - c->ClearCharacters(); - - if (!RunQuery(query,MakeAnyLenString(&query, "select `id`, `account_id`, `level` from `character_` where `name`='%s' limit 1", - CharacterName),errbuf,&result)) - { - _log(UCS__ERROR, "FindAccount query failed: %s", query); - safe_delete_array(query); + client->ClearCharacters(); + std::string query = StringFormat("SELECT `id`, `account_id`, `level` " + "FROM `character_data` WHERE `name` = '%s' LIMIT 1", + characterName); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(UCS__ERROR, "FindAccount query failed: %s", query.c_str()); return -1; } - safe_delete_array(query); - if (mysql_num_rows(result) != 1) - { + if (results.RowCount() != 1) { _log(UCS__ERROR, "Bad result from query"); - mysql_free_result(result); return -1; } - row = mysql_fetch_row(result); - c->AddCharacter(atoi(row[0]), CharacterName, atoi(row[2])); - int AccountID = atoi(row[1]); + auto row = results.begin(); + client->AddCharacter(atoi(row[0]), characterName, atoi(row[2])); - mysql_free_result(result); - _log(UCS__TRACE, "Account ID for %s is %i", CharacterName, AccountID); + int accountID = atoi(row[1]); - if (!RunQuery(query,MakeAnyLenString(&query, "select `id`, `name`, `level` from `character_` where `account_id`=%i and `name` !='%s'", - AccountID, CharacterName),errbuf,&result)) - { - safe_delete_array(query); - return AccountID; - } - safe_delete_array(query); + _log(UCS__TRACE, "Account ID for %s is %i", characterName, accountID); - for(unsigned int i = 0; i < mysql_num_rows(result); i++) - { - row = mysql_fetch_row(result); - c->AddCharacter(atoi(row[0]), row[1], atoi(row[2])); - } - mysql_free_result(result); - return AccountID; + query = StringFormat("SELECT `id`, `name`, `level` FROM `character_data` " + "WHERE `account_id` = %i AND `name` != '%s'", + accountID, characterName); + results = QueryDatabase(query); + if (!results.Success()) + return accountID; + + for (auto row = results.begin(); row != results.end(); ++row) + client->AddCharacter(atoi(row[0]), row[1], atoi(row[2])); + + return accountID; } -bool Database::VerifyMailKey(std::string CharacterName, int IPAddress, std::string MailKey) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `mailkey` from `character_` where `name`='%s' limit 1", - CharacterName.c_str()),errbuf,&result)){ - - safe_delete_array(query); - - _log(UCS__ERROR, "Error retrieving mailkey from database: %s", errbuf); +bool Database::VerifyMailKey(std::string characterName, int IPAddress, std::string MailKey) { + std::string query = StringFormat("SELECT `mailkey` FROM `character_data` WHERE `name`='%s' LIMIT 1", + characterName.c_str()); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(UCS__ERROR, "Error retrieving mailkey from database: %s", results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - row = mysql_fetch_row(result); + auto row = results.begin(); // The key is the client's IP address (expressed as 8 hex digits) and an 8 hex digit random string generated // by world. // - char CombinedKey[17]; + char combinedKey[17]; if(RuleB(Chat, EnableMailKeyIPVerification) == true) - sprintf(CombinedKey, "%08X%s", IPAddress, MailKey.c_str()); + sprintf(combinedKey, "%08X%s", IPAddress, MailKey.c_str()); else - sprintf(CombinedKey, "%s", MailKey.c_str()); + sprintf(combinedKey, "%s", MailKey.c_str()); - _log(UCS__TRACE, "DB key is [%s], Client key is [%s]", row[0], CombinedKey); - - bool Valid = !strcmp(row[0], CombinedKey); - - mysql_free_result(result); - - return Valid; + _log(UCS__TRACE, "DB key is [%s], Client key is [%s]", row[0], combinedKey); + return !strcmp(row[0], combinedKey); } -int Database::FindCharacter(const char *CharacterName) { +int Database::FindCharacter(const char *characterName) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - char *SafeCharName = RemoveApostrophes(CharacterName); - - if (!RunQuery(query,MakeAnyLenString(&query, "select `id` from `character_` where `name`='%s' limit 1", - SafeCharName),errbuf,&result)){ - - _log(UCS__ERROR, "FindCharacter failed. %s %s", query, errbuf); - - safe_delete_array(query); - safe_delete_array(SafeCharName); + char *safeCharName = RemoveApostrophes(characterName); + 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()); + safe_delete(safeCharName); + return -1; + } + safe_delete(safeCharName); + if (results.RowCount() != 1) { + _log(UCS__ERROR, "Bad result from FindCharacter query for character %s", characterName); return -1; } - safe_delete_array(query); - safe_delete_array(SafeCharName); + auto row = results.begin(); - if (mysql_num_rows(result) != 1) { - - _log(UCS__ERROR, "Bad result from FindCharacter query for character %s", CharacterName); - - mysql_free_result(result); - - return -1; - } - - row = mysql_fetch_row(result); - - int CharacterID = atoi(row[0]); - - mysql_free_result(result); - - return CharacterID; + int characterID = atoi(row[0]); + return characterID; } bool Database::GetVariable(const char* varname, char* varvalue, uint16 varvalue_len) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `value` from `variables` where `varname`='%s'", varname), errbuf, &result)) { - - _log(UCS__ERROR, "Unable to get message count from database. %s %s", query, errbuf); - - safe_delete_array(query); - + std::string query = StringFormat("SELECT `value` FROM `variables` WHERE `varname` = '%s'", varname); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(UCS__ERROR, "Unable to get message count from database. %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - if (mysql_num_rows(result) != 1) { - - mysql_free_result(result); - + if (results.RowCount() != 1) return false; - } - row = mysql_fetch_row(result); + auto row = results.begin(); snprintf(varvalue, varvalue_len, "%s", row[0]); - mysql_free_result(result); - return true; } @@ -311,302 +246,238 @@ bool Database::LoadChatChannels() { _log(UCS__INIT, "Loading chat channels from the database."); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `name`,`owner`,`password`, `minstatus` from `chatchannels`"),errbuf,&result)){ - - _log(UCS__ERROR, "Failed to load channels. %s %s", query, errbuf); - safe_delete_array(query); - + const std::string query = "SELECT `name`, `owner`, `password`, `minstatus` FROM `chatchannels`"; + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(UCS__ERROR, "Failed to load channels. %s %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); + for (auto row = results.begin();row != results.end(); ++row) { + std::string channelName = row[0]; + std::string channelOwner = row[1]; + std::string channelPassword = row[2]; - while((row = mysql_fetch_row(result))) { - - std::string ChannelName = row[0]; - std::string ChannelOwner = row[1]; - std::string ChannelPassword = row[2]; - - ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3])); + ChannelList->CreateChannel(channelName, channelOwner, channelPassword, true, atoi(row[3])); } - mysql_free_result(result); - return true; } -void Database::SetChannelPassword(std::string ChannelName, std::string Password) { +void Database::SetChannelPassword(std::string channelName, std::string password) { - _log(UCS__TRACE, "Database::SetChannelPassword(%s, %s)", ChannelName.c_str(), Password.c_str()); + _log(UCS__TRACE, "Database::SetChannelPassword(%s, %s)", channelName.c_str(), password.c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("UPDATE `chatchannels` SET `password` = '%s' WHERE `name` = '%s'", + password.c_str(), channelName.c_str()); + auto results = QueryDatabase(query); + if(!results.Success()) + _log(UCS__ERROR, "Error updating password in database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); - if(!RunQuery(query, MakeAnyLenString(&query, "UPDATE `chatchannels` set `password`='%s' where `name`='%s'", Password.c_str(), - ChannelName.c_str()), errbuf)) { - - _log(UCS__ERROR, "Error updating password in database: %s, %s", query, errbuf); - - } - - safe_delete_array(query); } -void Database::SetChannelOwner(std::string ChannelName, std::string Owner) { +void Database::SetChannelOwner(std::string channelName, std::string owner) { - _log(UCS__TRACE, "Database::SetChannelOwner(%s, %s)", ChannelName.c_str(), Owner.c_str()); + _log(UCS__TRACE, "Database::SetChannelOwner(%s, %s)", channelName.c_str(), owner.c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("UPDATE `chatchannels` SET `owner` = '%s' WHERE `name` = '%s'", + owner.c_str(), channelName.c_str()); + auto results = QueryDatabase(query); + if(!results.Success()) + _log(UCS__ERROR, "Error updating Owner in database: %s, %s", query.c_str(), results.ErrorMessage().c_str()); - if(!RunQuery(query, MakeAnyLenString(&query, "UPDATE `chatchannels` set `owner`='%s' where `name`='%s'", Owner.c_str(), - ChannelName.c_str()), errbuf)) { - - _log(UCS__ERROR, "Error updating Owner in database: %s, %s", query, errbuf); - - } - - safe_delete_array(query); } -void Database::SendHeaders(Client *c) { +void Database::SendHeaders(Client *client) { - int UnknownField2 = 25015275; - int UnknownField3 = 1; + int unknownField2 = 25015275; + int unknownField3 = 1; + int characterID = FindCharacter(client->MailBoxName().c_str()); - int CharacterID = FindCharacter(c->MailBoxName().c_str()); - _log(UCS__TRACE, "Sendheaders for %s, CharID is %i", c->MailBoxName().c_str(), CharacterID); - if(CharacterID <= 0) + _log(UCS__TRACE, "Sendheaders for %s, CharID is %i", client->MailBoxName().c_str(), characterID); + + if(characterID <= 0) return; + std::string query = StringFormat("SELECT `msgid`,`timestamp`, `from`, `subject`, `status` " + "FROM `mail` WHERE `charid`=%i", characterID); + auto results = QueryDatabase(query); + if (!results.Success()) + return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + char buffer[100]; - if (!RunQuery(query,MakeAnyLenString(&query, "select `msgid`,`timestamp`,`from`,`subject`, `status` from `mail` " - "where `charid`=%i", CharacterID),errbuf,&result)){ + int headerCountPacketLength = 0; - safe_delete_array(query); + sprintf(buffer, "%i", client->GetMailBoxNumber()); + headerCountPacketLength += (strlen(buffer) + 1); - return ; - } + sprintf(buffer, "%i", unknownField2); + headerCountPacketLength += (strlen(buffer) + 1); - safe_delete_array(query); + sprintf(buffer, "%i", unknownField3); + headerCountPacketLength += (strlen(buffer) + 1); - char Buf[100]; + sprintf(buffer, "%i", results.RowCount()); + headerCountPacketLength += (strlen(buffer) + 1); - uint32 NumRows = mysql_num_rows(result); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailHeaderCount, headerCountPacketLength); - int HeaderCountPacketLength = 0; + char *packetBuffer = (char *)outapp->pBuffer; - sprintf(Buf, "%i", c->GetMailBoxNumber()); - HeaderCountPacketLength += (strlen(Buf) + 1); - - sprintf(Buf, "%i", UnknownField2); - HeaderCountPacketLength += (strlen(Buf) + 1); - - sprintf(Buf, "%i", UnknownField3); - HeaderCountPacketLength += (strlen(Buf) + 1); - - sprintf(Buf, "%i", NumRows); - HeaderCountPacketLength += (strlen(Buf) + 1); - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailHeaderCount, HeaderCountPacketLength); - - char *PacketBuffer = (char *)outapp->pBuffer; - - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, c->GetMailBoxNumber()); - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, UnknownField2); - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, UnknownField3); - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, NumRows); + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, client->GetMailBoxNumber()); + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, unknownField2); + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, unknownField3); + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, results.RowCount()); _pkt(UCS__PACKETS, outapp); - c->QueuePacket(outapp); + client->QueuePacket(outapp); safe_delete(outapp); - int RowNum = 0; + int rowIndex = 0; + for(auto row = results.begin(); row != results.end(); ++row, ++rowIndex) { + int headerPacketLength = 0; - while((row = mysql_fetch_row(result))) { + sprintf(buffer, "%i", client->GetMailBoxNumber()); + headerPacketLength += strlen(buffer) + 1; + sprintf(buffer, "%i", unknownField2); + headerPacketLength += strlen(buffer) + 1; + sprintf(buffer, "%i", rowIndex); + headerPacketLength += strlen(buffer) + 1; + headerPacketLength += strlen(row[0]) + 1; + headerPacketLength += strlen(row[1]) + 1; + headerPacketLength += strlen(row[4]) + 1; + headerPacketLength += GetMailPrefix().length() + strlen(row[2]) + 1; + headerPacketLength += strlen(row[3]) + 1; - int HeaderPacketLength = 0; + outapp = new EQApplicationPacket(OP_MailHeader, headerPacketLength); - sprintf(Buf, "%i", c->GetMailBoxNumber()); - HeaderPacketLength = HeaderPacketLength + strlen(Buf) + 1; - sprintf(Buf, "%i", UnknownField2); - HeaderPacketLength = HeaderPacketLength + strlen(Buf) + 1; - sprintf(Buf, "%i", RowNum); - HeaderPacketLength = HeaderPacketLength + strlen(Buf) + 1; + packetBuffer = (char *)outapp->pBuffer; - HeaderPacketLength = HeaderPacketLength + strlen(row[0]) + 1; - HeaderPacketLength = HeaderPacketLength + strlen(row[1]) + 1; - HeaderPacketLength = HeaderPacketLength + strlen(row[4]) + 1; - HeaderPacketLength = HeaderPacketLength + GetMailPrefix().length() + strlen(row[2]) + 1; - HeaderPacketLength = HeaderPacketLength + strlen(row[3]) + 1; - - outapp = new EQApplicationPacket(OP_MailHeader, HeaderPacketLength); - - PacketBuffer = (char *)outapp->pBuffer; - - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, c->GetMailBoxNumber()); - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, UnknownField2); - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, RowNum); - VARSTRUCT_ENCODE_STRING(PacketBuffer, row[0]); - VARSTRUCT_ENCODE_STRING(PacketBuffer, row[1]); - VARSTRUCT_ENCODE_STRING(PacketBuffer, row[4]); - VARSTRUCT_ENCODE_STRING(PacketBuffer, GetMailPrefix().c_str()); PacketBuffer--; - VARSTRUCT_ENCODE_STRING(PacketBuffer, row[2]); - VARSTRUCT_ENCODE_STRING(PacketBuffer, row[3]); + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, client->GetMailBoxNumber()); + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, unknownField2); + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, rowIndex); + VARSTRUCT_ENCODE_STRING(packetBuffer, row[0]); + VARSTRUCT_ENCODE_STRING(packetBuffer, row[1]); + VARSTRUCT_ENCODE_STRING(packetBuffer, row[4]); + VARSTRUCT_ENCODE_STRING(packetBuffer, GetMailPrefix().c_str()); + packetBuffer--; + VARSTRUCT_ENCODE_STRING(packetBuffer, row[2]); + VARSTRUCT_ENCODE_STRING(packetBuffer, row[3]); _pkt(UCS__PACKETS, outapp); - c->QueuePacket(outapp); + client->QueuePacket(outapp); safe_delete(outapp); - - RowNum++; } - mysql_free_result(result); - } -void Database::SendBody(Client *c, int MessageNumber) { +void Database::SendBody(Client *client, int messageNumber) { - int CharacterID = FindCharacter(c->MailBoxName().c_str()); + int characterID = FindCharacter(client->MailBoxName().c_str()); - _log(UCS__TRACE, "SendBody: MsgID %i, to %s, CharID is %i", MessageNumber, c->MailBoxName().c_str(), CharacterID); + _log(UCS__TRACE, "SendBody: MsgID %i, to %s, CharID is %i", messageNumber, client->MailBoxName().c_str(), characterID); - if(CharacterID <= 0) + if(characterID <= 0) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `msgid`, `body`, `to` from `mail` " - "where `charid`=%i and `msgid`=%i", CharacterID, MessageNumber), errbuf, &result)){ - safe_delete_array(query); - - return ; - } - - safe_delete_array(query); - - if (mysql_num_rows(result) != 1) { - - mysql_free_result(result); - + std::string query = StringFormat("SELECT `msgid`, `body`, `to` FROM `mail` " + "WHERE `charid`=%i AND `msgid`=%i", characterID, messageNumber); + auto results = QueryDatabase(query); + if (!results.Success()) return; - } - row = mysql_fetch_row(result); - _log(UCS__TRACE, "Message: %i body (%i bytes)", MessageNumber, strlen(row[1])); + if (results.RowCount() != 1) + return; - int PacketLength = 12 + strlen(row[0]) + strlen(row[1]) + strlen(row[2]); + auto row = results.begin(); - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailSendBody,PacketLength); + _log(UCS__TRACE, "Message: %i body (%i bytes)", messageNumber, strlen(row[1])); - char *PacketBuffer = (char *)outapp->pBuffer; + int packetLength = 12 + strlen(row[0]) + strlen(row[1]) + strlen(row[2]); - VARSTRUCT_ENCODE_INTSTRING(PacketBuffer, c->GetMailBoxNumber()); - VARSTRUCT_ENCODE_STRING(PacketBuffer,row[0]); - VARSTRUCT_ENCODE_STRING(PacketBuffer,row[1]); - VARSTRUCT_ENCODE_STRING(PacketBuffer,"1"); - VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0); - VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x0a); - VARSTRUCT_ENCODE_STRING(PacketBuffer, "TO:"); PacketBuffer--; - VARSTRUCT_ENCODE_STRING(PacketBuffer, row[2]); PacketBuffer--; // Overwrite the null terminator - VARSTRUCT_ENCODE_TYPE(uint8, PacketBuffer, 0x0a); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MailSendBody,packetLength); - mysql_free_result(result); + char *packetBuffer = (char *)outapp->pBuffer; + + VARSTRUCT_ENCODE_INTSTRING(packetBuffer, client->GetMailBoxNumber()); + VARSTRUCT_ENCODE_STRING(packetBuffer,row[0]); + VARSTRUCT_ENCODE_STRING(packetBuffer,row[1]); + VARSTRUCT_ENCODE_STRING(packetBuffer,"1"); + VARSTRUCT_ENCODE_TYPE(uint8, packetBuffer, 0); + VARSTRUCT_ENCODE_TYPE(uint8, packetBuffer, 0x0a); + VARSTRUCT_ENCODE_STRING(packetBuffer, "TO:"); + packetBuffer--; + VARSTRUCT_ENCODE_STRING(packetBuffer, row[2]); + packetBuffer--; // Overwrite the null terminator + VARSTRUCT_ENCODE_TYPE(uint8, packetBuffer, 0x0a); _pkt(UCS__PACKETS, outapp); - c->QueuePacket(outapp); + client->QueuePacket(outapp); safe_delete(outapp); - - } -bool Database::SendMail(std::string Recipient, std::string From, std::string Subject, std::string Body, std::string RecipientsString) { +bool Database::SendMail(std::string recipient, std::string from, std::string subject, std::string body, std::string recipientsString) { - int CharacterID; + int characterID; + std::string characterName; - std::string CharacterName; + auto lastPeriod = recipient.find_last_of("."); - //printf("Database::SendMail(%s, %s, %s)\n", Recipient.c_str(), From.c_str(), Subject.c_str()); - - std::string::size_type LastPeriod = Recipient.find_last_of("."); - - if(LastPeriod == std::string::npos) - CharacterName = Recipient; + if(lastPeriod == std::string::npos) + characterName = recipient; else - CharacterName = Recipient.substr(LastPeriod+1); + characterName = recipient.substr(lastPeriod+1); - CharacterName[0] = toupper(CharacterName[0]); + characterName[0] = toupper(characterName[0]); - for(unsigned int i = 1; i < CharacterName.length(); i++) - CharacterName[i] = tolower(CharacterName[i]); + for(unsigned int i = 1; i < characterName.length(); i++) + characterName[i] = tolower(characterName[i]); - CharacterID = FindCharacter(CharacterName.c_str()); + characterID = FindCharacter(characterName.c_str()); - _log(UCS__TRACE, "SendMail: CharacterID for recipient %s is %i", CharacterName.c_str(), CharacterID); + _log(UCS__TRACE, "SendMail: CharacterID for recipient %s is %i", characterName.c_str(), characterID); - if(CharacterID <= 0) return false; + if(characterID <= 0) + return false; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + char *escSubject = new char[subject.length() * 2 + 1]; + char *escBody = new char[body.length() * 2 + 1]; - char *EscSubject = new char[Subject.length() * 2 + 1]; - char *EscBody = new char[Body.length() * 2 + 1]; + DoEscapeString(escSubject, subject.c_str(), subject.length()); + DoEscapeString(escBody, body.c_str(), body.length()); - DoEscapeString(EscSubject, Subject.c_str(), Subject.length()); - DoEscapeString(EscBody, Body.c_str(), Body.length()); - - const char *MailQuery="INSERT INTO `mail` (`charid`, `timestamp`, `from`, `subject`, `body`, `to`, `status`) " - "VALUES ('%i', %i, '%s', '%s', '%s', '%s', %i)"; - - uint32 LastMsgID; - - int Now = time(nullptr); // time returns a 64 bit int on Windows at least, which vsnprintf doesn't like. - - if(!RunQuery(query, MakeAnyLenString(&query, MailQuery, CharacterID, Now, From.c_str(), EscSubject, EscBody, - RecipientsString.c_str(), 1), errbuf, 0, 0, &LastMsgID)) { - - _log(UCS__ERROR, "SendMail: Query %s failed with error %s", query, errbuf); - - safe_delete_array(EscSubject); - safe_delete_array(EscBody); - safe_delete_array(query); + int now = time(nullptr); // time returns a 64 bit int on Windows at least, which vsnprintf doesn't like. + std::string query = StringFormat("INSERT INTO `mail` " + "(`charid`, `timestamp`, `from`, `subject`, `body`, `to`, `status`) " + "VALUES ('%i', %i, '%s', '%s', '%s', '%s', %i)", + characterID, now, from.c_str(), escSubject, escBody, + recipientsString.c_str(), 1); + safe_delete_array(escSubject); + safe_delete_array(escBody); + auto results = QueryDatabase(query); + if(!results.Success()) { + _log(UCS__ERROR, "SendMail: Query %s failed with error %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - _log(UCS__TRACE, "MessageID %i generated, from %s, to %s", LastMsgID, From.c_str(), Recipient.c_str()); + _log(UCS__TRACE, "MessageID %i generated, from %s, to %s", results.LastInsertedID(), from.c_str(), recipient.c_str()); - safe_delete_array(EscSubject); - safe_delete_array(EscBody); - safe_delete_array(query); - Client *c = CL->IsCharacterOnline(CharacterName); + Client *client = CL->IsCharacterOnline(characterName); - if(c) { - std::string FQN = GetMailPrefix() + From; - - c->SendNotification(c->GetMailBoxNumber(CharacterName), Subject, FQN, LastMsgID); + if(client) { + std::string FQN = GetMailPrefix() + from; + client->SendNotification(client->GetMailBoxNumber(characterName), subject, FQN, results.LastInsertedID()); } MailMessagesSent++; @@ -614,156 +485,122 @@ bool Database::SendMail(std::string Recipient, std::string From, std::string Sub return true; } -void Database::SetMessageStatus(int MessageNumber, int Status) { +void Database::SetMessageStatus(int messageNumber, int status) { - _log(UCS__TRACE, "SetMessageStatus %i %i", MessageNumber, Status); + _log(UCS__TRACE, "SetMessageStatus %i %i", messageNumber, status); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + if(status == 0) { + std::string query = StringFormat("DELETE FROM `mail` WHERE `msgid` = %i", messageNumber); + auto results = QueryDatabase(query); + return; + } - if(Status == 0) - RunQuery(query, MakeAnyLenString(&query, "delete from `mail` where `msgid`=%i", MessageNumber), errbuf); - else if (!RunQuery(query, MakeAnyLenString(&query, "update `mail` set `status`=%i where `msgid`=%i", Status, MessageNumber), errbuf)) { + std::string query = StringFormat("UPDATE `mail` SET `status` = %i WHERE `msgid`=%i", status, messageNumber); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(UCS__ERROR, "Error updating status %s, %s", query.c_str(), results.ErrorMessage().c_str()); - _log(UCS__ERROR, "Error updating status %s, %s", query, errbuf); - - } - - safe_delete_array(query); } void Database::ExpireMail() { _log(UCS__INIT, "Expiring mail..."); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - uint32 AffectedRows; - - if (!RunQuery(query,MakeAnyLenString(&query, "select COUNT(*) from `mail` "),errbuf,&result)){ - _log(UCS__ERROR, "Unable to get message count from database. %s %s", query, errbuf); - safe_delete_array(query); - return ; + std::string query = "SELECT COUNT(*) FROM `mail`"; + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(UCS__ERROR, "Unable to get message count from database. %s %s", query.c_str(), results.ErrorMessage().c_str()); + return; } - safe_delete_array(query); - row = mysql_fetch_row(result); + auto row = results.begin(); _log(UCS__INIT, "There are %s messages in the database.", row[0]); - mysql_free_result(result); - // Expire Trash if(RuleI(Mail, ExpireTrash) >= 0) { - if(RunQuery(query, MakeAnyLenString(&query, "delete from `mail` where `status`=4 and `timestamp` < %i", - time(nullptr) - RuleI(Mail, ExpireTrash)), errbuf, 0, &AffectedRows)) { - _log(UCS__INIT, "Expired %i trash messages.", AffectedRows); - } - else { - _log(UCS__ERROR, "Error expiring trash messages, %s %s", query, errbuf); - } - safe_delete_array(query); + query = StringFormat("DELETE FROM `mail` WHERE `status`=4 AND `timestamp` < %i", + time(nullptr) - RuleI(Mail, ExpireTrash)); + results = QueryDatabase(query); + if(results.Success()) + _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()); + } + // Expire Read if(RuleI(Mail, ExpireRead) >= 0) { - if(RunQuery(query, MakeAnyLenString(&query, "delete from `mail` where `status`=3 and `timestamp` < %i", - time(nullptr) - RuleI(Mail, ExpireRead)), errbuf, 0, &AffectedRows)) { - _log(UCS__INIT, "Expired %i read messages.", AffectedRows); - } - else { - _log(UCS__ERROR, "Error expiring read messages, %s %s", query, errbuf); - } - safe_delete_array(query); + query = StringFormat("DELETE FROM `mail` WHERE `status` = 3 AND `timestamp` < %i", + time(nullptr) - RuleI(Mail, ExpireRead)); + results = QueryDatabase(query); + if(results.Success()) + _log(UCS__INIT, "Expired %i read messages.", results.RowsAffected()); + else + _log(UCS__ERROR, "Error expiring read messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); } + // Expire Unread if(RuleI(Mail, ExpireUnread) >= 0) { - if(RunQuery(query, MakeAnyLenString(&query, "delete from `mail` where `status`=1 and `timestamp` < %i", - time(nullptr) - RuleI(Mail, ExpireUnread)), errbuf, 0, &AffectedRows)) { - _log(UCS__INIT, "Expired %i unread messages.", AffectedRows); - } - else { - _log(UCS__ERROR, "Error expiring unread messages, %s %s", query, errbuf); - } - safe_delete_array(query); + query = StringFormat("DELETE FROM `mail` WHERE `status`=1 AND `timestamp` < %i", + time(nullptr) - RuleI(Mail, ExpireUnread)); + results = QueryDatabase(query); + if(results.Success()) + _log(UCS__INIT, "Expired %i unread messages.", results.RowsAffected()); + else + _log(UCS__ERROR, "Error expiring unread messages, %s %s", query.c_str(), results.ErrorMessage().c_str()); } } -void Database::AddFriendOrIgnore(int CharID, int Type, std::string Name) { +void Database::AddFriendOrIgnore(int charID, int type, std::string name) { - const char *FriendsQuery="INSERT INTO `friends` (`charid`, `type`, `name`) VALUES ('%i', %i, '%s')"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - - if(!RunQuery(query, MakeAnyLenString(&query, FriendsQuery, CharID, Type, CapitaliseName(Name).c_str()), errbuf, 0, 0)) - _log(UCS__ERROR, "Error adding friend/ignore, query was %s : %s", query, errbuf); + std::string query = StringFormat("INSERT INTO `friends` (`charid`, `type`, `name`) " + "VALUES('%i', %i, '%s')", + charID, type, CapitaliseName(name).c_str()); + auto results = QueryDatabase(query); + if(!results.Success()) + _log(UCS__ERROR, "Error adding friend/ignore, query was %s : %s", query.c_str(), results.ErrorMessage().c_str()); else - _log(UCS__TRACE, "Wrote Friend/Ignore entry for charid %i, type %i, name %s to database.", - CharID, Type, Name.c_str()); + _log(UCS__TRACE, "Wrote Friend/Ignore entry for charid %i, type %i, name %s to database.", charID, type, name.c_str()); - - safe_delete_array(query); } -void Database::RemoveFriendOrIgnore(int CharID, int Type, std::string Name) { +void Database::RemoveFriendOrIgnore(int charID, int type, std::string name) { - const char *FriendsQuery="DELETE FROM `friends` WHERE `charid`=%i AND `type`=%i and `name`='%s'"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - if(!RunQuery(query, MakeAnyLenString(&query, FriendsQuery, CharID, Type, CapitaliseName(Name).c_str()), errbuf, 0, 0)) - _log(UCS__ERROR, "Error removing friend/ignore, query was %s", query); + std::string query = StringFormat("DELETE FROM `friends` WHERE `charid` = %i " + "AND `type` = %i AND `name` = '%s'", + charID, type, CapitaliseName(name).c_str()); + auto results = QueryDatabase(query); + if(!results.Success()) + _log(UCS__ERROR, "Error removing friend/ignore, query was %s", query.c_str()); else - _log(UCS__TRACE, "Removed Friend/Ignore entry for charid %i, type %i, name %s from database.", - CharID, Type, Name.c_str()); + _log(UCS__TRACE, "Removed Friend/Ignore entry for charid %i, type %i, name %s from database.", charID, type, name.c_str()); - - safe_delete_array(query); } -void Database::GetFriendsAndIgnore(int CharID, std::vector &Friends, std::vector &Ignorees) { +void Database::GetFriendsAndIgnore(int charID, std::vector &friends, std::vector &ignorees) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - const char *FriendsQuery="select `type`, `name` from `friends` WHERE `charid`=%i"; - - if (!RunQuery(query,MakeAnyLenString(&query, FriendsQuery, CharID),errbuf,&result)){ - - _log(UCS__ERROR, "GetFriendsAndIgnore query error %s, %s", query, errbuf); - - safe_delete_array(query); - - return ; + std::string query = StringFormat("select `type`, `name` FROM `friends` WHERE `charid`=%i", charID); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(UCS__ERROR, "GetFriendsAndIgnore query error %s, %s", query.c_str(), results.ErrorMessage().c_str()); + return; } - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - - std::string Name = row[1]; + for (auto row = results.begin(); row != results.end(); ++row) { + std::string name = row[1]; if(atoi(row[0]) == 0) { - Ignorees.push_back(Name); - _log(UCS__TRACE, "Added Ignoree from DB %s", Name.c_str()); - } - else - { - Friends.push_back(Name); - _log(UCS__TRACE, "Added Friend from DB %s", Name.c_str()); + ignorees.push_back(name); + _log(UCS__TRACE, "Added Ignoree from DB %s", name.c_str()); + continue; } + + friends.push_back(name); + _log(UCS__TRACE, "Added Friend from DB %s", name.c_str()); } - mysql_free_result(result); - - return; } diff --git a/ucs/ucs.cpp b/ucs/ucs.cpp index 322b58983..46d898f6b 100644 --- a/ucs/ucs.cpp +++ b/ucs/ucs.cpp @@ -20,7 +20,7 @@ #include "../common/debug.h" #include "clientlist.h" #include "../common/opcodemgr.h" -#include "../common/EQStreamFactory.h" +#include "../common/eq_stream_factory.h" #include "../common/rulesys.h" #include "../common/servertalk.h" #include "../common/platform.h" diff --git a/ucs/ucsconfig.h b/ucs/ucsconfig.h index 6c4fb8579..23bc82a4f 100644 --- a/ucs/ucsconfig.h +++ b/ucs/ucsconfig.h @@ -20,7 +20,7 @@ #ifndef __ucsconfig_H #define __ucsconfig_H -#include "../common/EQEmuConfig.h" +#include "../common/eqemu_config.h" class ucsconfig : public EQEmuConfig { public: diff --git a/utils/azone2/3d.hpp b/utils/deprecated/azone2/3d.hpp similarity index 100% rename from utils/azone2/3d.hpp rename to utils/deprecated/azone2/3d.hpp diff --git a/utils/azone2/3d_base.hpp b/utils/deprecated/azone2/3d_base.hpp similarity index 100% rename from utils/azone2/3d_base.hpp rename to utils/deprecated/azone2/3d_base.hpp diff --git a/utils/azone2/GLModelViewer.cpp b/utils/deprecated/azone2/GLModelViewer.cpp similarity index 100% rename from utils/azone2/GLModelViewer.cpp rename to utils/deprecated/azone2/GLModelViewer.cpp diff --git a/utils/azone2/GLModelViewer.h b/utils/deprecated/azone2/GLModelViewer.h similarity index 100% rename from utils/azone2/GLModelViewer.h rename to utils/deprecated/azone2/GLModelViewer.h diff --git a/utils/azone2/README b/utils/deprecated/azone2/README similarity index 100% rename from utils/azone2/README rename to utils/deprecated/azone2/README diff --git a/utils/azone2/archive.hpp b/utils/deprecated/azone2/archive.hpp similarity index 100% rename from utils/azone2/archive.hpp rename to utils/deprecated/azone2/archive.hpp diff --git a/utils/azone2/awater.cpp b/utils/deprecated/azone2/awater.cpp similarity index 100% rename from utils/azone2/awater.cpp rename to utils/deprecated/azone2/awater.cpp diff --git a/utils/azone2/awater.h b/utils/deprecated/azone2/awater.h similarity index 100% rename from utils/azone2/awater.h rename to utils/deprecated/azone2/awater.h diff --git a/utils/azone2/azone.cpp b/utils/deprecated/azone2/azone.cpp similarity index 100% rename from utils/azone2/azone.cpp rename to utils/deprecated/azone2/azone.cpp diff --git a/utils/azone2/azone.h b/utils/deprecated/azone2/azone.h similarity index 100% rename from utils/azone2/azone.h rename to utils/deprecated/azone2/azone.h diff --git a/utils/azone2/azone.ini b/utils/deprecated/azone2/azone.ini similarity index 100% rename from utils/azone2/azone.ini rename to utils/deprecated/azone2/azone.ini diff --git a/utils/azone2/dat.cpp b/utils/deprecated/azone2/dat.cpp similarity index 100% rename from utils/azone2/dat.cpp rename to utils/deprecated/azone2/dat.cpp diff --git a/utils/azone2/dat.hpp b/utils/deprecated/azone2/dat.hpp similarity index 100% rename from utils/azone2/dat.hpp rename to utils/deprecated/azone2/dat.hpp diff --git a/utils/azone2/file.cpp b/utils/deprecated/azone2/file.cpp similarity index 100% rename from utils/azone2/file.cpp rename to utils/deprecated/azone2/file.cpp diff --git a/utils/azone2/file.hpp b/utils/deprecated/azone2/file.hpp similarity index 100% rename from utils/azone2/file.hpp rename to utils/deprecated/azone2/file.hpp diff --git a/utils/azone2/file_loader.hpp b/utils/deprecated/azone2/file_loader.hpp similarity index 100% rename from utils/azone2/file_loader.hpp rename to utils/deprecated/azone2/file_loader.hpp diff --git a/utils/azone2/global.cpp b/utils/deprecated/azone2/global.cpp similarity index 100% rename from utils/azone2/global.cpp rename to utils/deprecated/azone2/global.cpp diff --git a/utils/azone2/global.hpp b/utils/deprecated/azone2/global.hpp similarity index 100% rename from utils/azone2/global.hpp rename to utils/deprecated/azone2/global.hpp diff --git a/utils/azone2/listobj.cpp b/utils/deprecated/azone2/listobj.cpp similarity index 100% rename from utils/azone2/listobj.cpp rename to utils/deprecated/azone2/listobj.cpp diff --git a/utils/azone2/octree.hpp b/utils/deprecated/azone2/octree.hpp similarity index 100% rename from utils/azone2/octree.hpp rename to utils/deprecated/azone2/octree.hpp diff --git a/utils/azone2/pfs.cpp b/utils/deprecated/azone2/pfs.cpp similarity index 100% rename from utils/azone2/pfs.cpp rename to utils/deprecated/azone2/pfs.cpp diff --git a/utils/azone2/pfs.hpp b/utils/deprecated/azone2/pfs.hpp similarity index 100% rename from utils/azone2/pfs.hpp rename to utils/deprecated/azone2/pfs.hpp diff --git a/utils/azone2/s3d.h b/utils/deprecated/azone2/s3d.h similarity index 100% rename from utils/azone2/s3d.h rename to utils/deprecated/azone2/s3d.h diff --git a/utils/azone2/ter.cpp b/utils/deprecated/azone2/ter.cpp similarity index 100% rename from utils/azone2/ter.cpp rename to utils/deprecated/azone2/ter.cpp diff --git a/utils/azone2/ter.hpp b/utils/deprecated/azone2/ter.hpp similarity index 100% rename from utils/azone2/ter.hpp rename to utils/deprecated/azone2/ter.hpp diff --git a/utils/azone2/types.h b/utils/deprecated/azone2/types.h similarity index 100% rename from utils/azone2/types.h rename to utils/deprecated/azone2/types.h diff --git a/utils/azone2/wld.cpp b/utils/deprecated/azone2/wld.cpp similarity index 100% rename from utils/azone2/wld.cpp rename to utils/deprecated/azone2/wld.cpp diff --git a/utils/azone2/wld.hpp b/utils/deprecated/azone2/wld.hpp similarity index 100% rename from utils/azone2/wld.hpp rename to utils/deprecated/azone2/wld.hpp diff --git a/utils/azone2/wld_structs.hpp b/utils/deprecated/azone2/wld_structs.hpp similarity index 100% rename from utils/azone2/wld_structs.hpp rename to utils/deprecated/azone2/wld_structs.hpp diff --git a/utils/azone2/zon.cpp b/utils/deprecated/azone2/zon.cpp similarity index 100% rename from utils/azone2/zon.cpp rename to utils/deprecated/azone2/zon.cpp diff --git a/utils/azone2/zon.hpp b/utils/deprecated/azone2/zon.hpp similarity index 100% rename from utils/azone2/zon.hpp rename to utils/deprecated/azone2/zon.hpp diff --git a/utils/azone2/zonv4.cpp b/utils/deprecated/azone2/zonv4.cpp similarity index 100% rename from utils/azone2/zonv4.cpp rename to utils/deprecated/azone2/zonv4.cpp diff --git a/utils/azone2/zonv4.hpp b/utils/deprecated/azone2/zonv4.hpp similarity index 100% rename from utils/azone2/zonv4.hpp rename to utils/deprecated/azone2/zonv4.hpp diff --git a/utils/pfs_list/CMakeLists.txt b/utils/deprecated/pfs_list/CMakeLists.txt similarity index 100% rename from utils/pfs_list/CMakeLists.txt rename to utils/deprecated/pfs_list/CMakeLists.txt diff --git a/utils/pfs_list/Common/CMakeLists.txt b/utils/deprecated/pfs_list/Common/CMakeLists.txt similarity index 100% rename from utils/pfs_list/Common/CMakeLists.txt rename to utils/deprecated/pfs_list/Common/CMakeLists.txt diff --git a/utils/pfs_list/Common/Include/Archive.h b/utils/deprecated/pfs_list/Common/Include/Archive.h similarity index 100% rename from utils/pfs_list/Common/Include/Archive.h rename to utils/deprecated/pfs_list/Common/Include/Archive.h diff --git a/utils/pfs_list/Common/Include/Compression.h b/utils/deprecated/pfs_list/Common/Include/Compression.h similarity index 100% rename from utils/pfs_list/Common/Include/Compression.h rename to utils/deprecated/pfs_list/Common/Include/Compression.h diff --git a/utils/pfs_list/Common/Include/PFSArchive.h b/utils/deprecated/pfs_list/Common/Include/PFSArchive.h similarity index 100% rename from utils/pfs_list/Common/Include/PFSArchive.h rename to utils/deprecated/pfs_list/Common/Include/PFSArchive.h diff --git a/utils/pfs_list/Common/Include/PFSDataStructs.h b/utils/deprecated/pfs_list/Common/Include/PFSDataStructs.h similarity index 100% rename from utils/pfs_list/Common/Include/PFSDataStructs.h rename to utils/deprecated/pfs_list/Common/Include/PFSDataStructs.h diff --git a/utils/pfs_list/Common/Source/Compression.cpp b/utils/deprecated/pfs_list/Common/Source/Compression.cpp similarity index 100% rename from utils/pfs_list/Common/Source/Compression.cpp rename to utils/deprecated/pfs_list/Common/Source/Compression.cpp diff --git a/utils/pfs_list/Common/Source/PFSArchive.cpp b/utils/deprecated/pfs_list/Common/Source/PFSArchive.cpp similarity index 100% rename from utils/pfs_list/Common/Source/PFSArchive.cpp rename to utils/deprecated/pfs_list/Common/Source/PFSArchive.cpp diff --git a/utils/pfs_list/PFSList/CMakeLists.txt b/utils/deprecated/pfs_list/PFSList/CMakeLists.txt similarity index 100% rename from utils/pfs_list/PFSList/CMakeLists.txt rename to utils/deprecated/pfs_list/PFSList/CMakeLists.txt diff --git a/utils/pfs_list/PFSList/Source/main.cpp b/utils/deprecated/pfs_list/PFSList/Source/main.cpp similarity index 100% rename from utils/pfs_list/PFSList/Source/main.cpp rename to utils/deprecated/pfs_list/PFSList/Source/main.cpp diff --git a/utils/player_profile_set/Readme.txt b/utils/deprecated/player_profile_set/Readme.txt similarity index 100% rename from utils/player_profile_set/Readme.txt rename to utils/deprecated/player_profile_set/Readme.txt diff --git a/utils/player_profile_set/bin/database.ini b/utils/deprecated/player_profile_set/bin/database.ini similarity index 100% rename from utils/player_profile_set/bin/database.ini rename to utils/deprecated/player_profile_set/bin/database.ini diff --git a/utils/player_profile_set/player_profile_set.sln b/utils/deprecated/player_profile_set/player_profile_set.sln similarity index 100% rename from utils/player_profile_set/player_profile_set.sln rename to utils/deprecated/player_profile_set/player_profile_set.sln diff --git a/utils/player_profile_set/player_profile_set/MiscFunctions.cpp b/utils/deprecated/player_profile_set/player_profile_set/MiscFunctions.cpp similarity index 99% rename from utils/player_profile_set/player_profile_set/MiscFunctions.cpp rename to utils/deprecated/player_profile_set/player_profile_set/MiscFunctions.cpp index 5af82a611..aae237489 100644 --- a/utils/player_profile_set/player_profile_set/MiscFunctions.cpp +++ b/utils/deprecated/player_profile_set/player_profile_set/MiscFunctions.cpp @@ -1,4 +1,4 @@ -#include "MiscFunctions.h" +#include "misc_functions.h" #include #include #include diff --git a/utils/player_profile_set/player_profile_set/MiscFunctions.h b/utils/deprecated/player_profile_set/player_profile_set/MiscFunctions.h similarity index 100% rename from utils/player_profile_set/player_profile_set/MiscFunctions.h rename to utils/deprecated/player_profile_set/player_profile_set/MiscFunctions.h diff --git a/utils/player_profile_set/player_profile_set/database.cpp b/utils/deprecated/player_profile_set/player_profile_set/database.cpp similarity index 100% rename from utils/player_profile_set/player_profile_set/database.cpp rename to utils/deprecated/player_profile_set/player_profile_set/database.cpp diff --git a/utils/player_profile_set/player_profile_set/database.h b/utils/deprecated/player_profile_set/player_profile_set/database.h similarity index 97% rename from utils/player_profile_set/player_profile_set/database.h rename to utils/deprecated/player_profile_set/player_profile_set/database.h index 499449634..cc82333d9 100644 --- a/utils/player_profile_set/player_profile_set/database.h +++ b/utils/deprecated/player_profile_set/player_profile_set/database.h @@ -1,7 +1,7 @@ #if !defined(_L__EQDATAB__H) #define _L__EQDATAB__H -#include "MiscFunctions.h" +#include "misc_functions.h" #ifdef WIN32 #include diff --git a/utils/player_profile_set/player_profile_set/eq_player_structs.h b/utils/deprecated/player_profile_set/player_profile_set/eq_player_structs.h similarity index 100% rename from utils/player_profile_set/player_profile_set/eq_player_structs.h rename to utils/deprecated/player_profile_set/player_profile_set/eq_player_structs.h diff --git a/utils/player_profile_set/player_profile_set/eqemu_string.h b/utils/deprecated/player_profile_set/player_profile_set/eqemu_string.h similarity index 100% rename from utils/player_profile_set/player_profile_set/eqemu_string.h rename to utils/deprecated/player_profile_set/player_profile_set/eqemu_string.h diff --git a/utils/player_profile_set/player_profile_set/ini.cpp b/utils/deprecated/player_profile_set/player_profile_set/ini.cpp similarity index 100% rename from utils/player_profile_set/player_profile_set/ini.cpp rename to utils/deprecated/player_profile_set/player_profile_set/ini.cpp diff --git a/utils/player_profile_set/player_profile_set/ini.h b/utils/deprecated/player_profile_set/player_profile_set/ini.h similarity index 100% rename from utils/player_profile_set/player_profile_set/ini.h rename to utils/deprecated/player_profile_set/player_profile_set/ini.h diff --git a/utils/player_profile_set/player_profile_set/main.cpp b/utils/deprecated/player_profile_set/player_profile_set/main.cpp similarity index 100% rename from utils/player_profile_set/player_profile_set/main.cpp rename to utils/deprecated/player_profile_set/player_profile_set/main.cpp diff --git a/utils/player_profile_set/player_profile_set/main.h b/utils/deprecated/player_profile_set/player_profile_set/main.h similarity index 100% rename from utils/player_profile_set/player_profile_set/main.h rename to utils/deprecated/player_profile_set/player_profile_set/main.h diff --git a/utils/player_profile_set/player_profile_set/player_profile_set.vcproj b/utils/deprecated/player_profile_set/player_profile_set/player_profile_set.vcproj similarity index 100% rename from utils/player_profile_set/player_profile_set/player_profile_set.vcproj rename to utils/deprecated/player_profile_set/player_profile_set/player_profile_set.vcproj diff --git a/utils/player_profile_set/player_profile_set/types.h b/utils/deprecated/player_profile_set/player_profile_set/types.h similarity index 100% rename from utils/player_profile_set/player_profile_set/types.h rename to utils/deprecated/player_profile_set/player_profile_set/types.h 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 c4e0b7955..027eac161 100644 --- a/utils/patches/patch_RoF.conf +++ b/utils/patches/patch_RoF.conf @@ -125,6 +125,7 @@ OP_GuildLeader=0x7c6f OP_GuildDelete=0x241b OP_GuildInviteAccept=0x78a5 OP_GuildDemote=0x3100 +OP_GuildPromote=0x2945 OP_GuildPublicNote=0x3c2c OP_GuildManageBanker=0x096d # Was 0x0737 OP_GuildBank=0x2ab0 # Was 0x10c3 @@ -165,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 @@ -288,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 @@ -497,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 @@ -509,8 +514,8 @@ OP_LFGAppearance=0x0000 OP_LFGResponse=0x0000 # Raid Opcodes -OP_RaidInvite=0x1bd1 -OP_RaidUpdate=0x548e +OP_RaidInvite=0x5fb2 +OP_RaidUpdate=0x1bd1 OP_RaidJoin=0x0000 # Button-push commands @@ -652,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/scripts/db_dumper.pl b/utils/scripts/db_dumper.pl new file mode 100644 index 000000000..cce98fdff --- /dev/null +++ b/utils/scripts/db_dumper.pl @@ -0,0 +1,214 @@ +#!/usr/bin/perl + +############################################################ +#::: Script: DB_Dumper.pl +#::: Purpose: Utility to easily manage database backups and compress. +#::: Export Individual DB Tables... +#::: Export specific databases... +#::: Built for both Windows and Linux +#::: Windows uses WinRar or 7-Zip for compression +#::: Linux uses tar for compression +#::: Author: Akkadius +############################################################ + +$localdrive = "C:"; #::: Where Windows and all Install Programs are... +$linesep = "---------------------------------------"; + +use POSIX qw(strftime); +my $date = strftime "%m-%d-%Y", localtime; +print "\nTodays Date: " . $date . "\n"; + +use Config; +print "Operating System is: $Config{osname}\n"; +if($Config{osname}=~/linux/i){ $OS = "Linux"; } +if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; } + +if(!$ARGV[0]){ + print "\nERROR! Need arguments\n\n"; + print "#::: Help :::#\n"; + print "######################################################\n\n"; + print "Arguments\n"; + print " loc=\"C:\\File Location\" - File path location to backup...\n"; + print " database=\"dbname\" - Manually specify databasename, default is database in eqemu_config.xml\n"; + print " tables=\"table1,table2,table3\" - Manually specify tables, default is to dump all tables from database\n"; + print " compress - Compress Database with 7-ZIP, will fallback to WinRAR depending on what is installed (Must be installed to default program dir)...\n"; + print ' Example: perl DB_Dumper.pl Loc="E:\Backups"' . "\n\n"; + print "######################################################\n"; + exit; +} + +#::: CONFIG VARIABLES - Parsed from eqemu_config.xml + +my $confile = "eqemu_config.xml"; #default +open(F, "<$confile") or die "Unable to open config: $confile - This must be in your EQEmu Server Folder with your XML config\n"; +my $indb = 0; + +while() { + s/\r//g; + if(//i) { $indb = 1; } + next unless($indb == 1); + if(/<\/database>/i) { $indb = 0; last; } + if(/(.*)<\/host>/i) { $host = $1; } + elsif(/(.*)<\/username>/i) { $user = $1; } + elsif(/(.*)<\/password>/i) { $pass = $1; } + elsif(/(.*)<\/db>/i) { $db = $1; } +} + +$Debug = 0; +print "Arguments\n" if $Debug; +$n = 0; +while($ARGV[$n]){ + print $n . ': ' . $ARGV[$n] . "\n" if $Debug; + if($ARGV[$n]=~/compress/i){ + print "Compression SET\n"; + $Compress = 1; + } + if($ARGV[$n]=~/database=/i){ + @DB_NAME = split('=', $ARGV[$n]); + print "Database is " . $DB_NAME[1] . "\n"; + $db = $DB_NAME[1]; + } + if($ARGV[$n]=~/loc=/i){ + @B_LOC = split('=', $ARGV[$n]); + print "Backup Directory: " . $B_LOC[1] . "\n"; + } + if($ARGV[$n]=~/tables=/i){ + @Tables = split('=', $ARGV[$n]); @TList = split(',', $Tables[1]); + foreach my $tables (@TList){ + $t_tables .= $tables . " "; + $t_tables_l .= $tables . "_"; + $t_tables_p .= $tables . "\n"; + } + print "Backing up tables: \n\n############################\n" . $t_tables_p . "############################\n\n"; + } + $n++; +} + +#::: Check for Backup Directory existence, if doesn't exist then create... +if (-d $B_LOC[1]) { + print "Directory currently exists... Adding files to it...\n\n"; +} +elsif($B_LOC[1] ne ""){ + print "Directory does NOT exist! Creating...\n\n"; + mkdir($B_LOC[1]) or die 'Failed to create folder, maybe created the folder manually at "' . $B_LOC[1]. '" ?'; +} +else{ + print "No save location specified... Saving to folder script is running in...\n"; +} +if($B_LOC[1] ne ""){ + if($OS eq "Windows"){ $file_app = "\\"; } + if($OS eq "Linux"){ $file_app = "/"; } +} +else { + $file_app = ""; +} + +if($t_tables ne ""){ + $tables_f_l = substr($t_tables_l, 0, 20) . '...'; + $target_file = '' . $tables_f_l . ' ' . $date . ''; + print "Performing table based backup...\n"; + #::: Backup Database... + print "Backing up Database " . $db . "... \n\n"; + $cmd = 'mysqldump -u' . $user . ' --max_allowed_packet=512M --password="' . $pass . '" ' . $db . ' ' . $t_tables . ' > "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql"'; + printcmd($cmd); + system($cmd); +} +else{ #::: Entire DB Backup + $target_file = '' . $db . ' ' . $date . ''; + #::: Backup Database... + print "Backing up Database " . $db . "... \n\n"; + $cmd = 'mysqldump -u' . $user . ' --max_allowed_packet=512M --password="' . $pass . '" ' . $db . ' > "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql"'; + printcmd($cmd); + system($cmd); +} + +#::: Get File Size +$fileloc = '' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql'; +$filesize = -s $fileloc; +if($filesize < 1000){ print "\n" . 'Error occurred... exiting...' . "\n\n"; exit; } +print "Backup DONE... DB Backup File Size '" . $filesize . "' (" . get_filesize_str($fileloc) . ")\n\n"; + +#::: WinRar Get, check compression flag +if($Compress == 1){ + if($OS eq "Windows"){ + if(-d $localdrive . "\\Program Files\\7-Zip"){ + print " ::: You have 7-Zip installed as 64 Bit...\n\n"; + $S_ZIP = $localdrive . "\\Program Files\\7-Zip"; + } + elsif(-d $localdrive . "\\Program Files (x86)\\7-Zip"){ + print " ::: You have 7-Zip installed as 32 Bit...\n\n"; + $S_ZIP = $localdrive . "\\Program Files (x86)\\7-Zip"; + } + elsif(-d $localdrive . "\\Program Files (x86)\\WinRAR"){ + print " ::: You have WinRAR installed as 32 Bit...\n\n"; + $WinRar = $localdrive . "\\Program Files (x86)\\WinRAR"; + } + elsif(-d $localdrive . "\\Program Files\\WinRAR"){ + print " ::: You have WinRAR installed as 64 Bit...\n\n"; + $WinRar = $localdrive . "\\Program Files\\WinRAR"; + } + else{ + print "No WinRAR installed... Will not compress...\n"; + } + if($S_ZIP ne ""){ + print "Compressing Database with 7-ZIP... \n\n"; + $cmd = '"' . $S_ZIP . '\\7z" a -t7z -m0=lzma -mx=9 "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.7z" "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + print "\nDeleting RAW .sql Dump... \n\n"; + $cmd = 'del "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + $final_file = $target_file . ".7z"; + } + elsif($WinRar ne ""){ + print "Compressing Database with WinRAR... \n"; + $cmd = '"' . $WinRar . '\\rar" a "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.rar" "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + print "\nDeleting RAW .sql Dump... \n\n"; + $cmd = 'del "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + $final_file = $target_file . ".rar"; + } + } + if($OS eq "Linux"){ + print "Compressing Database with Tarball... \n"; + $cmd = 'tar -zcvf "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.tar.gz" "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + print "\nDeleting RAW .sql Dump... \n\n"; + $cmd = 'rm "' . $B_LOC[1] . '' . $file_app . '' . $target_file . '.sql" '; + printcmd($cmd); + system($cmd); + $final_file = $target_file . ".tar.gz"; + } +} + +#::: Get Final File Location for display +if($B_LOC[1] ne ""){ $final_loc = $B_LOC[1] . '' . $file_app . ""; } +else{ + if($OS eq "Windows"){ + $final_loc = `echo %cd%`; + } + elsif($OS eq "Linux"){ + $final_loc = `pwd`; + } +} + +print "Final file located: " . $final_loc . "" . $final_file . "\n\n"; + +sub printcmd{ + print "--- CMD --- \n" . $_[0] . "\n" . $linesep . "\n\n"; +} + +sub get_filesize_str{ + my $file = shift(); + my $size = (stat($file))[7] || die "stat($file): $!\n"; + if ($size > 1099511627776) { return sprintf("%.2f TiB", $size / 1099511627776); } + elsif ($size > 1073741824) { return sprintf("%.2f GiB", $size / 1073741824); } + elsif ($size > 1048576) { return sprintf("%.2f MiB", $size / 1048576); } + elsif ($size > 1024) { return sprintf("%.2f KiB", $size / 1024); } + else { return "$size byte" . ($size == 1 ? "" : "s"); } +} diff --git a/utils/scripts/db_update.pl b/utils/scripts/db_update.pl new file mode 100644 index 000000000..bf92ce60f --- /dev/null +++ b/utils/scripts/db_update.pl @@ -0,0 +1,425 @@ +#!/usr/bin/perl + +########################################################### +#::: Automatic Database Upgrade Script +#::: Author: Akkadius +#::: Purpose: To upgrade databases with ease and maintain versioning +########################################################### + + +#::: If current version is less than what world is reporting, then download a new one... +$current_version = 1; +if($ARGV[0] eq "V"){ + if($ARGV[1] > $current_version){ + print "Retrieving latest database manifest...\n"; + GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/scripts/db_update.pl", "db_update.pl"); + exit; + } + else{ + print "No update necessary \n"; + } + exit; +} + +$perl_version = $^V; +$perl_version =~s/v//g; +print "Perl Version is " . $perl_version . "\n"; +if($perl_version > 5.12){ no warnings 'uninitialized'; } +no warnings; + +my $confile = "eqemu_config.xml"; #default +open(F, "<$confile") or die "Unable to open config: $confile\n"; +my $indb = 0; +while() { + s/\r//g; + if(//i) { $indb = 1; } + next unless($indb == 1); + if(/<\/database>/i) { $indb = 0; last; } + if(/(.*)<\/host>/i) { $host = $1; } + elsif(/(.*)<\/username>/i) { $user = $1; } + elsif(/(.*)<\/password>/i) { $pass = $1; } + elsif(/(.*)<\/db>/i) { $db = $1; } +} + +$console_output = +"============================================================ + EQEmu: Automatic Database Upgrade Check +============================================================ +"; + +use Config; +$console_output .= " Operating System is: $Config{osname}\n"; +if($Config{osname}=~/linux/i){ $OS = "Linux"; } +if($Config{osname}=~/Win|MS/i){ $OS = "Windows"; } + +if($OS eq "Windows"){ + $has_mysql_path = `echo %PATH%`; + if($has_mysql_path=~/MySQL|MariaDB/i){ + @mysql = split(';', $has_mysql_path); + foreach my $v (@mysql){ + if($v=~/MySQL|MariaDB/i){ + $v =~s/\n//g; + $path = trim($v) . "/mysql"; + last; + } + } + $console_output .= " (Windows) MySQL is in system path \n"; + $console_output .= " Path = " . $path . "\n"; + $console_output .= "============================================================\n"; + } +} + +#::: Linux Check +if($OS eq "Linux"){ + $path = `which mysql`; + if ($path eq "") { + $path = `which mariadb`; + } + $path =~s/\n//g; + + $console_output .= " (Linux) MySQL is in system path \n"; + $console_output .= " Path = " . $path . "\n"; + $console_output .= "============================================================\n"; +} + +#::: Path not found, error and exit +if($path eq ""){ + print "MySQL path not found, please add the path for automatic database upgrading to continue... \n\n"; + print "Exiting...\n"; + exit; +} + +#::: Create db_update working directory if not created +mkdir('db_update'); + +#::: Check if db_version table exists... +if(trim(GetMySQLResult("SHOW COLUMNS FROM db_version LIKE 'Revision'")) ne ""){ + print GetMySQLResult("DROP TABLE db_version"); + print "Old db_version table present, dropping...\n\n"; +} + +if(GetMySQLResult("SHOW TABLES LIKE 'db_version'") eq ""){ + print GetMySQLResult(" + CREATE TABLE db_version ( + version int(11) DEFAULT '0' + ) ENGINE=InnoDB DEFAULT CHARSET=latin1; + INSERT INTO db_version (version) VALUES ('1000');"); + print "Table 'db_version' does not exists.... Creating...\n\n"; +} + +if($OS eq "Windows"){ @db_version = split(': ', `world db_version`); } +if($OS eq "Linux"){ @db_version = split(': ', `./world db_version`); } + +$bin_db_ver = trim($db_version[1]); +$local_db_ver = trim(GetMySQLResult("SELECT version FROM db_version LIMIT 1")); + +#::: If ran from Linux startup script, supress output +if($bin_db_ver == $local_db_ver && $ARGV[0] eq "ran_from_start"){ + print "Database up to date...\n"; + exit; +} +else{ + print $console_output; +} + + +print " Binary Database Version: (" . $bin_db_ver . ")\n"; +print " Local Database Version: (" . $local_db_ver . ")\n\n"; + +#::: If World ran this script, and our version is up to date, continue... +if($bin_db_ver == $local_db_ver && $ARGV[0] eq "ran_from_world"){ + print " Database up to Date: Continuing World Bootup...\n"; + print "============================================================\n"; + exit; +} + +print "Retrieving latest database manifest...\n"; +GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt", "db_update/db_update_manifest.txt"); +# GetRemoteFile("https://dl.dropboxusercontent.com/u/50023467/dl/db_update_manifest.txt", "db_update/db_update_manifest.txt"); + +if($local_db_ver < $bin_db_ver && $ARGV[0] eq "ran_from_world"){ + print "You have missing database updates, type 1 or 2 to backup your database before running them as recommended...\n\n"; + #::: Display Menu + ShowMenuPrompt(); +} +else{ + #::: Most likely ran standalone + print "\n"; + ShowMenuPrompt(); +} + + +sub ShowMenuPrompt { + my %dispatch = ( + 1 => \&database_dump, + 2 => \&database_dump_compress, + 3 => \&Run_Database_Check, + 4 => \&AA_Fetch, + 0 => \&Exit, + ); + + while (1) { + { + local $| = 1; + if(!$menu_show && ($ARGV[0] eq "ran_from_world" || $ARGV[0] eq "ran_from_start")){ + $menu_show++; + next; + } + print MenuOptions(), '> '; + } + + my $choice = <>; + + $choice =~ s/\A\s+//; + $choice =~ s/\s+\z//; + + if (defined(my $handler = $dispatch{$choice})) { + my $result = $handler->(); + unless (defined $result) { + exit 0; + } + } + else { + if($ARGV[0] ne "ran_from_world"){ + # warn "\n\nInvalid selection\n\n"; + } + } + } +} + +sub MenuOptions { + if(@total_updates){ + $option[3] = "Run pending REQUIRED updates... (" . scalar (@total_updates) . ")"; + } + else{ + $option[3] = "Check for pending REQUIRED Database updates + Stages updates for automatic upgrade..."; + } + +return <new; + $ua->timeout(10); + $ua->env_proxy; + my $response = $ua->get($URL); + + if ($response->is_success){ + open (FILE, '> ' . $Dest_File . ''); + print FILE $response->decoded_content; + close (FILE); + print " URL: " . $URL . "\n"; + print " Saved: " . $Dest_File . " \n"; + } + else { + print "Error, no connection to the internet...\n\n"; + die $response->status_line; + } + } + if($OS eq "Linux"){ + #::: wget -O db_update/db_update_manifest.txt https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/db_update_manifest.txt + $wget = `wget --quiet -O $Dest_File $URL`; + print " URL: " . $URL . "\n"; + print " Saved: " . $Dest_File . " \n"; + if($wget=~/unable to resolve/i){ + print "Error, no connection to the internet...\n\n"; + die; + } + } +} + +#::: Trim Whitespaces +sub trim { + my $string = $_[0]; + $string =~ s/^\s+//; + $string =~ s/\s+$//; + return $string; +} + +#::: Fetch Latest PEQ AA's +sub AA_Fetch{ + print "Pulling down PEQ AA Tables...\n"; + GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/peq_aa_tables.sql", "db_update/peq_aa_tables.sql"); + print "\n\nInstalling AA Tables...\n"; + print GetMySQLResultFromFile("db_update/peq_aa_tables.sql"); + print "\nDone...\n\n"; +} + +#::: Responsible for Database Upgrade Routines +sub Run_Database_Check{ + #::: Run 2 - Running pending updates... + if(defined(@total_updates)){ + @total_updates = sort @total_updates; + foreach my $val (@total_updates){ + $file_name = trim($m_d{$val}[1]); + print "Running Update: " . $val . " - " . $file_name . "\n"; + print GetMySQLResultFromFile("db_update/$file_name"); + print GetMySQLResult("UPDATE db_version SET version = $val WHERE version < $val"); + } + } + + #::: Run 1 - Initial checking of needed updates... + print "Reading manifest...\n\n"; + use Data::Dumper; + open (FILE, "db_update/db_update_manifest.txt"); + while () { + chomp; + $o = $_; + if($o=~/#/i){ next; } + @manifest = split('\|', $o); + $m_d{$manifest[0]} = [@manifest]; + } + + @total_updates = (); + + #::: Iterate through Manifest backwards from binary version down to local version... + for($i = $bin_db_ver; $i > 1000; $i--){ + if(!defined($m_d{$i}[0])){ next; } + + $file_name = trim($m_d{$i}[1]); + $query_check = trim($m_d{$i}[2]); + $match_type = trim($m_d{$i}[3]); + $match_text = trim($m_d{$i}[4]); + + #::: Match type update + if($match_type eq "contains"){ + if(trim(GetMySQLResult($query_check))=~/$match_text/i){ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + else{ + print "DB up to date with: " . $i . " - '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + if($match_type eq "missing"){ + if(GetMySQLResult($query_check)=~/$match_text/i){ + print "DB up to date with: " . $i . " - '" . $file_name . "' \n"; + next; + } + else{ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + print_match_debug(); + print_break(); + } + if($match_type eq "empty"){ + if(GetMySQLResult($query_check) eq ""){ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + else{ + print "DB up to date with: " . $i . " - '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + if($match_type eq "not_empty"){ + if(GetMySQLResult($query_check) ne ""){ + print "Missing DB Update " . $i . " '" . $file_name . "' \n"; + FetchMissingUpdate($i, $file_name); + push(@total_updates, $i); + } + else{ + print "DB up to date with: " . $i . " - '" . $file_name . "' \n"; + } + print_match_debug(); + print_break(); + } + } + print "\n"; + + if(scalar (@total_updates) == 0){ + print "No updates need to be run...\n"; + print "Setting Database to Binary Version (" . $bin_db_ver . ") if not already...\n\n"; + GetMySQLResult("UPDATE db_version SET version = $bin_db_ver"); + } +} + +sub FetchMissingUpdate{ + $db_update = $_[0]; + $update_file = $_[1]; + if($db_update >= 9000){ + GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/git/required/" . $update_file, "db_update/" . $update_file . ""); + } + elsif($db_update >= 5000 && $db_update <= 9000){ + GetRemoteFile("https://raw.githubusercontent.com/EQEmu/Server/master/utils/sql/svn/" . $update_file, "db_update/" . $update_file . ""); + } +} + +sub print_match_debug{ + if(!$debug){ return; } + print " Match Type: '" . $match_type . "'\n"; + print " Match Text: '" . $match_text . "'\n"; + print " Query Check: '" . $query_check . "'\n"; + print " Result: '" . trim(GetMySQLResult($query_check)) . "'\n"; +} +sub print_break{ + if(!$debug){ return; } + print "\n==============================================\n"; +} diff --git a/utils/scripts/opcode_handlers.py b/utils/scripts/opcode_handlers.py index 6cad4ade0..ce0e955a3 100644 --- a/utils/scripts/opcode_handlers.py +++ b/utils/scripts/opcode_handlers.py @@ -23,8 +23,8 @@ VERBOSE = False # messaging: {False - minimal, True - robust} base_path = os.getcwd()[:-14] # '/utils/scripts' base_path = base_path.replace('\\', '/') -client_list = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF', 'RoF2', 'ClientTest'] -server_list = ['Login', 'World', 'Zone', 'UCS', 'ServerTest'] +client_list = ['6.2', 'Titanium', 'SoF', 'SoD', 'Underfoot', 'RoF', 'RoF2'] +server_list = ['Login', 'World', 'Zone', 'UCS'] client_opcodes = {} # x[key='Client'][key='OP_CodeName'](value='0x####') server_opcodes = {} # x[key='OP_CodeName'](value=) - opcodes apply to all servers @@ -248,7 +248,7 @@ def loadclientopcodes(): for client in client_list: try: - short_name = '/patch_{0}.conf'.format(client) + short_name = '/patch_{0}.conf'.format(client).lower() file_name = '{0}/utils/patches{1}'.format( base_path, @@ -455,9 +455,9 @@ def loadclienttranslators(): for client in client_list: try: if client == '6.2': - short_name = '/Client62_ops.h' + short_name = '/client62_ops.h' else: - short_name = '/{0}_ops.h'.format(client) + short_name = '/{0}_ops.h'.format(client).lower() file_name = '{0}/common/patches{1}'.format( base_path, @@ -734,15 +734,15 @@ def discoverserverhandlers(): locations[server] = [] if 'Login' in locations: - locations['Login'].append('/loginserver/Client.cpp') - locations['Login'].append('/loginserver/ServerManager.cpp') - locations['Login'].append('/loginserver/WorldServer.cpp') + locations['Login'].append('/loginserver/client.cpp') + locations['Login'].append('/loginserver/server_manager.cpp') + locations['Login'].append('/loginserver/world_server.cpp') if 'World' in locations: locations['World'].append('/world/client.cpp') if 'Zone' in locations: - locations['Zone'].append('/zone/AA.cpp') + locations['Zone'].append('/zone/aa.cpp') locations['Zone'].append('/zone/attack.cpp') locations['Zone'].append('/zone/bot.cpp') locations['Zone'].append('/zone/client.cpp') @@ -762,8 +762,8 @@ def discoverserverhandlers(): locations['Zone'].append('/zone/loottables.cpp') locations['Zone'].append('/zone/merc.cpp') locations['Zone'].append('/zone/mob.cpp') - locations['Zone'].append('/zone/MobAI.cpp') - locations['Zone'].append('/zone/Object.cpp') + locations['Zone'].append('/zone/mob_ai.cpp') + locations['Zone'].append('/zone/object.cpp') locations['Zone'].append('/zone/pathing.cpp') locations['Zone'].append('/zone/petitions.cpp') locations['Zone'].append('/zone/questmgr.cpp') diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt new file mode 100644 index 000000000..8291465a4 --- /dev/null +++ b/utils/sql/db_update_manifest.txt @@ -0,0 +1,334 @@ +5001|1_task_system.sql +# 5002|2_optional_maxclients.sql +# 5003|14_optional_merchantlist.sql +5004|35_task_stepped.sql +5005|42_task_min_maxlevel.sql +5006|55_zone_shutdowndeleay.sql +# 5007|68_optional_character_maxexplevel.sql +# 5008|103_optional_chat_rules.sql +5009|104_traps.sql +# 5010|106_optional_proc_rules.sql +5011|120_damageshieldtypes.sql +5012|125_aggrozone.sql +# 5013|127_optional_spell_rules.sql +# 5014|129_optional_shared_plat_rule.sql +# 5015|131_optional_combat_rules.sql +5016|133_task_repeatable.sql +5017|142_deathpeace_and_lifetap_aas.sql +# 5018|158_optional_death_exp_loss.sql +5019|176_melody.sql +5020|189_character_.sql +5021|196_trader.sql +5022|210_undyeme.sql +5023|222_buyer.sql +5024|226_account_limiting.sql +5025|230_spells_table.sql +5026|235_horses_table.sql +5027|243_spawn_timers.sql +5028|247_mail.sql +5029|249_chatchannels.sql +5030|250_bot_spell_update.sql +# 5031|250_optional_bot_spell_update.sql +# 5032|285_optional_bot_spell_update.sql +5033|292_augslots.sql +5034|294_merchant_logging.sql +5035|304_faction_list.sql +5036|326_aas.sql +5037|328_bot_management.sql +# 5038|328_optional_bot_management.sql +5039|340_gm_ips.sql +5040|356_combat.sql +5041|360_peqzone.sql +5042|364_ranged_dist_rule.sql +5043|386_bot_save_raid.sql +# 5044|434_optional_rest_state_rules.sql +5045|447_sof_startzone_rule.sql +5046|463_altadv_vars.sql +5047|475_aa_actions.sql +5048|500_spawn2_optimization.sql +5049|503_bugs.sql +5050|518_drakkin_npc_type_features.sql +5051|524_rule_values_notes.sql +5052|527_npc_armor_tint.sql +5053|553_saylink_table.sql +5054|564_nokeyring.sql +5055|600_group_leadership.sql +5056|612_instance_changes.sql +5057|615_adventure_assassination.sql +5058|619_Adventure_Recruiter_Flavor.sql +5059|621_LDoNTraps.sql +5060|633_ucs.sql +5061|634_TrapTemplateDefaultValue.sql +5062|643_BotsTable.sql +5063|646_archery_penalty_rule.sql +5064|665_heroic_resists.sql +5065|667_titles.sql +5066|687_aa_table_changes.sql +5067|699_peqzone_rule.sql +5068|702_aashieldblock_tint_table.sql +5069|703_peqzone_rule.sql +5070|704_rules.sql +5071|710_tint_set_naming.sql +5072|721_pathing_rules.sql +5073|730_smart_delay_moving.sql +5074|731_rule_assist_notarget_self.sql +5075|732_sacrifice_rules.sql +5076|745_slow_mitigation.sql +5077|754_archery_base_damage_rule.sql +5078|755_sof_altadv_vars_updates.sql +5079|773_monk_rules.sql +# 5080|853_optional_rule_aaexp.sql +# 5081|858_optional_rule_ip_limit_by_status.sql +# 5082|892_optional_bots_table_mod.sql +# 5083|893_optional_bots_table_mod.sql +5084|898_npc_maxlevel_scalerate.sql +# 5085|902_optional_rule_snareflee.sql +5086|923_spawn2_enabled.sql +5087|962_hot_zone.sql +5088|964_reports.sql +5089|971_veteran_rewards.sql +5090|977_raid_npc_private_corpses.sql +5091|979_unique_spawn_by_name.sql +5092|980_account_ip.sql +5093|1022_botadventuring.sql +5094|1027_botactives.sql +5095|1030_botzoningsupport.sql +5096|1036_botbuffs.sql +5097|1038_botpetstatepersists.sql +5098|1038_grouptablesuniquecolumndefinitions.sql +5099|1039_botguilds.sql +5100|1040_DeprecatedBotRaidsSystems.sql +5101|1057_titles.sql +5102|1077_botgroups.sql +5103|1136_spell_globals.sql +# 5104|1144_optional_rule_return_nodrop.sql +5105|1195_account_suspendeduntil.sql +5106|1259_npc_skill_types.sql +5107|1280_bot_augs.sql +# 5108|1290_optional_exp_loss_rule.sql +5109|1293_guild_bank.sql +5110|1379_loginserver_trusted_server.sql +5111|1392_recipe_learning.sql +# 5112|1394_optional_rule_sod_hp_mana_end.sql +5113|1404_faction_list.sql +# 5114|1410_optional_sod_aas_ht_and_loh.sql +5115|1436_login_server_table_fix.sql +5116|1446_allowrest_optional.sql +5117|1446_allowrest_required.sql +5118|1450_cvs.sql +5119|1451_guilds.sql +5120|1498_instance_adventure.sql +5121|1510_global_instances.sql +5122|1511_map_path_loading.sql +5123|1513_zone_points.sql +5124|1519_zone_primary_key_id.sql +5125|1542_items_table_cleanup.sql +5126|1548_nimbuseffect_required.sql +5127|1562_instanced_spawnconditions.sql +5128|1586_waypoints_optional.sql +5129|1610_tradeskill_required.sql +5130|1618_zone.sql +# 5131|1625_optional_rule_class_race_exp_bonus.sql +# 5132|1672_optional_rules_respawn_window.sql +# 5133|1679_optional_rules_blocked_buffs.sql +5134|1696_modify_zone_and_object_tables.sql +5135|1711_account_restricted_aa.sql +# 5136|1717_optional_rule_bash_stun_chance.sql +# 5137|1718_optional_rules_mod3s.sql +# 5138|1719_optional_triggerOnCastAAs.sql +# 5139|1720_optional_sql_AAs.sql +5140|1720_required_sql_AA_effects_update.sql +# 5141|1721_optional_sql_drakkin_breath_update.sql +5142|1721_required_sql_altadv_vars_update.sql +# 5143|1723_optional_sql_new_stats_window_rule.sql +5144|1723_required_sql_corruption.sql +# 5145|1736_optional_sql_feral_swipe.sql +5146|1737_required_sql_rule_and_aa_update.sql +# 5147|1746_optional_sql_bot_manaregen.sql +# 5148|1747_optional_HoT_zone_and_zonepoints.sql +# 5149|1750_optional_sql_reflect_rule.sql +# 5150|1753_optional_haste_cap_rule.sql +5151|1753_required_sql_healing_adept_aa.sql +5152|1754_required_sql_healing_adept_aa_fix.sql +5153|1755_required_sql_fear_resist_aas.sql +# 5154|1784_optional_corpsedrag_rules.sql +5155|1786_required_update_to_aas.sql +5156|1790_required_aa_required_level_cost.sql +5157|1793_resist_adjust.sql +# 5158|1799_optional_rest_regen_endurance_rule.sql +5159|1802_required_doppelganger.sql +5160|1803_required_tasks_xpreward_signed.sql +5161|1804_required_ae_melee_updates.sql +# 5162|1809_optional_rules.sql +5163|1813_required_doppelganger_npcid_change.sql +# 5164|1817_optional_npc_archery_bonus_rule.sql +# 5165|1823_optional_delay_death.sql +5166|1847_required_doors_dest_zone_size_32.sql +# 5167|1859_optional_item_casts_use_focus_rule.sql +# 5168|1884_optional_bot_spells_update.sql +# 5169|1885_optional_rules_fv_pvp_expansions.sql +# 5170|1889_optional_skill_cap_rule.sql +5171|1908_required_npc_types_definitions.sql +# 5172|1926_optional_stat_cap.sql +5173|1944_spawn2.sql +5174|1946_doors.sql +# 5175|1960_optional_console_timeout_rule.sql +# 5176|1962_optional_guild_creation_window_rules.sql +# 5177|1963_optional_rule_live_like_focuses.sql +# 5178|1968_optional_enrage_rules.sql +# 5179|1972_optional_extradmg_item_cap.sql +5180|1974_required_bot_spells_update.sql +5181|1977_underwater.sql +# 5182|1998_optional_intoxication_and_looting_rules.sql +5183|2004_charges_alt_currency.sql +# 5184|2015_optional_specialization_training_rule.sql +# 5185|2016_optional_rule_bot_aa_expansion.sql +# 5186|2023_optional_mysqlcli.sql +# 5187|2024_optional_update_crystals.sql +5188|2024_required_update.sql +5189|2057_required_discovered_items.sql +# 5190|2058_optional_rule_discovered_items.sql +5191|2062_required_version_changes.sql +5192|2069_required_pets.sql +5193|2079_player_speech.sql +5194|2087_required_bots_hp_and_mana_and_spell_updates.sql +5195|2098_required_zonepoint_version_changes.sql +5196|2099_required_discovered_items_account_status.sql +5197|2104_required_group_roles.sql +5198|2107_required_bot_stances.sql +5199|2129_required_lfguild.sql +5200|2133_required_faction_loot_despawn.sql +5201|2136_extended_targets.sql +5202|2142_emotes.sql +# 5203|2154_optional_rule_spell_procs_resists_falloff.sql +# 5204|2156_optional_charm_break_rule.sql +# 5205|2159_optional_defensiveproc_rules.sql +5206|2164_require_bots_bottimers.sql +# 5207|2171_optional_SpecialAttackACBonus_rule.sql +# 5208|2176_optional_aa_expansion_SOF_fix.sql +# 5209|2176_optional_FrenzyBonus_rule.sql +5210|2176_required_aa_updates.sql +5211|2178_required_aa_updates.sql +# 5212|2183_optional_bot_xp_rule.sql +# 5213|2185_optional_NPCFlurryChacne_rule +# 5214|2185_optional_NPCFlurryChacne_rule.sql +# 5215|2185_optional_NPCFlurryChance_rule.sql +5216|2185_required_aa_updates +5217|2185_required_aa_updates.sql +# 5218|2188_optional_miscspelleffect_rules +# 5219|2188_optional_miscspelleffect_rules.sql +5220|2188_required_aa_updates +5221|2188_required_aa_updates.sql +# 5222|2189_optional_taunt_rules +# 5223|2189_optional_taunt_rules.sql +5224|2195_required_sharedplatupdates.sql +# 5225|2208_optional_aa_stacking_rule.sql +# 5226|2208_optional_EnableSoulAbrasionAA.sql +5227|2208_required_aa_updates.sql +# 5228|2209_optional_additive_bonus_rule.sql +5229|2213_loot_changes.sql +5230|2214_faction_list_mod.sql +5231|2215_required_aa_updates.sql +# 5232|2243_optional_char_max_level_rule.sql +5233|2260_probability.sql +5234|2262_required_pet_discipline_update.sql +5235|2264_required_aa_updates.sql +5236|2268_QueryServ.sql +5237|2268_required_updates.sql +# 5238|2274_optional_rule_iplimitdisconnectall.sql +# 5239|2278_optional_rule_targetableswarmpet.sql +# 5240|2280_optional_rule_targetableswarmpet-rename.sql +5241|2283_required_npc_changes.sql +5242|2299_required_inspectmessage_fields.sql +# 5243|2300_optional_loot_changes.sql +5244|2304_QueryServ.sql +5245|2340_required_maxbuffslotspet.sql +5246|2361_QueryServ.sql +5247|2361_required_qs_rule_values.sql +5248|2370_required_aa_updates.sql +5249|2376_required_aa_updates.sql +# 5250|2380_optional_merc_data.sql +# 5251|2380_optional_merc_merchant_npctypes_update.sql +# 5252|2380_optional_merc_rules.sql +5253|2383_required_group_ismerc.sql +# 5254|2428_optional_levelbasedexpmods.sql +# 5255|2448_optional_stun_proc_aggro_rule.sql +5256|2471_required_aa_updates.sql +5257|2482_required_start_zones.sql +5258|2504_required_aa_updates.sql +8000|mercs.sql|SHOW TABLES LIKE 'merc_stats'|empty| +9000|2013_02_18_Merc_Rules_and_Tables.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE '%Mercs:ResurrectRadius%'|empty| +9001|2013_02_25_Impr_HT_LT.sql|SHOW TABLES LIKE 'merc_inventory'|empty| +9002|2013_03_1_Merc_Rules_and_Equipment.sql|SHOW TABLES LIKE 'merc_inventory'|empty| +# 9003|2013_03_23_Escape_FadingMemories.sql +# 9004|2013_04_04_NaturesBounty.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '1230' AND `slot` = '1' AND `effectid` = '313' AND `base1` = '15' AND `base2` = '0'|empty| +9005|2013_04_08_Salvage.sql|SHOW COLUMNS FROM `tradeskill_recipe_entries` LIKE 'salvagecount'|empty| +9006|2013_05_05_Account_Flags.sql|SHOW TABLES LIKE 'account_flags'|empty| +9007|2013_05_05_Item_Tick.sql|SHOW TABLES LIKE 'item_tick'|empty| +9008|2013_07_11_NPC_Special_Abilities.sql|SHOW COLUMNS FROM `npc_types` LIKE 'special_abilities'|empty| +9009|2013_10_12_Merc_Special_Abilities.sql|SHOW COLUMNS FROM `merc_stats` LIKE 'special_abilities'|empty| +# 9010|2013_10_12_Merc_vwMercNpcTypes.sql +9011|2013_10_31_Recipe_disabling.sql|SHOW COLUMNS FROM `tradeskill_recipe` LIKE 'enabled'|empty| +9012|2013_11_07_BaseData.sql|SHOW TABLES LIKE 'base_data'|empty| +# 9013|2013_11_13_Instrument_Singing_Mastery.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '213' AND `slot` = '1' AND `effectid` = '260' AND `base1` = '2' AND `base2` = '23'|empty| +9014|2013_11_18_AssistRadius.sql|SHOW COLUMNS FROM `npc_types` LIKE 'assistradius'|empty| +9015|2013_12_26_MerchantList_Class_Required.sql|SHOW COLUMNS FROM `merchantlist` LIKE 'classes_required'|empty| +# 9016|2014_01_04_SongModCapAAs.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '571' AND `slot` = '1' AND `effectid` = '261'|empty| +9017|2014_01_08_SpellsNewAdditions.sql|SHOW COLUMNS FROM `spells_new` LIKE 'persistdeath'|empty| +9018|2014_01_09_PreservePetSize.sql|SHOW COLUMNS FROM `character_pet_info` LIKE 'size'|empty| +# 9019|2014_01_20_MezMastery.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '781' AND `slot` = '1' AND `effectid` = '287'|empty| +9020|2014_01_20_Not_Extendable.sql|SHOW COLUMNS FROM `spells_new` LIKE 'not_extendable'|empty| +# 9021|2014_01_20_SpellCastingReinforcement.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '86' AND `slot` = '1' AND `effectid` = '128'|empty| +9022|2014_01_20_Weather.sql|SHOW COLUMNS FROM `zone` LIKE 'rain_chance1'|empty| +# 9023|2014_01_27_CritcalMendAA.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '230' AND `slot` = '1' AND `effectid` = '275'|empty +# 9024|2014_02_02_SpellCriticalsAA.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '4755' AND `slot` = '1' AND `effectid` = '294'|empty +9025|2014_02_13_Rename_instance_lockout_tables.sql|SHOW TABLES LIKE 'instance_list'|empty| +9026|2014_02_13_spells_new_update.sql|SHOW COLUMNS FROM `spells_new` LIKE 'ConeStartAngle'|empty| +9027|2014_02_20_buff_update.sql|SHOW COLUMNS FROM `character_buffs` LIKE 'caston_y'|empty| +9028|2014_02_26_roambox_update.sql|SHOW COLUMNS FROM `spawngroup` LIKE 'mindelay'|empty| +# 9029|2014_02_26_virulentvenomAA.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '888' AND `slot` = '1' AND `effectid` = '250'|empty| +9030|2014_04_04_PhysicalResist.sql|SHOW COLUMNS FROM `npc_types` LIKE 'PhR'|empty| +9031|2014_04_10_No_Target_With_Hotkey.sql|SHOW COLUMNS FROM `npc_types` LIKE 'no_target_hotkey'|empty| +9032|2014_04_12_SlowMitigation.sql|SHOW COLUMNS FROM `npc_types` LIKE 'slow_mitigation'|contains|float +9034|2014_04_25_spawn_events.sql|SHOW COLUMNS FROM `spawn_events` LIKE 'strict'|empty| +9035|2014_04_27_AISpellEffects.sql|SHOW COLUMNS FROM `npc_types` LIKE 'npc_spells_effects_id'|empty| +9036|2014_05_04_SlowMitigationFix.sql|SHOW COLUMNS FROM `npc_types` LIKE 'slow_mitigation'|contains|float +# 9038|2014_06_25_AA_Updates.sql|SELECT * FROM `altadv_vars` WHERE `skill_id` = '1604'|empty +# 9039|2014_07_04_AA_Updates.sql|SELECT * FROM `aa_effects` WHERE `aaid` = '158' AND `slot` = '1' AND `effectid` = '238'|empty +9040|2014_07_10_npc_spells.sql|SHOW COLUMNS FROM `npc_spells` LIKE 'engaged_no_sp_recast_min'|empty| +9041|2014_08_02_spells_new.sql|SHOW COLUMNS FROM `spells_new` LIKE 'viral_range'|empty| +9042|2014_08_12_NPC_raid_targets.sql|SHOW COLUMNS FROM `npc_types` LIKE 'raid_target'|empty| +9043|2014_08_18_spells_new_update.sql|SHOW COLUMNS FROM `spells_new` LIKE 'viral_targets'|empty| +9044|2014_08_20_merchantlist_probability.sql|SHOW COLUMNS FROM `merchantlist` LIKE 'probability'|empty| +9045|2014_08_23_Complete_QueryServ_Table_Structures.sql|SHOW TABLES LIKE 'qs_player_aa_rate_hourly'|empty| +9046|2014_08_23_player_events_and_player_aa_rate_hourly.sql|SHOW TABLES LIKE 'qs_player_events'|empty| +9048|2014_09_09_attack_delay.sql|SHOW COLUMNS FROM `npc_types` LIKE 'attack_delay'|empty| +9050|2014_09_20_ban_messages.sql|SHOW COLUMNS FROM `account` LIKE 'ban_reason'|empty| +9051|2014_10_11_RaidMOTD.sql|SHOW COLUMNS FROM `raid_details` LIKE 'motd'|empty| +9052|2014_10_13_RaidLeadership.sql|SHOW TABLES LIKE 'raid_leaders'|empty| +9053|2014_10_18_group_mentor.sql|SHOW COLUMNS FROM `group_leaders` LIKE 'mentoree'|empty| +9054|2014_10_19_raid_group_mentor.sql|SHOW COLUMNS FROM `raid_leaders` LIKE 'mentoree'|empty| +9055|2014_10_30_special_abilities_null.sql|SHOW COLUMNS FROM `npc_types` LIKE 'special_abilities'|contains|NO +9056|2014_11_08_RaidMembers.sql|SHOW COLUMNS FROM `raid_members` LIKE 'groupid'|missing|unsigned +9057|2014_11_13_spells_new_updates.sql|SHOW COLUMNS FROM `spells_new` LIKE 'disallow_sit'|empty| +9058|2014_11_26_InventoryTableUpdate.sql|SHOW COLUMNS FROM `inventory` LIKE 'ornamenticon'|empty| +9059|2014_11_30_mercs_table_update.sql|SHOW COLUMNS FROM `mercs` LIKE 'MercSize'|empty| + +# Upgrade conditions: +# This won't be needed after this system is implemented, but it is used database that are not +# yet using the versioning system to figure out where the database is schema wise to determine +# which updates are necessary to run +# +# Example: Version|Filename.sql|Query_to_Check_Condition_For_Needed_Update|match type|text to match +# 0 = Database Version +# 1 = Filename.sql +# 2 = Query_to_Check_Condition_For_Needed_Update +# 3 = Match Type - If condition from match type to Value 4 is true, update will flag for needing to be ran +# contains = If query results contains text from 4th value +# match = If query results matches text from 4th value +# missing = If query result is missing text from 4th value +# empty = If the query results in no results +# not_empty = If the query is not empty +# 4 = Text to match +# +# \ No newline at end of file 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_08_23_Complete_QueryServ_Rules_Disabled.sql b/utils/sql/git/optional/2014_08_23_Complete_QueryServ_Rules_Disabled.sql new file mode 100644 index 000000000..65206ce4c --- /dev/null +++ b/utils/sql/git/optional/2014_08_23_Complete_QueryServ_Rules_Disabled.sql @@ -0,0 +1,45 @@ +-- Disable Player Logging for All -- +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogPCCoordinates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogNPCKills', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTrades', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMerchantTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeletes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogHandins', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoves', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogChat', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogKeyringAddition', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAAPurchases', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogIssuedCommandes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoneyTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTradeSkillEvents', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogPCCoordinates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDropItem', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMerchantTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeletes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogHandins', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoves', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogNPCKills', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTrades', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogQGlobalUpdate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTaskUpdates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeaths', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogZone', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogConnectDisconnect', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogLevels', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAARate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogChat', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDropItem', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogZone', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeaths', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogLevels', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogEXPRate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAARate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogQGlobalUpdate', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTaskUpdates', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogKeyringAddition', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAAPurchases', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTradeSkillEvents', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogIssuedCommandes', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoneyTransactions', 'false', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'false', ''); diff --git a/utils/sql/git/optional/2014_08_23_Complete_QueryServ_Rules_Enabled.sql b/utils/sql/git/optional/2014_08_23_Complete_QueryServ_Rules_Enabled.sql new file mode 100644 index 000000000..9426acc8f --- /dev/null +++ b/utils/sql/git/optional/2014_08_23_Complete_QueryServ_Rules_Enabled.sql @@ -0,0 +1,45 @@ +-- Enable Player Logging for All -- +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogPCCoordinates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogNPCKills', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTrades', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMerchantTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeletes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogHandins', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoves', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogChat', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogKeyringAddition', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAAPurchases', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogIssuedCommandes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoneyTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTradeSkillEvents', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogPCCoordinates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDropItem', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMerchantTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeletes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogHandins', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogMoves', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogNPCKills', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTrades', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogQGlobalUpdate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogTaskUpdates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogDeaths', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogZone', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogConnectDisconnect', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogLevels', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (9, 'QueryServ:PlayerLogAARate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogChat', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDropItem', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogZone', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogDeaths', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogLevels', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogEXPRate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAARate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogQGlobalUpdate', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTaskUpdates', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogKeyringAddition', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAAPurchases', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogTradeSkillEvents', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogIssuedCommandes', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogMoneyTransactions', 'true', ''); +REPLACE INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (0, 'QueryServ:PlayerLogAlternateCurrencyTransactions', 'true', ''); 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/optional/2014_10_24_Quick_Draw.sql b/utils/sql/git/optional/2014_10_24_Quick_Draw.sql new file mode 100644 index 000000000..1dafd19a3 --- /dev/null +++ b/utils/sql/git/optional/2014_10_24_Quick_Draw.sql @@ -0,0 +1,4 @@ +INSERT INTO altadv_vars SET skill_id="4698", name="Quick Draw", cost="5", max_level="1", hotkey_sid="4294967295", hotkey_sid2="4294967295", title_sid="4698", desc_sid="4698", type="1", spellid="4294967295", prereq_skill="0", prereq_minpoints="0", spell_type="0", spell_refresh="0", class_type="51", cost_inc="0", aa_expansion="12", special_category="4294967295", sof_type="1", sof_cost_inc="0", sof_max_level="2", sof_next_skill="4698", clientver="4", account_time_required="0", sof_current_level="0", sof_next_id="6545", level_inc="0", classes="65534", berserker="1"; +INSERT INTO aa_effects SET aaid=4698, slot=1, effectid=362, base1=1, base2=0; +INSERT INTO altadv_vars SET skill_id="6545", name="Quick Draw II", cost="5", max_level="1", hotkey_sid="4294967295", hotkey_sid2="4294967295", title_sid="4698", desc_sid="4698", type="1", spellid="4294967295", prereq_skill="4698", prereq_minpoints="1", spell_type="0", spell_refresh="0", class_type="56", cost_inc="0", aa_expansion="14", special_category="4294967295", sof_type="1", sof_cost_inc="0", sof_max_level="2", sof_next_skill="4698", clientver="5", account_time_required="0", sof_current_level="1", sof_next_id="0", level_inc="0", classes="65534", berserker="1"; +INSERT INTO aa_effects SET aaid=6545, slot=1, effectid=362, base1=1, base2=0; diff --git a/utils/sql/git/optional/2014_11_09_LiveFactionMessages.sql b/utils/sql/git/optional/2014_11_09_LiveFactionMessages.sql new file mode 100644 index 000000000..de407d283 --- /dev/null +++ b/utils/sql/git/optional/2014_11_09_LiveFactionMessages.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Client:UseLiveFactionMessage', 'false', 'Allow players to see faction adjustments like Live.'); diff --git a/utils/sql/git/optional/2014_11_15_SwarmPetTargetLock.sql b/utils/sql/git/optional/2014_11_15_SwarmPetTargetLock.sql new file mode 100644 index 000000000..f2c11ae45 --- /dev/null +++ b/utils/sql/git/optional/2014_11_15_SwarmPetTargetLock.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:SwarmPetTargetLock', 'false', 'Use old method of swarm pet AI, where they lock onto a set target then depop when target is dead.'); diff --git a/utils/sql/git/optional/2014_11_24_EnableMeritBasedFaction.sql b/utils/sql/git/optional/2014_11_24_EnableMeritBasedFaction.sql new file mode 100644 index 000000000..942b016ab --- /dev/null +++ b/utils/sql/git/optional/2014_11_24_EnableMeritBasedFaction.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'NPC:EnableMeritBasedFaction', 'false', 'If set to true, faction will be given in the same way as experience (solo/group/raid).'); \ No newline at end of file diff --git a/utils/sql/git/optional/2014_11_26_TransformationRules.sql b/utils/sql/git/optional/2014_11_26_TransformationRules.sql new file mode 100644 index 000000000..d86e6811d --- /dev/null +++ b/utils/sql/git/optional/2014_11_26_TransformationRules.sql @@ -0,0 +1,3 @@ +/* Optional Transformation Rules */ +INSERT INTO `rule_values` VALUES (1, 'Inventory:DeleteTransformationMold', 'true', 'false to keep transformation mold forever'); +INSERT INTO `rule_values` VALUES (1, 'Inventory:AllowAnyWeaponTransformation', 'false', 'True allows any MELEE weapon to use the other melee type transformatios'); diff --git a/utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql b/utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql new file mode 100644 index 000000000..ce7e16905 --- /dev/null +++ b/utils/sql/git/optional/2014_11_27_ProjectileDmgOnImpact.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:ProjectileDmgOnImpact', 'true', 'If enabled, projectiles (ie arrows) will hit on impact, instead of instantly.'); diff --git a/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql b/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql index dfcdfd552..e93989777 100644 --- a/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql +++ b/utils/sql/git/required/2013_07_11_NPC_Special_Abilities.sql @@ -1,4 +1,5 @@ -ALTER TABLE `npc_types` ADD COLUMN `special_abilities` TEXT NOT NULL DEFAULT '' AFTER `npcspecialattks`; +ALTER TABLE `npc_types` ADD COLUMN `special_abilities` TEXT NULL AFTER `npcspecialattks`; +ALTER TABLE `npc_types` MODIFY COLUMN `special_abilities` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL; UPDATE npc_types SET special_abilities = CONCAT(special_abilities, "1,1^") WHERE npcspecialattks LIKE BINARY '%S%'; UPDATE npc_types SET special_abilities = CONCAT(special_abilities, "2,1^") WHERE npcspecialattks LIKE BINARY '%E%'; diff --git a/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql b/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql index 740bf7517..cf63625da 100644 --- a/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql +++ b/utils/sql/git/required/2013_10_12_Merc_Special_Abilities.sql @@ -1,5 +1,6 @@ -ALTER TABLE `merc_stats` ADD COLUMN `special_abilities` TEXT NOT NULL DEFAULT '' AFTER `specialattks`; - +ALTER TABLE `merc_stats` ADD COLUMN `special_abilities` TEXT NULL AFTER `specialattks`; +ALTER TABLE `merc_stats` MODIFY COLUMN `special_abilities` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL; + UPDATE merc_stats SET special_abilities = CONCAT(special_abilities, "1,1^") WHERE specialattks LIKE BINARY '%S%'; UPDATE merc_stats SET special_abilities = CONCAT(special_abilities, "2,1^") WHERE specialattks LIKE BINARY '%E%'; UPDATE merc_stats SET special_abilities = CONCAT(special_abilities, "3,1^") WHERE specialattks LIKE BINARY '%R%'; diff --git a/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql b/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql index 15ce809af..1ca4981f2 100644 --- a/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql +++ b/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey.sql @@ -1,5 +1 @@ --- npc_types -ALTER TABLE `npc_types` ADD `ammo_idfile` varchar( 30 ) NOT NULL DEFAULT 'IT10' AFTER `d_meele_texture2`; -ALTER TABLE `npc_types` ADD `ranged_type` tinyint( 4 ) UNSIGNED NOT NULL DEFAULT '7' AFTER `sec_melee_type`; - - +ALTER TABLE `npc_types` ADD `no_target_hotkey` tinyint( 1 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `healscale`; diff --git a/utils/sql/git/required/2014_06_25_AA_Updates..sql b/utils/sql/git/required/2014_06_25_AA_Updates..sql deleted file mode 100644 index 3d7a048b6..000000000 --- a/utils/sql/git/required/2014_06_25_AA_Updates..sql +++ /dev/null @@ -1,34 +0,0 @@ --- AA MGB update -UPDATE altadv_vars SET spellid = 5228 WHERE skill_id = 128; -UPDATE aa_actions SET spell_id = 5228, nonspell_action = 0 WHERE aaid = 128; - --- AA Project Illusion update -UPDATE altadv_vars SET spellid = 5227 WHERE skill_id = 643; -UPDATE aa_actions SET spell_id = 5227, nonspell_action = 0 WHERE aaid = 643; - --- AA Improved Reclaim Energy -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('180', '1', '241', '95', '0'); - --- AA Headshot -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '1', '217', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '2', '346', '46', '0'); - --- AA Anatomy (Rogue Assassinate) -INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`, `sof_current_level`,`sof_next_id`,`level_inc`) VALUES ('1604', 'Anatomy', '5', '3', '4294967295', '4294967295', '1604', '1604', '1', '4294967295', '0', '0', '0', '0', '512', '0', '60', '1', '10', '4294967295', '3', '0', '3', '1604', '1', '0', '0', '0', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '1', '439', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '2', '345', '48', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '1', '439', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '2', '345', '51', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '1', '439', '0', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '2', '345', '53', '0'); - --- AA Finishing Blow Fix -DELETE FROM aa_effects WHERE aaid = 199 AND slot = 2; -DELETE FROM aa_effects WHERE aaid = 200 AND slot = 2; -DELETE FROM aa_effects WHERE aaid = 201 AND slot = 2; -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '1', '278', '500', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '2', '440', '50', '200'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '1', '278', '500', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '2', '440', '52', '200'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '1', '278', '500', '32000'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '2', '440', '54', '200'); \ No newline at end of file diff --git a/utils/sql/git/required/2014_08_18_spells_new_update.sql b/utils/sql/git/required/2014_08_18_spells_new_update.sql new file mode 100644 index 000000000..bda3a55a4 --- /dev/null +++ b/utils/sql/git/required/2014_08_18_spells_new_update.sql @@ -0,0 +1,2 @@ +ALTER TABLE `spells_new` CHANGE `field191` `viral_targets` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field192` `viral_timer` INT(11) NOT NULL DEFAULT '0'; \ No newline at end of file diff --git a/utils/sql/git/required/2014_08_20_merchantlist_probability.sql b/utils/sql/git/required/2014_08_20_merchantlist_probability.sql new file mode 100644 index 000000000..9ceedbb30 --- /dev/null +++ b/utils/sql/git/required/2014_08_20_merchantlist_probability.sql @@ -0,0 +1 @@ +ALTER TABLE `merchantlist` ADD `probability` INT(3) NOT NULL DEFAULT '100' AFTER `classes_required`; \ No newline at end of file diff --git a/utils/sql/git/required/2014_08_23_Complete_QueryServ_Table_Structures.sql b/utils/sql/git/required/2014_08_23_Complete_QueryServ_Table_Structures.sql new file mode 100644 index 000000000..40dc41377 --- /dev/null +++ b/utils/sql/git/required/2014_08_23_Complete_QueryServ_Table_Structures.sql @@ -0,0 +1,247 @@ +-- QS Table Structures -- + +SET FOREIGN_KEY_CHECKS=0; + +-- ---------------------------- +-- Table structure for qs_merchant_transaction_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_merchant_transaction_record`; +CREATE TABLE `qs_merchant_transaction_record` ( + `transaction_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `zone_id` int(11) DEFAULT '0', + `merchant_id` int(11) DEFAULT '0', + `merchant_pp` int(11) DEFAULT '0', + `merchant_gp` int(11) DEFAULT '0', + `merchant_sp` int(11) DEFAULT '0', + `merchant_cp` int(11) DEFAULT '0', + `merchant_items` mediumint(7) DEFAULT '0', + `char_id` int(11) DEFAULT '0', + `char_pp` int(11) DEFAULT '0', + `char_gp` int(11) DEFAULT '0', + `char_sp` int(11) DEFAULT '0', + `char_cp` int(11) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`transaction_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_merchant_transaction_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_merchant_transaction_record_entries`; +CREATE TABLE `qs_merchant_transaction_record_entries` ( + `event_id` int(11) DEFAULT '0', + `char_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_aa_rate_hourly +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_aa_rate_hourly`; +CREATE TABLE `qs_player_aa_rate_hourly` ( + `char_id` int(11) NOT NULL DEFAULT '0', + `hour_time` int(11) NOT NULL, + `aa_count` varchar(11) DEFAULT NULL, + PRIMARY KEY (`char_id`,`hour_time`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +-- ---------------------------- +-- Table structure for qs_player_delete_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_delete_record`; +CREATE TABLE `qs_player_delete_record` ( + `delete_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `char_id` int(11) DEFAULT '0', + `stack_size` mediumint(7) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`delete_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_delete_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_delete_record_entries`; +CREATE TABLE `qs_player_delete_record_entries` ( + `event_id` int(11) DEFAULT '0', + `char_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_events +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_events`; +CREATE TABLE `qs_player_events` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `char_id` int(11) DEFAULT '0', + `event` int(11) unsigned DEFAULT '0', + `event_desc` varchar(255) DEFAULT NULL, + `time` int(11) unsigned DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- ---------------------------- +-- Table structure for qs_player_handin_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_handin_record`; +CREATE TABLE `qs_player_handin_record` ( + `handin_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `quest_id` int(11) DEFAULT '0', + `char_id` int(11) DEFAULT '0', + `char_pp` int(11) DEFAULT '0', + `char_gp` int(11) DEFAULT '0', + `char_sp` int(11) DEFAULT '0', + `char_cp` int(11) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + `npc_id` int(11) DEFAULT '0', + `npc_pp` int(11) DEFAULT '0', + `npc_gp` int(11) DEFAULT '0', + `npc_sp` int(11) DEFAULT '0', + `npc_cp` int(11) DEFAULT '0', + `npc_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`handin_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_handin_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_handin_record_entries`; +CREATE TABLE `qs_player_handin_record_entries` ( + `event_id` int(11) DEFAULT '0', + `action_type` char(6) DEFAULT 'action', + `char_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_move_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_move_record`; +CREATE TABLE `qs_player_move_record` ( + `move_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `char_id` int(11) DEFAULT '0', + `from_slot` mediumint(7) DEFAULT '0', + `to_slot` mediumint(7) DEFAULT '0', + `stack_size` mediumint(7) DEFAULT '0', + `char_items` mediumint(7) DEFAULT '0', + `postaction` tinyint(1) DEFAULT '0', + PRIMARY KEY (`move_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_move_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_move_record_entries`; +CREATE TABLE `qs_player_move_record_entries` ( + `event_id` int(11) DEFAULT '0', + `from_slot` mediumint(7) DEFAULT '0', + `to_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_npc_kill_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_npc_kill_record`; +CREATE TABLE `qs_player_npc_kill_record` ( + `fight_id` int(11) NOT NULL AUTO_INCREMENT, + `npc_id` int(11) DEFAULT NULL, + `type` int(11) DEFAULT NULL, + `zone_id` int(11) DEFAULT NULL, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`fight_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_npc_kill_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_npc_kill_record_entries`; +CREATE TABLE `qs_player_npc_kill_record_entries` ( + `event_id` int(11) DEFAULT '0', + `char_id` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_speech +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_speech`; +CREATE TABLE `qs_player_speech` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `from` varchar(64) NOT NULL, + `to` varchar(64) NOT NULL, + `message` varchar(256) NOT NULL, + `minstatus` smallint(5) NOT NULL, + `guilddbid` int(11) NOT NULL, + `type` tinyint(3) NOT NULL, + `timerecorded` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_trade_record +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_trade_record`; +CREATE TABLE `qs_player_trade_record` ( + `trade_id` int(11) NOT NULL AUTO_INCREMENT, + `time` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, + `char1_id` int(11) DEFAULT '0', + `char1_pp` int(11) DEFAULT '0', + `char1_gp` int(11) DEFAULT '0', + `char1_sp` int(11) DEFAULT '0', + `char1_cp` int(11) DEFAULT '0', + `char1_items` mediumint(7) DEFAULT '0', + `char2_id` int(11) DEFAULT '0', + `char2_pp` int(11) DEFAULT '0', + `char2_gp` int(11) DEFAULT '0', + `char2_sp` int(11) DEFAULT '0', + `char2_cp` int(11) DEFAULT '0', + `char2_items` mediumint(7) DEFAULT '0', + PRIMARY KEY (`trade_id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; + +-- ---------------------------- +-- Table structure for qs_player_trade_record_entries +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_trade_record_entries`; +CREATE TABLE `qs_player_trade_record_entries` ( + `event_id` int(11) DEFAULT '0', + `from_id` int(11) DEFAULT '0', + `from_slot` mediumint(7) DEFAULT '0', + `to_id` int(11) DEFAULT '0', + `to_slot` mediumint(7) DEFAULT '0', + `item_id` int(11) DEFAULT '0', + `charges` mediumint(7) DEFAULT '0', + `aug_1` int(11) DEFAULT '0', + `aug_2` int(11) DEFAULT '0', + `aug_3` int(11) DEFAULT '0', + `aug_4` int(11) DEFAULT '0', + `aug_5` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; diff --git a/utils/sql/git/required/2014_08_23_player_events_and_player_aa_rate_hourly.sql b/utils/sql/git/required/2014_08_23_player_events_and_player_aa_rate_hourly.sql new file mode 100644 index 000000000..2d5cbfa17 --- /dev/null +++ b/utils/sql/git/required/2014_08_23_player_events_and_player_aa_rate_hourly.sql @@ -0,0 +1,23 @@ +-- ---------------------------- +-- Table structure for qs_player_events +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_events`; +CREATE TABLE `qs_player_events` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `char_id` int(11) DEFAULT '0', + `event` int(11) unsigned DEFAULT '0', + `event_desc` varchar(255) DEFAULT NULL, + `time` int(11) unsigned DEFAULT '0', + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- ---------------------------- +-- Table structure for qs_player_aa_rate_hourly +-- ---------------------------- +DROP TABLE IF EXISTS `qs_player_aa_rate_hourly`; +CREATE TABLE `qs_player_aa_rate_hourly` ( + `char_id` int(11) NOT NULL DEFAULT '0', + `hour_time` int(11) NOT NULL, + `aa_count` varchar(11) DEFAULT NULL, + PRIMARY KEY (`char_id`,`hour_time`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/utils/sql/git/required/2014_08_24_character_lookup.sql b/utils/sql/git/required/2014_08_24_character_lookup.sql new file mode 100644 index 000000000..414dbbdcb --- /dev/null +++ b/utils/sql/git/required/2014_08_24_character_lookup.sql @@ -0,0 +1,33 @@ +-- chracter_lookup table structure -- + +CREATE TABLE `character_lookup` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `account_id` int(11) NOT NULL DEFAULT '0', + `name` varchar(64) NOT NULL DEFAULT '', + `timelaston` int(11) unsigned DEFAULT '0', + `x` float NOT NULL DEFAULT '0', + `y` float NOT NULL DEFAULT '0', + `z` float NOT NULL DEFAULT '0', + `zonename` varchar(30) NOT NULL DEFAULT '', + `zoneid` smallint(6) NOT NULL DEFAULT '0', + `instanceid` smallint(5) unsigned NOT NULL DEFAULT '0', + `pktime` int(8) NOT NULL DEFAULT '0', + `groupid` int(10) unsigned NOT NULL DEFAULT '0', + `class` tinyint(4) NOT NULL DEFAULT '0', + `level` mediumint(8) unsigned NOT NULL DEFAULT '0', + `lfp` tinyint(1) unsigned NOT NULL DEFAULT '0', + `lfg` tinyint(1) unsigned NOT NULL DEFAULT '0', + `mailkey` char(16) NOT NULL, + `xtargets` tinyint(3) unsigned NOT NULL DEFAULT '5', + `firstlogon` tinyint(3) NOT NULL DEFAULT '0', + `inspectmessage` varchar(256) NOT NULL DEFAULT '', + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`), + KEY `account_id` (`account_id`) +) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; + +-- Initial population of the character_lookup table -- + +INSERT INTO `character_lookup` (id, account_id, `name`, timelaston, x, y, z, zonename, zoneid, instanceid, pktime, groupid, class, `level`, lfp, lfg, mailkey, xtargets, firstlogon, inspectmessage) +SELECT id, account_id, `name`, timelaston, x, y, z, zonename, zoneid, instanceid, pktime, groupid, class, `level`, lfp, lfg, mailkey, xtargets, firstlogon, inspectmessage +FROM `character_`; \ No newline at end of file diff --git a/utils/sql/git/required/2014_09_09_attack_delay.sql b/utils/sql/git/required/2014_09_09_attack_delay.sql new file mode 100644 index 000000000..87efa043a --- /dev/null +++ b/utils/sql/git/required/2014_09_09_attack_delay.sql @@ -0,0 +1,3 @@ +ALTER TABLE `npc_types` ADD `attack_delay` TINYINT(3) UNSIGNED DEFAULT '30' NOT NULL AFTER `attack_speed`; +UPDATE `npc_types` SET `attack_delay` = 36 + 36 * (`attack_speed` / 100); +UPDATE `npc_types` SET `attack_delay` = 30 WHERE `attack_speed` = 0; diff --git a/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..229f08776 --- /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, 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/utils/sql/git/required/2014_10_30_special_abilities_null.sql b/utils/sql/git/required/2014_10_30_special_abilities_null.sql new file mode 100644 index 000000000..c9e52011d --- /dev/null +++ b/utils/sql/git/required/2014_10_30_special_abilities_null.sql @@ -0,0 +1,4 @@ +ALTER TABLE `merc_stats` MODIFY COLUMN `special_abilities` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL; + +ALTER TABLE `npc_types` MODIFY COLUMN `special_abilities` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL; + diff --git a/utils/sql/git/required/2014_11_08_RaidMembers.sql b/utils/sql/git/required/2014_11_08_RaidMembers.sql new file mode 100644 index 000000000..79e1bb17a --- /dev/null +++ b/utils/sql/git/required/2014_11_08_RaidMembers.sql @@ -0,0 +1 @@ +ALTER TABLE `raid_members` CHANGE COLUMN `groupid` `groupid` INT(4) UNSIGNED NOT NULL DEFAULT '0' AFTER `charid`; \ No newline at end of file diff --git a/utils/sql/git/required/2014_11_13_spells_new_updates.sql b/utils/sql/git/required/2014_11_13_spells_new_updates.sql new file mode 100644 index 000000000..a3863cbd8 --- /dev/null +++ b/utils/sql/git/required/2014_11_13_spells_new_updates.sql @@ -0,0 +1,9 @@ +-- spells new table update +ALTER TABLE `spells_new` CHANGE `field124` `disallow_sit` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field125` `deities0` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field196` `sneaking` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field158` `effectdescnum2` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field165` `ldon_trap` INT(11) NOT NULL DEFAULT '0'; +ALTER TABLE `spells_new` CHANGE `field205` `no_block` INT(11) NOT NULL DEFAULT '0'; + + diff --git a/utils/sql/git/required/2014_11_26_InventoryTableUpdate.sql b/utils/sql/git/required/2014_11_26_InventoryTableUpdate.sql new file mode 100644 index 000000000..27a6d6c37 --- /dev/null +++ b/utils/sql/git/required/2014_11_26_InventoryTableUpdate.sql @@ -0,0 +1,4 @@ +-- Inventory table update +ALTER TABLE `inventory` + ADD COLUMN `ornamenticon` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `custom_data`, + ADD COLUMN `ornamentidfile` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `ornamenticon`; diff --git a/utils/sql/git/required/2014_12_01_mercs_table_update.sql b/utils/sql/git/required/2014_12_01_mercs_table_update.sql new file mode 100644 index 000000000..e5dc895f9 --- /dev/null +++ b/utils/sql/git/required/2014_12_01_mercs_table_update.sql @@ -0,0 +1 @@ +ALTER TABLE `mercs` ADD `MercSize` float( 0 ) NOT NULL DEFAULT '5' AFTER `Gender`; diff --git a/utils/sql/peq_aa_tables.sql b/utils/sql/peq_aa_tables.sql new file mode 100644 index 000000000..700106fa0 --- /dev/null +++ b/utils/sql/peq_aa_tables.sql @@ -0,0 +1,75 @@ +DROP TABLE IF EXISTS `aa_effects`; +CREATE TABLE `aa_effects` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `aaid` mediumint(8) unsigned NOT NULL DEFAULT '0', + `slot` tinyint(3) unsigned NOT NULL DEFAULT '0', + `effectid` mediumint(8) unsigned NOT NULL DEFAULT '0', + `base1` mediumint(8) NOT NULL DEFAULT '0', + `base2` mediumint(8) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `NewIndex` (`aaid`,`slot`) +) ENGINE=MyISAM AUTO_INCREMENT=2527 DEFAULT CHARSET=latin1; +INSERT INTO `aa_effects` VALUES (1,2,1,4,2,0),(2,3,1,4,4,0),(3,4,1,4,6,0),(4,5,1,4,8,0),(5,6,1,4,10,0),(6,7,1,7,2,0),(7,8,1,7,4,0),(8,9,1,7,6,0),(9,10,1,7,8,0),(10,11,1,7,10,0),(11,12,1,6,2,0),(12,13,1,6,4,0),(13,14,1,6,6,0),(14,15,1,6,8,0),(15,16,1,6,10,0),(16,17,1,5,2,0),(17,18,1,5,4,0),(18,19,1,5,6,0),(19,20,1,5,8,0),(20,21,1,5,10,0),(21,22,1,8,2,0),(22,23,1,8,4,0),(23,24,1,8,6,0),(24,25,1,8,8,0),(25,26,1,8,10,0),(26,27,1,9,2,0),(27,28,1,9,4,0),(28,29,1,9,6,0),(29,30,1,9,8,0),(30,31,1,9,10,0),(31,32,1,10,2,0),(32,33,1,10,4,0),(33,34,1,10,6,0),(34,35,1,10,8,0),(35,36,1,10,10,0),(36,37,1,46,2,0),(37,38,1,46,4,0),(38,39,1,46,6,0),(39,40,1,46,8,0),(40,41,1,46,10,0),(41,42,1,47,2,0),(42,43,1,47,4,0),(43,44,1,47,6,0),(44,45,1,47,8,0),(45,46,1,47,10,0),(46,47,1,50,2,0),(47,48,1,50,4,0),(48,49,1,50,6,0),(49,50,1,50,8,0),(50,51,1,50,10,0),(51,52,1,48,2,0),(52,53,1,48,4,0),(53,54,1,48,6,0),(54,55,1,48,8,0),(55,56,1,48,10,0),(56,57,1,49,2,0),(57,58,1,49,4,0),(58,59,1,49,6,0),(59,60,1,49,8,0),(60,61,1,49,10,0),(61,62,1,271,8,0),(62,63,1,271,14,0),(63,64,1,271,21,0),(64,65,1,0,1,0),(65,66,1,0,2,0),(66,67,1,0,3,0),(67,71,1,246,110,0),(68,72,1,246,125,0),(69,73,1,246,150,0),(70,89,1,15,1,0),(71,90,1,15,2,0),(72,91,1,15,3,0),(1953,104,1,127,10,10),(1954,104,2,138,1,0),(1955,104,3,140,1,0),(1956,104,4,143,3000,0),(1957,105,1,127,25,25),(1958,105,2,138,1,0),(1959,105,3,140,1,0),(1960,105,4,143,3000,0),(1961,106,1,127,50,50),(1962,106,2,138,1,0),(1963,106,3,140,1,0),(1964,106,4,143,3000,0),(910,107,1,214,200,0),(911,108,1,214,500,0),(912,109,1,214,1000,0),(88,110,1,0,1,0),(89,111,1,0,2,0),(90,112,1,0,3,0),(1908,137,1,127,10,10),(1909,137,2,137,88,0),(1910,138,1,127,25,25),(1911,138,2,137,88,0),(1912,139,1,127,50,50),(1913,139,2,137,88,0),(1893,141,1,127,2,2),(1894,141,2,137,0,0),(1895,141,3,138,0,0),(1896,141,4,141,1,0),(1897,141,5,143,3000,0),(1898,142,1,127,5,5),(1899,142,2,137,0,0),(1900,142,3,138,0,0),(1901,142,4,141,1,0),(1902,142,5,143,3000,0),(1903,143,1,127,10,10),(1904,143,2,137,0,0),(1905,143,3,138,0,0),(1906,143,4,141,1,0),(1907,143,5,143,3000,0),(1941,147,1,127,10,10),(1942,147,2,138,1,0),(1943,147,3,140,1,0),(1944,147,4,143,3000,0),(1945,148,1,127,25,25),(1946,148,2,138,1,0),(1947,148,3,140,1,0),(1948,148,4,143,3000,0),(1949,149,1,127,50,50),(1950,149,2,138,1,0),(1951,149,3,140,1,0),(1952,149,4,143,3000,0),(1914,164,1,127,10,10),(1915,164,2,137,32,0),(1916,164,3,127,10,10),(1917,164,4,137,33,0),(1923,165,1,127,25,25),(1924,165,2,137,32,0),(1925,165,3,127,25,25),(1926,165,4,137,33,0),(1932,166,1,127,50,50),(1933,166,2,137,32,0),(1934,166,3,127,50,50),(1935,166,4,137,33,0),(137,196,1,226,1,0),(138,237,1,227,1,25),(139,238,1,227,3,25),(140,239,1,227,5,25),(1077,270,1,270,10,0),(1078,271,1,270,15,0),(1079,272,1,270,25,0),(144,278,1,0,1,0),(145,278,2,15,1,0),(907,279,1,214,200,0),(147,280,1,227,1,62),(148,280,2,227,1,17),(149,281,1,227,3,62),(150,281,2,227,3,17),(151,282,1,227,5,62),(152,282,2,227,5,17),(153,292,1,4,2,0),(154,293,1,4,4,0),(155,294,1,4,6,0),(156,295,1,4,8,0),(157,296,1,4,10,0),(158,297,1,4,12,0),(159,298,1,4,14,0),(160,299,1,4,16,0),(161,300,1,4,18,0),(162,301,1,4,20,0),(163,302,1,7,2,0),(164,303,1,7,4,0),(165,304,1,7,6,0),(166,305,1,7,8,0),(167,306,1,7,10,0),(168,307,1,7,12,0),(169,308,1,7,14,0),(170,309,1,7,16,0),(171,310,1,7,18,0),(172,311,1,7,20,0),(173,312,1,6,2,0),(174,313,1,6,4,0),(175,314,1,6,6,0),(176,315,1,6,8,0),(177,316,1,6,10,0),(178,317,1,6,12,0),(179,318,1,6,14,0),(180,319,1,6,16,0),(181,320,1,6,18,0),(182,321,1,6,20,0),(183,322,1,5,2,0),(184,323,1,5,4,0),(185,324,1,5,6,0),(186,325,1,5,8,0),(187,326,1,5,10,0),(188,327,1,5,12,0),(189,328,1,5,14,0),(190,329,1,5,16,0),(191,330,1,5,18,0),(192,331,1,5,20,0),(193,332,1,8,2,0),(194,333,1,8,4,0),(195,334,1,8,6,0),(196,335,1,8,8,0),(197,336,1,8,10,0),(198,337,1,8,12,0),(199,338,1,8,14,0),(200,339,1,8,16,0),(201,340,1,8,18,0),(202,341,1,8,20,0),(203,342,1,9,2,0),(204,343,1,9,4,0),(205,344,1,9,6,0),(206,345,1,9,8,0),(207,346,1,9,10,0),(208,347,1,9,12,0),(209,348,1,9,14,0),(210,349,1,9,16,0),(211,350,1,9,18,0),(212,351,1,9,20,0),(213,352,1,10,2,0),(214,353,1,10,4,0),(215,354,1,10,6,0),(216,355,1,10,8,0),(217,356,1,10,10,0),(218,357,1,10,12,0),(219,358,1,10,14,0),(220,359,1,10,16,0),(221,360,1,10,18,0),(222,361,1,10,20,0),(223,362,1,46,2,0),(224,363,1,46,4,0),(225,364,1,46,6,0),(226,365,1,46,8,0),(227,366,1,46,10,0),(229,368,1,46,14,0),(230,369,1,46,16,0),(231,370,1,46,18,0),(232,371,1,46,20,0),(233,372,1,47,2,0),(234,373,1,47,4,0),(235,374,1,47,6,0),(236,375,1,47,8,0),(237,376,1,47,10,0),(238,377,1,47,12,0),(239,378,1,47,14,0),(240,379,1,47,16,0),(241,380,1,47,18,0),(242,381,1,47,20,0),(243,382,1,50,2,0),(244,383,1,50,4,0),(245,384,1,50,6,0),(246,385,1,50,8,0),(247,386,1,50,10,0),(248,387,1,50,12,0),(249,388,1,50,14,0),(250,389,1,50,16,0),(251,390,1,50,18,0),(252,391,1,50,20,0),(253,392,1,48,2,0),(254,393,1,48,4,0),(255,394,1,48,6,0),(256,395,1,48,8,0),(257,396,1,48,10,0),(258,397,1,48,12,0),(259,398,1,48,14,0),(260,399,1,48,16,0),(261,400,1,48,18,0),(262,401,1,48,20,0),(263,402,1,49,2,0),(264,403,1,49,4,0),(265,404,1,49,6,0),(266,405,1,49,8,0),(267,406,1,49,10,0),(268,407,1,49,12,0),(269,408,1,49,14,0),(270,409,1,49,16,0),(271,410,1,49,18,0),(272,411,1,49,20,0),(273,418,1,262,5,0),(274,418,2,262,5,1),(275,418,3,262,5,2),(276,418,4,262,5,3),(277,418,5,262,5,4),(278,418,6,262,5,5),(279,418,7,262,5,6),(280,419,1,262,10,0),(281,419,2,262,10,1),(282,419,3,262,10,2),(283,419,4,262,10,3),(284,419,5,262,10,4),(285,419,6,262,10,5),(286,419,7,262,10,6),(287,420,1,262,15,0),(288,420,2,262,15,1),(289,420,3,262,15,2),(290,420,4,262,15,3),(291,420,5,262,15,4),(292,420,6,262,15,5),(293,420,7,262,15,6),(294,421,1,262,20,0),(295,421,2,262,20,1),(296,421,3,262,20,2),(297,421,4,262,20,3),(298,421,5,262,20,4),(299,421,6,262,20,5),(300,421,7,262,20,6),(301,422,1,262,25,0),(302,422,2,262,25,1),(303,422,3,262,25,2),(304,422,4,262,25,3),(305,422,5,262,25,4),(306,422,6,262,25,5),(307,422,7,262,25,6),(308,423,1,214,150,0),(309,424,1,214,300,0),(310,425,1,214,450,0),(311,426,1,262,10,4),(312,426,2,262,10,5),(313,427,1,262,20,4),(314,427,2,262,20,5),(315,428,1,262,30,4),(316,428,2,262,30,5),(317,429,1,262,40,4),(318,429,2,262,40,5),(319,430,1,262,50,4),(320,430,2,262,50,5),(2267,568,1,270,5,0),(2268,569,1,270,10,0),(2269,570,1,270,15,0),(324,622,1,227,1,29),(325,623,1,227,2,29),(326,624,1,227,3,29),(327,628,1,290,5,0),(328,629,1,290,15,0),(329,672,1,271,7,0),(330,673,1,271,14,0),(331,676,1,246,125,0),(332,677,1,246,150,0),(333,978,1,14,1,0),(334,1001,1,262,5,0),(335,1001,2,262,5,1),(336,1001,3,262,5,2),(337,1001,4,262,5,3),(338,1001,5,262,5,4),(339,1001,6,262,5,5),(340,1001,7,262,5,6),(341,1002,1,262,10,0),(342,1002,2,262,10,1),(343,1002,3,262,10,2),(344,1002,4,262,10,3),(345,1002,5,262,10,4),(346,1002,6,262,10,5),(347,1002,7,262,10,6),(348,1003,1,262,15,0),(349,1003,2,262,15,1),(350,1003,3,262,15,2),(351,1003,4,262,15,3),(352,1003,5,262,15,4),(353,1003,6,262,15,5),(354,1003,7,262,15,6),(355,1004,1,262,20,0),(356,1004,2,262,20,1),(357,1004,3,262,20,2),(358,1004,4,262,20,3),(359,1004,5,262,20,4),(360,1004,6,262,20,5),(361,1004,7,262,20,6),(362,1005,1,262,25,0),(363,1005,2,262,25,1),(364,1005,3,262,25,2),(365,1005,4,262,25,3),(366,1005,5,262,25,4),(367,1005,6,262,25,5),(368,1005,7,262,25,6),(369,1006,1,262,5,7),(370,1006,2,262,5,8),(371,1006,3,262,5,9),(372,1006,4,262,5,10),(373,1006,5,262,5,11),(374,1007,1,262,10,7),(375,1007,2,262,10,8),(376,1007,3,262,10,9),(377,1007,4,262,10,10),(378,1007,5,262,10,11),(379,1008,1,262,15,7),(380,1008,2,262,15,8),(381,1008,3,262,15,9),(382,1008,4,262,15,10),(383,1008,5,262,15,11),(384,1009,1,262,20,7),(385,1009,2,262,20,8),(386,1009,3,262,20,9),(387,1009,4,262,20,10),(388,1009,5,262,20,11),(389,1010,1,262,25,7),(390,1010,2,262,25,8),(391,1010,3,262,25,9),(392,1010,4,262,25,10),(393,1010,5,262,25,11),(394,1021,1,327,1,0),(395,1022,1,327,2,0),(396,1023,1,327,3,0),(397,1024,1,327,4,0),(398,1025,1,327,5,0),(399,1071,1,326,1,0),(2277,1129,1,267,1,0),(2276,288,1,267,1,15),(402,678,1,221,3,0),(403,679,1,221,6,0),(404,680,1,221,9,0),(405,681,1,221,12,0),(406,682,1,221,15,0),(407,807,1,292,5,0),(408,808,1,292,10,0),(409,809,1,292,15,0),(913,864,1,216,10,0),(921,865,1,216,20,0),(929,866,1,216,30,0),(413,907,1,69,100,0),(414,908,1,69,200,0),(415,909,1,69,300,0),(416,910,1,69,400,0),(417,911,1,69,500,0),(2289,580,9,254,0,0),(2288,580,8,254,0,0),(2287,580,7,254,0,0),(2286,580,6,254,0,0),(2285,580,5,254,0,0),(2284,580,4,254,0,0),(2283,580,3,267,1,5),(2282,580,2,267,1,8),(2281,580,1,267,1,4),(1541,1435,1,339,3,8105),(1542,1435,2,142,65,0),(1543,1435,3,311,0,0),(1544,1435,4,134,70,0),(1565,1436,1,339,6,8105),(1566,1436,2,142,65,0),(1567,1436,3,311,0,0),(1568,1436,4,134,70,0),(1589,1437,1,339,10,8105),(1590,1437,2,142,65,0),(1591,1437,3,311,0,0),(1592,1437,4,134,70,0),(1277,1486,1,339,1,8164),(1278,1486,2,138,1,0),(1279,1486,3,141,1,0),(1280,1486,4,142,60,0),(1281,1486,5,137,0,0),(1282,1486,6,311,0,0),(1293,1487,1,339,3,8165),(1294,1487,2,138,1,0),(1295,1487,3,141,1,0),(1296,1487,4,142,60,0),(1297,1487,5,137,0,0),(1298,1487,6,311,0,0),(1309,1488,1,339,5,8166),(1310,1488,2,138,1,0),(1311,1488,3,141,1,0),(1312,1488,4,142,60,0),(1313,1488,5,137,0,0),(1314,1488,6,311,0,0),(1325,1489,1,339,7,8167),(1326,1489,2,138,1,0),(1327,1489,3,141,1,0),(1328,1489,4,142,60,0),(1329,1489,5,137,0,0),(1330,1489,6,311,0,0),(1341,1490,1,339,9,8168),(1342,1490,2,138,1,0),(1343,1490,3,141,1,0),(1344,1490,4,142,60,0),(1345,1490,5,137,0,0),(1346,1490,6,311,0,0),(1749,625,1,294,3,100),(1750,626,1,294,6,100),(1751,627,1,294,9,100),(472,92,1,294,2,100),(473,93,1,294,4,100),(474,94,1,294,7,100),(475,267,1,294,3,100),(476,268,1,294,5,100),(477,269,1,294,7,100),(478,637,1,294,2,100),(479,638,1,294,4,100),(480,639,1,294,6,100),(481,640,1,294,2,100),(482,641,1,294,4,100),(483,642,1,294,6,100),(484,770,1,294,2,100),(485,771,1,294,4,100),(486,772,1,294,6,100),(487,1107,1,294,2,100),(488,1108,1,294,4,100),(489,1109,1,294,6,100),(490,924,1,294,2,100),(491,925,1,294,4,100),(1517,1210,1,294,0,107),(1518,1211,1,294,0,116),(1519,1212,1,294,0,125),(1965,77,1,125,2,2),(1966,77,2,137,0,0),(1973,78,1,125,5,5),(1974,78,2,137,0,0),(1981,79,1,125,10,10),(1982,79,2,137,0,0),(1989,434,1,125,13,13),(1997,435,1,125,16,16),(2005,436,1,125,19,19),(1990,434,2,137,0,0),(1998,435,2,137,0,0),(2006,436,2,137,0,0),(507,80,1,274,3,0),(2299,581,8,254,0,0),(2298,581,7,254,0,0),(510,81,1,274,6,0),(511,82,1,274,10,0),(2297,581,6,254,0,0),(513,437,1,274,2,0),(514,437,2,141,0,0),(515,438,1,274,4,0),(516,438,2,141,0,0),(517,439,1,274,6,0),(518,439,2,141,0,0),(833,113,1,169,15,-1),(834,114,1,169,30,-1),(835,115,1,169,45,-1),(836,443,1,169,50,-1),(837,444,1,169,100,-1),(838,445,1,169,153,-1),(1206,767,1,273,3,0),(1207,768,1,273,6,0),(1208,769,1,273,9,0),(1209,1099,1,273,3,0),(1210,1100,1,273,6,0),(1211,1101,1,273,9,0),(531,1011,1,262,8,7),(532,1011,2,262,8,8),(533,1011,3,262,8,9),(534,1011,4,262,8,10),(535,1011,5,262,8,11),(536,1012,1,262,16,7),(537,1012,2,262,16,8),(538,1012,3,262,16,9),(539,1012,4,262,16,10),(540,1012,5,262,16,11),(541,1013,1,262,24,7),(542,1013,2,262,24,8),(543,1013,3,262,24,9),(544,1013,4,262,24,10),(545,1013,5,262,24,11),(546,1014,1,262,32,7),(547,1014,2,262,32,8),(548,1014,3,262,32,9),(549,1014,4,262,32,10),(550,1014,5,262,32,11),(551,1015,1,262,40,7),(552,1015,2,262,40,8),(553,1015,3,262,40,9),(554,1015,4,262,40,10),(555,1015,5,262,40,11),(556,1016,1,262,50,7),(557,1016,2,262,50,8),(558,1016,3,262,50,9),(559,1016,4,262,50,10),(560,1016,5,262,50,11),(561,1361,1,262,10,0),(562,1361,2,262,10,1),(563,1361,3,262,10,2),(564,1361,4,262,10,3),(565,1361,5,262,10,4),(566,1361,6,262,10,5),(567,1361,7,262,10,6),(568,1362,1,69,250,0),(569,1362,2,97,250,0),(570,1362,3,190,250,0),(571,1363,1,327,1,0),(572,1364,1,273,3,0),(573,1364,2,274,3,0),(574,1364,3,294,3,100),(575,1364,4,319,3,0),(576,1364,5,169,3,0),(577,1365,1,180,3,0),(578,1366,1,262,10,0),(579,1366,2,262,10,1),(580,1366,3,262,10,2),(581,1366,4,262,10,3),(582,1366,5,262,10,4),(583,1366,6,262,10,5),(584,1366,7,262,10,6),(585,1367,1,69,250,0),(586,1367,2,97,250,0),(587,1367,3,190,250,0),(588,1368,1,327,1,0),(589,1369,1,273,3,0),(590,1369,2,274,3,0),(591,1369,3,294,3,100),(592,1369,4,319,3,0),(593,1369,5,169,3,0),(594,1370,1,180,3,0),(595,116,1,181,5,0),(596,117,1,181,15,0),(597,118,1,181,25,0),(598,195,1,181,100,0),(599,683,1,189,1,0),(600,684,1,189,2,0),(601,685,1,189,3,0),(602,674,1,0,1,0),(603,675,1,0,2,0),(604,1031,1,0,1,0),(605,1032,1,0,2,0),(606,1033,1,0,3,0),(607,1034,1,0,4,0),(608,1035,1,0,5,0),(609,1072,1,318,1,0),(610,1073,1,318,2,0),(611,1074,1,318,3,0),(612,1075,1,318,4,0),(613,1076,1,318,5,0),(1215,692,1,229,5,0),(1216,693,1,229,10,0),(1217,694,1,229,15,0),(2296,581,5,267,1,7),(2295,581,4,267,1,2),(2294,581,3,267,1,5),(2293,581,2,267,1,8),(2292,581,1,267,1,4),(2291,580,11,254,0,0),(2290,580,10,254,0,0),(2280,1130,2,267,1,24),(2279,1130,1,267,1,21),(2278,1129,2,267,1,24),(627,1026,1,328,50,0),(628,1027,1,328,100,0),(629,1028,1,328,150,0),(630,1029,1,328,200,0),(631,1030,1,328,250,0),(632,1587,1,344,50,0),(633,1588,1,344,55,0),(634,1589,1,344,60,0),(635,652,1,293,25,0),(636,653,1,293,50,0),(637,654,1,293,75,0),(638,1586,1,293,25,0),(639,74,1,269,10,0),(640,75,1,269,20,0),(641,76,1,269,30,0),(642,263,1,282,10,0),(643,264,1,282,25,0),(644,265,1,282,50,0),(645,537,1,282,60,0),(646,538,1,282,70,0),(647,1611,1,282,4,0),(648,1612,1,282,8,0),(649,1613,1,282,12,0),(650,1614,1,282,16,0),(651,1615,1,282,20,0),(652,5136,1,282,4,0),(653,5137,1,282,8,0),(654,5138,1,282,12,0),(655,5139,1,282,16,0),(656,5140,1,282,20,0),(2246,1621,1,177,5,0),(2247,1622,1,177,10,0),(2248,1623,1,177,15,0),(1819,599,1,266,2,0),(1820,600,1,266,4,0),(1821,601,1,266,8,0),(1822,1536,1,266,3,0),(1823,1537,1,266,6,0),(1824,1538,1,266,9,0),(666,602,1,266,2,0),(667,603,1,266,4,0),(668,604,1,266,8,0),(669,1388,2,13,1,0),(670,1627,1,271,5,0),(671,1628,1,271,10,0),(672,1629,1,271,15,0),(673,855,1,220,5,10),(674,856,1,220,15,10),(675,857,1,220,30,10),(676,820,1,220,10,26),(677,820,2,220,10,30),(678,820,3,220,10,38),(679,821,1,220,20,26),(680,821,2,220,20,30),(681,821,3,220,20,38),(682,822,1,220,30,26),(683,822,2,220,30,30),(684,822,3,220,30,38),(685,915,1,220,5,10),(686,915,2,220,5,30),(687,916,1,220,15,10),(688,916,2,220,15,30),(689,917,1,220,30,10),(690,917,2,220,30,30),(691,1287,1,320,1,0),(692,1288,2,320,3,0),(693,1289,3,320,5,0),(694,1134,1,220,32,74),(695,1135,1,220,64,74),(696,1136,1,220,96,74),(697,1158,1,220,128,74),(698,1159,1,220,192,74),(699,1160,1,220,224,74),(700,1161,1,220,256,74),(701,1162,1,220,288,74),(2225,846,1,258,5,0),(2226,847,1,258,10,0),(2227,848,1,258,15,0),(2228,1301,1,258,5,0),(2229,1302,1,258,10,0),(2230,1303,1,258,15,0),(708,287,1,253,1,0),(709,205,1,251,100,0),(2273,199,1,301,30,0),(2274,200,1,301,60,0),(2275,201,1,301,100,0),(713,1131,1,185,10,51),(714,1132,1,185,20,51),(715,1133,1,185,30,51),(716,806,1,249,1,0),(717,823,1,222,20,0),(718,824,1,222,25,0),(719,825,1,222,30,0),(720,826,1,222,35,0),(721,827,1,222,40,0),(722,631,1,292,15,0),(723,632,1,292,30,0),(724,633,1,292,45,0),(2237,564,1,177,3,0),(2238,565,1,177,6,0),(2239,566,1,177,9,0),(2493,591,1,303,600,0),(2241,562,1,177,6,0),(2242,563,1,177,9,0),(2243,1624,1,177,5,0),(2244,1625,1,177,10,0),(2245,1626,1,177,15,0),(734,551,1,225,3,0),(735,552,1,225,6,0),(736,553,1,225,9,0),(737,554,1,225,12,0),(738,555,1,225,15,0),(739,556,1,225,3,0),(740,557,1,225,6,0),(741,558,1,225,9,0),(742,559,1,225,12,0),(2491,590,1,303,400,0),(744,686,1,200,10,0),(745,687,1,200,20,0),(746,688,1,200,30,0),(747,689,1,200,40,0),(748,690,1,200,50,0),(749,273,1,288,100,21),(750,611,1,283,20,0),(751,612,1,283,40,0),(752,613,1,283,60,0),(753,614,1,283,80,0),(754,615,1,283,100,0),(2231,878,1,252,10,0),(2232,879,1,252,20,0),(2233,880,1,252,30,0),(2234,1539,1,252,5,0),(2235,1540,1,252,10,0),(2236,1541,1,252,15,0),(761,1041,1,330,25,0),(762,1041,2,330,25,1),(763,1041,3,330,25,2),(764,1041,4,330,25,3),(765,1041,5,330,25,28),(766,1041,6,330,25,36),(767,1042,1,330,30,0),(768,1042,2,330,30,1),(769,1042,3,330,30,2),(770,1042,4,330,30,3),(771,1042,5,330,30,28),(772,1042,6,330,30,36),(773,1043,1,330,35,0),(774,1043,2,330,35,1),(775,1043,3,330,35,2),(776,1043,4,330,35,3),(777,1043,5,330,35,28),(778,1043,6,330,35,36),(779,1044,1,330,25,0),(780,1044,2,330,25,1),(781,1044,3,330,25,2),(782,1044,4,330,25,3),(783,1044,5,330,25,28),(784,1044,6,330,25,36),(785,1045,1,330,30,0),(786,1045,2,330,30,1),(787,1045,3,330,30,2),(788,1045,4,330,30,3),(789,1045,5,330,30,28),(790,1045,6,330,30,36),(791,1046,1,330,35,0),(792,1046,2,330,35,1),(793,1046,3,330,35,2),(794,1046,4,330,35,3),(795,1046,5,330,35,28),(796,1046,6,330,35,36),(797,1047,1,330,25,0),(798,1047,2,330,25,1),(799,1047,3,330,25,2),(800,1047,4,330,25,3),(801,1047,5,330,25,28),(802,1047,6,330,25,36),(803,1048,1,330,30,0),(804,1048,2,330,30,1),(805,1048,3,330,30,2),(806,1048,4,330,30,3),(807,1048,5,330,30,28),(808,1048,6,330,30,36),(809,1049,1,330,35,0),(810,1049,2,330,35,1),(811,1049,3,330,35,2),(812,1049,4,330,35,3),(813,1049,5,330,35,28),(814,1049,6,330,35,36),(815,1050,1,330,15,0),(816,1050,2,330,15,1),(817,1050,3,330,15,2),(818,1050,4,330,15,3),(819,1050,5,330,15,28),(820,1050,6,330,15,36),(821,1051,1,330,20,0),(822,1051,2,330,20,1),(823,1051,3,330,20,2),(824,1051,4,330,20,3),(825,1051,5,330,20,28),(826,1051,6,330,20,36),(827,1052,1,330,25,0),(828,1052,2,330,25,1),(829,1052,3,330,25,2),(830,1052,4,330,25,3),(831,1052,5,330,25,28),(832,1052,6,330,25,36),(839,190,1,219,225,680),(840,191,1,219,235,725),(841,192,1,219,245,770),(842,1524,1,219,485,815),(843,1525,1,219,495,860),(844,1526,1,219,515,905),(845,790,1,218,1,0),(846,791,1,218,2,0),(847,792,1,218,3,0),(848,793,1,218,4,0),(849,794,1,218,5,0),(850,834,1,218,1,0),(851,835,1,218,2,0),(852,836,1,218,3,0),(853,837,1,218,4,0),(854,838,1,218,5,0),(855,724,1,218,1,0),(856,725,1,218,2,0),(857,726,1,218,3,0),(858,727,1,218,4,0),(859,728,1,218,5,0),(860,8201,1,218,1,0),(861,8202,1,218,2,0),(862,8203,1,218,3,0),(863,795,1,280,2,0),(864,796,1,280,4,0),(865,797,1,280,6,0),(866,798,1,280,8,0),(867,799,1,280,10,0),(868,839,1,280,2,0),(869,840,1,280,4,0),(870,841,1,280,6,0),(871,842,1,280,8,0),(872,843,1,280,10,0),(873,729,1,280,2,0),(874,730,1,280,4,0),(875,731,1,280,6,0),(876,732,1,280,8,0),(877,733,1,280,10,0),(878,803,1,213,2,0),(879,804,1,213,5,0),(880,805,1,213,10,0),(881,800,1,215,2,0),(882,801,1,215,5,0),(883,802,1,215,10,0),(2061,125,1,172,2,0),(2062,126,1,172,5,0),(2063,127,1,172,10,0),(2064,449,1,172,3,0),(2065,450,1,172,6,0),(2066,451,1,172,9,0),(2067,452,1,172,12,0),(2068,453,1,172,15,0),(2069,1061,1,172,1,0),(2070,1062,1,172,2,0),(2071,1063,1,172,3,0),(2072,1064,1,172,5,0),(2073,1065,1,172,7,0),(2074,1394,1,172,1,0),(2075,1395,1,172,2,0),(2076,1396,1,172,3,0),(2077,1397,1,172,5,0),(2078,1398,1,172,7,0),(2079,5519,1,172,1,0),(2080,5520,1,172,2,0),(2081,5521,1,172,3,0),(2082,5522,1,172,4,0),(2083,5523,1,172,5,0),(908,279,2,259,2,0),(909,279,3,172,2,0),(914,864,2,216,10,1),(915,864,3,216,10,2),(916,864,4,216,10,3),(917,864,5,216,10,10),(918,864,6,216,10,28),(919,864,7,216,10,30),(920,864,8,216,10,36),(922,865,2,216,20,1),(923,865,3,216,20,2),(924,865,4,216,20,3),(925,865,5,216,20,10),(926,865,6,216,20,28),(927,865,7,216,20,30),(928,865,8,216,20,36),(930,866,2,216,30,1),(931,866,3,216,30,2),(932,866,4,216,30,3),(933,866,5,216,30,10),(934,866,6,216,30,28),(935,866,7,216,30,30),(936,866,8,216,30,36),(937,1290,1,216,40,0),(938,1290,2,216,40,1),(939,1290,3,216,40,2),(940,1290,4,216,40,3),(941,1290,5,216,40,10),(942,1290,6,216,40,28),(943,1290,7,216,40,30),(944,1290,8,216,40,36),(945,1291,1,216,50,0),(946,1291,2,216,50,1),(947,1291,3,216,50,2),(948,1291,4,216,50,3),(949,1291,5,216,50,10),(950,1291,6,216,50,28),(951,1291,7,216,50,30),(952,1291,8,216,50,36),(953,1292,1,216,60,0),(954,1292,2,216,60,1),(955,1292,3,216,60,2),(956,1292,4,216,60,3),(957,1292,5,216,60,10),(958,1292,6,216,60,28),(959,1292,7,216,60,30),(960,1292,8,216,60,36),(961,1140,1,216,10,51),(963,1141,1,216,30,51),(2094,122,1,259,2,0),(2095,123,1,259,5,0),(2096,124,1,259,10,0),(2097,454,1,259,3,0),(2098,455,1,259,6,0),(2099,456,1,259,9,0),(2100,457,1,259,12,0),(2101,458,1,259,15,0),(2102,1066,1,259,1,0),(2103,1067,1,259,2,0),(2104,1068,1,259,3,0),(2105,1069,1,259,5,0),(2106,1070,1,259,7,0),(2107,1399,1,259,1,0),(2108,1400,1,259,2,0),(2109,1401,1,259,3,0),(2110,1402,1,259,5,0),(2111,1403,1,259,7,0),(2112,5524,1,259,1,0),(2113,5525,1,259,2,0),(2114,5526,1,259,3,0),(2115,5527,1,259,4,0),(2116,5528,1,259,5,0),(987,247,1,224,15,0),(988,248,1,224,35,0),(989,249,1,224,50,0),(990,504,1,224,60,0),(991,505,1,224,70,0),(992,506,1,224,80,0),(993,240,1,224,20,26),(994,240,2,173,1,0),(995,241,1,224,40,26),(996,241,2,173,2,0),(997,242,1,224,60,26),(998,242,2,173,3,0),(999,6287,1,224,20,30),(1000,6287,2,173,1,0),(1001,6288,1,224,40,30),(1002,6288,2,173,2,0),(1003,6289,1,224,60,30),(1004,6289,2,173,3,0),(1005,7106,1,224,20,30),(1006,7106,2,173,1,0),(1007,7107,1,224,40,30),(1008,7107,2,173,2,0),(1009,7108,1,224,60,30),(1010,7108,2,173,3,0),(1011,6269,1,224,20,30),(1012,6269,2,173,1,0),(1013,6270,1,224,40,30),(1014,6270,2,173,2,0),(1015,6271,1,224,60,30),(1016,6271,2,173,3,0),(1017,6266,1,224,20,10),(1018,6266,2,173,1,0),(1019,6267,1,224,40,10),(1020,6267,2,173,2,0),(1021,6268,1,224,60,10),(1022,6268,2,173,3,0),(1023,1143,1,224,20,74),(1024,1143,2,173,1,0),(1025,1144,1,224,40,74),(1026,1144,2,173,2,0),(1027,1145,1,224,60,74),(1028,1145,2,173,3,0),(1029,6275,1,224,20,8),(1030,6275,2,173,1,0),(1031,6276,1,224,40,8),(1032,6276,2,173,2,0),(1033,6277,1,224,60,8),(1034,6277,2,173,3,0),(2249,255,1,279,7,0),(2250,256,1,279,11,0),(2251,257,1,279,15,0),(2252,542,1,279,2,0),(2253,543,1,279,4,0),(2254,544,1,279,6,0),(2255,1163,1,279,2,0),(2256,1164,1,279,4,0),(2257,1165,1,279,6,0),(2258,4795,1,279,1,0),(2259,4796,1,279,2,0),(2260,4797,1,279,3,0),(2261,815,1,279,7,0),(2262,816,1,279,11,0),(2263,817,1,279,15,0),(2264,1616,1,279,7,0),(2265,1617,1,279,11,0),(2266,1618,1,279,15,0),(1053,198,1,276,32,0),(1054,741,1,232,2,4544),(1055,742,1,232,4,4545),(1056,743,1,232,6,4546),(1057,744,1,232,8,4547),(1058,745,1,232,10,4548),(1059,577,1,277,20,0),(1060,578,1,277,40,0),(1061,579,1,277,60,0),(1062,95,1,235,5,0),(1063,96,1,235,10,0),(1064,97,1,235,15,0),(1065,710,1,235,5,0),(1066,711,1,235,10,0),(1067,712,1,235,15,0),(1068,713,1,235,20,0),(1069,714,1,235,25,0),(2270,144,1,244,50,0),(2271,567,1,244,12,0),(2272,5061,1,244,11,0),(1073,734,1,237,1,0),(1076,225,1,272,1,0),(1083,101,1,265,20,0),(1084,102,1,265,35,0),(1085,103,1,265,52,0),(1086,446,1,265,54,0),(1087,447,1,265,56,0),(1088,448,1,265,58,0),(1089,735,1,265,54,0),(1090,736,1,265,56,0),(1091,737,1,265,58,0),(1092,7056,1,265,62,0),(1093,7057,1,265,64,0),(1094,7058,1,265,66,0),(1095,7059,1,265,67,0),(1096,7060,1,265,68,0),(1097,7061,1,265,69,0),(1100,7066,1,265,72,0),(1101,8263,1,262,5,0),(1102,8264,1,262,10,0),(1103,8265,1,262,15,0),(1104,8266,1,262,20,0),(1105,8267,1,262,25,0),(1106,8268,1,262,5,1),(1107,8269,1,262,10,1),(1108,8270,1,262,15,1),(1109,8271,1,262,20,1),(1110,8272,1,262,25,1),(1111,8273,1,262,5,2),(1112,8274,1,262,10,2),(1113,8275,1,262,15,2),(1114,8276,1,262,20,2),(1115,8277,1,262,25,2),(1116,8278,1,262,5,3),(1117,8279,1,262,10,3),(1118,8280,1,262,15,3),(1119,8281,1,262,20,3),(1120,8282,1,262,25,3),(1121,8283,1,262,5,4),(1122,8284,1,262,10,4),(1123,8285,1,262,15,4),(1124,8286,1,262,20,4),(1125,8287,1,262,25,4),(1126,8288,1,262,5,5),(1127,8289,1,262,10,5),(1128,8290,1,262,15,5),(1129,8291,1,262,20,5),(1130,8292,1,262,25,5),(1131,8293,1,262,5,6),(1132,8294,1,262,10,6),(1133,8295,1,262,15,6),(1134,8296,1,262,20,6),(1135,8297,1,262,25,6),(1136,4678,1,262,5,0),(1137,4679,1,262,10,0),(1138,4680,1,262,15,0),(1139,4681,1,262,20,0),(1140,4682,1,262,25,0),(1141,4678,2,262,5,1),(1142,4679,2,262,10,1),(1143,4680,2,262,15,1),(1144,4681,2,262,20,1),(1145,4682,2,262,25,1),(1146,4678,3,262,5,2),(1147,4679,3,262,10,2),(1148,4680,3,262,15,2),(1149,4681,3,262,20,2),(1150,4682,3,262,25,2),(1151,4678,4,262,5,3),(1152,4679,4,262,10,3),(1153,4680,4,262,15,3),(1154,4681,4,262,20,3),(1155,4682,4,262,25,3),(1156,4678,5,262,5,4),(1157,4679,5,262,10,4),(1158,4680,5,262,15,4),(1159,4681,5,262,20,4),(1160,4682,5,262,25,4),(1161,4678,6,262,5,5),(1162,4679,6,262,10,5),(1163,4680,6,262,15,5),(1164,4681,6,262,20,5),(1165,4682,6,262,25,5),(1166,4678,7,262,5,6),(1167,4679,7,262,10,6),(1168,4680,7,262,15,6),(1169,4681,7,262,20,6),(1170,4682,7,262,25,6),(1171,7547,1,262,5,0),(1172,7548,1,262,10,0),(1173,7549,1,262,15,0),(1174,7550,1,262,20,0),(1175,7551,1,262,25,0),(1176,7547,2,262,5,1),(1177,7548,2,262,10,1),(1178,7549,2,262,15,1),(1179,7550,2,262,20,1),(1180,7551,2,262,25,1),(1181,7547,3,262,5,2),(1182,7548,3,262,10,2),(1183,7549,3,262,15,2),(1184,7550,3,262,20,2),(1185,7551,3,262,25,2),(1186,7547,4,262,5,3),(1187,7548,4,262,10,3),(1188,7549,4,262,15,3),(1189,7550,4,262,20,3),(1190,7551,4,262,25,3),(1191,7537,5,262,5,4),(1192,7538,5,262,10,4),(1193,7539,5,262,15,4),(1194,7540,5,262,20,4),(1195,7541,5,262,25,4),(1196,7547,6,262,5,5),(1197,7548,6,262,10,5),(1198,7549,6,262,15,5),(1199,7550,6,262,20,5),(1200,7551,6,262,25,5),(1201,7547,7,262,5,6),(1202,7548,7,262,10,6),(1203,7549,7,262,15,6),(1204,7550,7,262,20,6),(1205,7551,7,262,25,6),(1212,12423,1,273,3,0),(1213,12424,1,273,6,0),(1214,12425,1,273,9,0),(1218,7647,1,229,5,0),(1219,7648,1,229,10,0),(1220,7649,1,229,15,0),(1221,7670,1,229,5,0),(1222,7671,1,229,10,0),(1223,7672,1,229,15,0),(1224,8224,1,339,10,16230),(1225,8224,2,138,1,0),(1226,8224,3,141,1,0),(1227,8224,4,142,60,0),(1228,8224,5,137,0,0),(1229,8224,6,311,0,0),(1230,8224,7,134,80,0),(1231,8224,8,139,-265,0),(1232,8224,9,139,-754,0),(1233,8224,10,139,-1332,0),(1234,8224,11,139,-1572,0),(1235,8224,12,139,-2749,0),(1236,8224,13,139,-4979,0),(1237,8224,14,139,-5418,0),(1238,8224,15,139,-5403,0),(1239,8225,1,339,10,16231),(1240,8225,2,138,1,0),(1241,8225,3,141,1,0),(1242,8225,4,142,65,0),(1243,8225,5,137,0,0),(1244,8225,6,311,0,0),(1245,8225,7,134,85,0),(1246,8225,8,139,-265,0),(1247,8225,9,139,-754,0),(1248,8225,10,139,-1332,0),(1249,8225,11,139,-1572,0),(1250,8225,12,139,-2749,0),(1251,8225,13,139,-4979,0),(1252,8225,14,139,-5418,0),(1253,8225,15,139,-5403,0),(1254,8226,1,339,10,16232),(1255,8226,2,138,1,0),(1256,8226,3,141,1,0),(1257,8226,4,142,70,0),(1258,8226,5,137,0,0),(1259,8226,6,311,0,0),(1260,8226,7,134,90,0),(1261,8226,8,139,-265,0),(1262,8226,9,139,-754,0),(1263,8226,10,139,-1332,0),(1264,8226,11,139,-1572,0),(1265,8226,12,139,-2749,0),(1266,8226,13,139,-4979,0),(1267,8226,14,139,-5418,0),(1268,8226,15,139,-5403,0),(1269,649,1,243,15,0),(1270,650,1,243,35,0),(1271,651,1,243,50,0),(1272,867,1,59,-3,0),(1273,868,1,59,-6,0),(1274,869,1,59,-9,0),(1275,870,1,59,-12,0),(1276,871,1,59,-15,0),(1283,1486,7,134,75,0),(1284,1486,8,139,-265,0),(1285,1486,9,139,-754,0),(1286,1486,10,139,-1332,0),(1287,1486,11,139,-1572,0),(1288,1486,12,139,-2749,0),(1289,1486,13,139,-4979,0),(1290,1486,14,139,-5418,0),(1291,1486,15,139,-5403,0),(1292,1486,16,348,10,0),(1299,1487,7,134,75,0),(1300,1487,8,139,-265,0),(1301,1487,9,139,-754,0),(1302,1487,10,139,-1332,0),(1303,1487,11,139,-1572,0),(1304,1487,12,139,-2749,0),(1305,1487,13,139,-4979,0),(1306,1487,14,139,-5418,0),(1307,1487,15,139,-5403,0),(1308,1487,16,348,10,0),(1315,1488,7,134,75,0),(1316,1488,8,139,-265,0),(1317,1488,9,139,-754,0),(1318,1488,10,139,-1332,0),(1319,1488,11,139,-1572,0),(1320,1488,12,139,-2749,0),(1321,1488,13,139,-4979,0),(1322,1488,14,139,-5418,0),(1323,1488,15,139,-5403,0),(1324,1488,16,348,10,0),(1331,1489,7,134,75,0),(1332,1489,8,139,-265,0),(1333,1489,9,139,-754,0),(1334,1489,10,139,-1332,0),(1335,1489,11,139,-1572,0),(1336,1489,12,139,-2749,0),(1337,1489,13,139,-4979,0),(1338,1489,14,139,-5418,0),(1339,1489,15,139,-5403,0),(1340,1489,16,348,10,0),(1347,1490,7,134,75,0),(1348,1490,8,139,-265,0),(1349,1490,9,139,-754,0),(1350,1490,10,139,-1332,0),(1351,1490,11,139,-1572,0),(1352,1490,12,139,-2749,0),(1353,1490,13,139,-4979,0),(1354,1490,14,139,-5418,0),(1355,1490,15,139,-5403,0),(1356,1490,16,348,10,0),(1357,5529,1,339,11,12680),(1358,5529,2,138,1,0),(1359,5529,3,141,1,0),(1360,5529,4,142,60,0),(1361,5529,5,137,0,0),(1362,5529,6,311,0,0),(1363,5529,7,134,75,0),(1364,5529,8,139,-265,0),(1365,5529,9,139,-754,0),(1366,5529,10,139,-1332,0),(1367,5529,11,139,-1572,0),(1368,5529,12,139,-2749,0),(1369,5529,13,139,-4979,0),(1370,5529,14,139,-5418,0),(1371,5529,15,139,-5403,0),(1372,5529,16,348,10,0),(1373,5530,1,339,13,12681),(1374,5530,2,138,1,0),(1375,5530,3,141,1,0),(1376,5530,4,142,60,0),(1377,5530,5,137,0,0),(1378,5530,6,311,0,0),(1379,5530,7,134,75,0),(1380,5530,8,139,-265,0),(1381,5530,9,139,-754,0),(1382,5530,10,139,-1332,0),(1383,5530,11,139,-1572,0),(1384,5530,12,139,-2749,0),(1385,5530,13,139,-4979,0),(1386,5530,14,139,-5418,0),(1387,5530,15,139,-5403,0),(1388,5530,16,348,10,0),(1389,5531,1,339,15,12682),(1390,5531,2,138,1,0),(1391,5531,3,141,1,0),(1392,5531,4,142,60,0),(1393,5531,5,137,0,0),(1394,5531,6,311,0,0),(1395,5531,7,134,75,0),(1396,5531,8,139,-265,0),(1397,5531,9,139,-754,0),(1398,5531,10,139,-1332,0),(1399,5531,11,139,-1572,0),(1400,5531,12,139,-2749,0),(1401,5531,13,139,-4979,0),(1402,5531,14,139,-5418,0),(1403,5531,15,139,-5403,0),(1404,5531,16,348,10,0),(2436,5532,16,348,10,0),(2435,5532,15,139,-5403,0),(2434,5532,14,139,-5418,0),(2433,5532,13,139,-4979,0),(2432,5532,12,139,-2749,0),(2431,5532,11,139,-1572,0),(2430,5532,10,139,-1332,0),(2429,5532,9,139,-754,0),(2428,5532,8,139,-265,0),(2427,5532,7,134,75,0),(2426,5532,6,311,0,0),(2425,5532,5,137,0,0),(2424,5532,4,142,60,0),(2423,5532,3,141,1,0),(2422,5532,2,138,1,0),(2421,5532,1,339,17,12683),(1421,5533,1,339,19,12684),(1422,5533,2,138,1,0),(1423,5533,3,141,1,0),(1424,5533,4,142,60,0),(1425,5533,5,137,0,0),(1426,5533,6,311,0,0),(1427,5533,7,134,75,0),(1428,5533,8,139,-265,0),(1429,5533,9,139,-754,0),(1430,5533,10,139,-1332,0),(1431,5533,11,139,-1572,0),(1432,5533,12,139,-2749,0),(1433,5533,13,139,-4979,0),(1434,5533,14,139,-5418,0),(1435,5533,15,139,-5403,0),(1436,5533,16,348,10,0),(1437,7554,1,339,21,16194),(1438,7554,2,138,1,0),(1439,7554,3,141,1,0),(1440,7554,4,142,60,0),(1441,7554,5,137,0,0),(1442,7554,6,311,0,0),(1443,7554,7,134,75,0),(1444,7554,8,139,-265,0),(1445,7554,9,139,-754,0),(1446,7554,10,139,-1332,0),(1447,7554,11,139,-1572,0),(1448,7554,12,139,-2749,0),(1449,7554,13,139,-4979,0),(1450,7554,14,139,-5418,0),(1451,7554,15,139,-5403,0),(1452,7554,16,348,10,0),(1453,7555,1,339,23,16195),(1454,7555,2,138,1,0),(1455,7555,3,141,1,0),(1456,7555,4,142,60,0),(1457,7555,5,137,0,0),(1458,7555,6,311,0,0),(1459,7555,7,134,75,0),(1460,7555,8,139,-265,0),(1461,7555,9,139,-754,0),(1462,7555,10,139,-1332,0),(1463,7555,11,139,-1572,0),(1464,7555,12,139,-2749,0),(1465,7555,13,139,-4979,0),(1466,7555,14,139,-5418,0),(1467,7555,15,139,-5403,0),(1468,7555,16,348,10,0),(1469,7556,1,339,25,16196),(1470,7556,2,138,1,0),(1471,7556,3,141,1,0),(1472,7556,4,142,60,0),(1473,7556,5,137,0,0),(1474,7556,6,311,0,0),(1475,7556,7,134,75,0),(1476,7556,8,139,-265,0),(1477,7556,9,139,-754,0),(1478,7556,10,139,-1332,0),(1479,7556,11,139,-1572,0),(1480,7556,12,139,-2749,0),(1481,7556,13,139,-4979,0),(1482,7556,14,139,-5418,0),(1483,7556,15,139,-5403,0),(1484,7556,16,348,10,0),(1485,7557,1,339,27,16417),(1486,7557,2,138,1,0),(1487,7557,3,141,1,0),(1488,7557,4,142,60,0),(1489,7557,5,137,0,0),(1490,7557,6,311,0,0),(1491,7557,7,134,75,0),(1492,7557,8,139,-265,0),(1493,7557,9,139,-754,0),(1494,7557,10,139,-1332,0),(1495,7557,11,139,-1572,0),(1496,7557,12,139,-2749,0),(1497,7557,13,139,-4979,0),(1498,7557,14,139,-5418,0),(1499,7557,15,139,-5403,0),(1500,7557,16,348,10,0),(1501,7558,1,339,29,16418),(1502,7558,2,138,1,0),(1503,7558,3,141,1,0),(1504,7558,4,142,60,0),(1505,7558,5,137,0,0),(1506,7558,6,311,0,0),(1507,7558,7,134,75,0),(1508,7558,8,139,-265,0),(1509,7558,9,139,-754,0),(1510,7558,10,139,-1332,0),(1511,7558,11,139,-1572,0),(1512,7558,12,139,-2749,0),(1513,7558,13,139,-4979,0),(1514,7558,14,139,-5418,0),(1515,7558,15,139,-5403,0),(1516,7558,16,348,10,0),(2486,4755,1,294,0,130),(2487,4756,1,294,0,135),(2488,4757,1,294,0,140),(1768,5557,1,341,10,0),(1769,5558,1,341,20,0),(1770,5559,1,341,30,0),(1526,7582,1,294,0,120),(1876,7583,1,114,-2,0),(1877,7584,1,114,-4,0),(2317,4779,1,274,2,0),(2318,4780,1,274,4,0),(2319,4781,1,274,6,0),(2320,5592,1,274,1,0),(2321,5593,1,274,2,0),(2322,5594,1,274,3,0),(2323,7590,1,274,1,0),(2324,7591,1,274,2,0),(2325,7592,1,274,3,0),(2326,12452,1,274,1,0),(2327,12453,1,274,2,0),(2328,12454,1,274,3,0),(1545,1435,5,348,10,0),(1546,1435,6,137,0,0),(1547,1435,7,339,3,8105),(1548,1435,8,142,65,0),(1549,1435,9,311,0,0),(1550,1435,10,134,70,0),(1551,1435,11,348,10,0),(1552,1435,12,137,100,0),(1553,1435,13,339,3,8105),(1554,1435,14,142,65,0),(1555,1435,15,311,0,0),(1556,1435,16,134,70,0),(1557,1435,17,348,10,0),(1558,1435,18,137,79,0),(1559,1435,19,339,3,8105),(1560,1435,20,142,65,0),(1561,1435,21,311,0,0),(1562,1435,22,134,70,0),(1563,1435,23,348,10,0),(1564,1435,24,137,149,0),(1569,1436,5,348,10,0),(1570,1436,6,137,0,0),(1571,1436,7,339,6,8105),(1572,1436,8,142,65,0),(1573,1436,9,311,0,0),(1574,1436,10,134,70,0),(1575,1436,11,348,10,0),(1576,1436,12,137,100,0),(1577,1436,13,339,6,8105),(1578,1436,14,142,65,0),(1579,1436,15,311,0,0),(1580,1436,16,134,70,0),(1581,1436,17,348,10,0),(1582,1436,18,137,79,0),(1583,1436,19,339,6,8105),(1584,1436,20,142,65,0),(1585,1436,21,311,0,0),(1586,1436,22,134,70,0),(1587,1436,23,348,10,0),(1588,1436,24,137,147,0),(1593,1437,5,348,10,0),(1594,1437,6,137,0,0),(1595,1437,7,339,10,8105),(1596,1437,8,142,65,0),(1597,1437,9,311,0,0),(1598,1437,10,134,70,0),(1599,1437,11,348,10,0),(1600,1437,12,137,100,0),(1601,1437,13,339,10,8105),(1602,1437,14,142,65,0),(1603,1437,15,311,0,0),(1604,1437,16,134,70,0),(1605,1437,17,348,10,0),(1606,1437,18,137,79,0),(1607,1437,19,339,10,8105),(1608,1437,20,142,65,0),(1609,1437,21,311,0,0),(1610,1437,22,134,70,0),(1611,1437,23,348,10,0),(1612,1437,24,137,147,0),(1613,4773,1,339,10,11404),(1614,4773,2,142,71,0),(1615,4773,3,311,0,0),(1616,4773,4,134,75,0),(1617,4773,5,348,10,0),(1618,4773,6,137,0,0),(1619,4773,7,339,10,11404),(1620,4773,8,142,71,0),(1621,4773,9,311,0,0),(1622,4773,10,134,75,0),(1623,4773,11,348,10,0),(1624,4773,12,137,100,0),(1625,4773,13,339,10,11404),(1626,4773,14,142,71,0),(1627,4773,15,311,0,0),(1628,4773,16,134,75,0),(1629,4773,17,348,10,0),(1630,4773,18,137,79,0),(1631,4773,19,339,10,11404),(1632,4773,20,142,71,0),(1633,4773,21,311,0,0),(1634,4773,22,134,75,0),(1635,4773,23,348,10,0),(1636,4773,24,137,149,0),(1637,6517,1,339,10,13199),(1638,6517,2,142,76,0),(1639,6517,3,311,0,0),(1640,6517,4,134,80,0),(1641,6517,5,348,10,0),(1642,6517,6,137,0,0),(1643,6517,7,339,10,13199),(1644,6517,8,142,76,0),(1645,6517,9,311,0,0),(1646,6517,10,134,80,0),(1647,6517,11,348,10,0),(1648,6517,12,137,100,0),(1649,6517,13,339,10,13199),(1650,6517,14,142,76,0),(1651,6517,15,311,0,0),(1652,6517,16,134,80,0),(1653,6517,17,348,10,0),(1654,6517,18,137,79,0),(1655,6517,19,339,10,13199),(1656,6517,20,142,76,0),(1657,6517,21,311,0,0),(1658,6517,22,134,80,0),(1659,6517,23,348,10,0),(1660,6517,24,137,149,0),(1661,7621,1,339,10,13830),(1662,7621,2,142,81,0),(1663,7621,3,311,0,0),(1664,7621,4,134,85,0),(1665,7621,5,348,10,0),(1666,7621,6,137,0,0),(1667,7621,7,339,10,13830),(1668,7621,8,142,81,0),(1669,7621,9,311,0,0),(1670,7621,10,134,85,0),(1671,7621,11,348,10,0),(1672,7621,12,137,100,0),(1673,7621,13,339,10,13830),(1674,7621,14,142,81,0),(1675,7621,15,311,0,0),(1676,7621,16,134,85,0),(1677,7621,17,348,10,0),(1678,7621,18,137,79,0),(1679,7621,19,339,10,13830),(1680,7621,20,142,81,0),(1681,7621,21,311,0,0),(1682,7621,22,134,85,0),(1683,7621,23,348,10,0),(1684,7621,24,137,149,0),(1806,8235,1,137,-40,0),(1812,8236,1,137,-40,0),(1687,8237,1,1,87,0),(1688,8238,1,1,116,0),(1689,8239,1,1,145,0),(1690,8305,1,1,174,0),(1691,8306,1,1,203,0),(1692,8307,1,1,232,0),(1693,8308,1,1,261,0),(1694,8309,1,1,290,0),(2517,120,1,278,500,32000),(2516,119,2,440,50,200),(2515,119,1,278,500,32000),(1701,440,1,278,550,50800),(1702,440,2,440,56,200),(1703,441,1,278,550,50800),(1704,441,2,440,58,200),(1705,442,1,278,550,50800),(1706,442,2,440,60,200),(1707,1053,1,278,600,65440),(1708,1053,2,440,61,200),(1709,1054,1,278,600,65440),(1710,1054,2,440,63,200),(1711,1055,1,278,600,65440),(1712,1055,2,440,65,200),(1713,4721,1,278,670,82600),(1714,4721,2,440,66,200),(1715,4722,1,278,670,82600),(1716,4722,2,440,68,200),(1717,4723,1,278,670,82600),(1718,4723,2,440,70,200),(1719,5554,1,278,750,102280),(1720,5554,2,440,71,200),(1721,5555,1,278,750,102280),(1722,5555,2,440,73,200),(1723,5556,1,278,750,102280),(1724,5556,2,440,75,200),(1725,7565,1,278,900,124480),(1726,7565,2,440,76,200),(1727,7566,1,278,900,124480),(1728,7566,2,440,78,200),(1729,7567,1,278,900,124480),(1730,7567,2,440,80,200),(1731,6023,1,278,900,132440),(1732,6023,2,440,82,200),(1733,6024,1,278,900,149200),(1734,6024,2,440,84,200),(1735,6025,1,278,900,149200),(1736,6025,2,440,86,200),(1737,4739,1,360,10,11023),(1738,4740,1,360,20,11023),(1739,4741,1,360,30,11023),(1740,5562,1,360,40,11023),(1741,5563,1,360,50,11023),(1742,5564,1,360,60,11023),(1743,7573,1,360,70,11023),(1744,7574,1,360,80,11023),(1745,7575,1,360,90,11023),(1746,8198,1,294,0,110),(1747,8199,1,294,0,120),(1748,8200,1,294,0,130),(1752,4733,1,294,11,100),(1753,4734,1,294,13,100),(1754,4735,1,294,15,100),(1755,7641,1,294,17,100),(1756,7642,1,294,19,100),(1757,7643,1,294,21,100),(1758,1592,1,341,10,0),(1759,1593,1,341,20,0),(1760,1594,1,341,30,0),(1761,1595,1,341,40,0),(1762,1596,1,341,50,0),(1763,4725,1,341,10,0),(1764,4726,1,341,20,0),(1765,4727,1,341,30,0),(1766,4728,1,341,40,0),(1767,4729,1,341,50,0),(1771,5560,1,341,40,0),(1772,5561,1,341,50,0),(1773,7568,1,341,10,0),(1774,7569,1,341,20,0),(1775,7570,1,341,30,0),(1776,7571,1,341,40,0),(1777,7572,1,341,50,0),(1778,12439,1,341,10,0),(1779,12440,1,341,20,0),(1780,12441,1,341,30,0),(1781,12442,1,341,40,0),(1782,12443,1,341,50,0),(2344,8232,1,128,5,5),(2350,8233,1,128,15,15),(2356,8234,1,128,25,25),(2362,8261,1,128,65,65),(1825,5534,1,266,1,0),(1827,5536,1,266,5,0),(1828,7659,1,266,1,0),(1829,7660,1,266,2,0),(1830,7661,1,266,3,0),(1831,1181,1,305,-20,0),(1832,1182,1,305,-40,0),(1833,1183,1,305,-60,0),(1834,1184,1,305,-80,0),(1835,1185,1,305,-100,0),(1836,1093,1,304,-20,0),(1837,1094,1,304,-40,0),(1838,1095,1,304,-60,0),(1839,1096,1,304,-80,0),(1840,1097,1,304,-100,0),(1841,8215,1,190,50,0),(1842,8216,1,190,100,0),(1843,8217,1,190,150,0),(1844,8218,1,190,200,0),(1845,8219,1,190,250,0),(1846,1036,1,189,4,0),(1847,1037,1,189,5,0),(1848,1039,1,189,6,0),(1849,5130,1,189,7,0),(1850,5131,1,189,8,0),(1851,5132,1,189,9,0),(1852,5133,1,189,10,0),(1853,5134,1,189,11,0),(1854,6080,1,189,12,0),(1855,6081,1,189,13,0),(1856,7604,1,189,14,0),(1857,7605,1,189,15,0),(1858,5516,1,320,1,0),(1859,5517,1,320,2,0),(1860,5518,1,320,3,0),(1861,12469,1,320,1,0),(1862,12470,1,320,2,0),(1863,12471,1,320,3,0),(1864,98,1,114,-4,0),(1865,99,1,114,-12,0),(1866,100,1,114,-20,0),(1867,738,1,114,-4,0),(1868,739,1,114,-12,0),(1869,740,1,114,-20,0),(1870,5317,1,114,-3,0),(1871,5318,1,114,-6,0),(1872,5319,1,114,-9,0),(1873,5628,1,114,-3,0),(1874,5629,1,114,-4,0),(1875,5630,1,114,-5,0),(1878,7585,1,114,-6,0),(1879,634,1,274,3,0),(1880,635,1,274,6,0),(1881,636,1,274,10,0),(1882,844,1,274,3,0),(1883,845,1,274,6,0),(1884,1319,1,274,2,0),(1885,1320,1,274,4,0),(1886,1321,1,274,6,0),(1887,210,1,302,50,50),(1888,210,2,385,99,0),(1889,211,1,302,100,100),(1890,211,2,385,99,0),(1891,212,1,302,200,200),(1892,212,2,385,99,0),(1918,164,5,127,10,10),(1919,164,6,137,82,0),(1920,164,7,127,2,2),(1921,164,8,137,152,0),(1922,164,9,143,3000,0),(1927,165,5,127,25,25),(1928,165,6,137,82,0),(1929,165,7,127,4,4),(1930,165,8,137,152,0),(1931,165,9,143,3000,0),(1936,166,5,127,50,50),(1937,166,6,137,82,0),(1938,166,7,127,6,6),(1939,166,8,137,152,0),(1940,166,9,143,3000,0),(1967,77,3,141,1,0),(1968,77,4,139,-6232,0),(1969,77,5,139,-6264,0),(1970,77,6,125,2,2),(1971,77,7,137,147,0),(1972,77,8,141,1,0),(1975,78,3,141,1,0),(1976,78,4,139,-6232,0),(1977,78,5,139,-6264,0),(1978,78,6,125,5,5),(1979,78,7,137,147,0),(1980,78,8,141,1,0),(1983,79,3,141,1,0),(1984,79,4,139,-6232,0),(1985,79,5,139,-6264,0),(1986,79,6,125,10,10),(1987,79,7,137,147,0),(1988,79,8,141,1,0),(1991,434,3,141,1,0),(1992,434,4,139,-6232,0),(1993,434,5,139,-6264,0),(1994,434,6,125,13,13),(1995,434,7,137,147,0),(1996,434,8,141,1,0),(1999,435,3,141,1,0),(2000,435,4,139,-6232,0),(2001,435,5,139,-6264,0),(2002,435,6,125,16,16),(2003,435,7,137,147,0),(2004,435,8,141,1,0),(2007,436,3,141,1,0),(2008,436,4,139,-6232,0),(2009,436,5,139,-6264,0),(2010,436,6,125,19,19),(2011,436,7,137,147,0),(2012,436,8,141,1,0),(2013,1083,1,125,22,13),(2014,1083,2,137,0,0),(2015,1083,3,141,1,0),(2016,1083,4,139,-6232,0),(2017,1083,5,139,-6264,0),(2018,1083,6,125,22,22),(2019,1083,7,137,147,0),(2020,1083,8,141,1,0),(2021,1084,1,125,25,16),(2022,1084,2,137,0,0),(2023,1084,3,141,1,0),(2024,1084,4,139,-6232,0),(2025,1084,5,139,-6264,0),(2026,1084,6,125,25,25),(2027,1084,7,137,147,0),(2028,1084,8,141,1,0),(2029,1085,1,125,28,19),(2030,1085,2,137,0,0),(2031,1085,3,141,1,0),(2032,1085,4,139,-6232,0),(2033,1085,5,139,-6264,0),(2034,1085,6,125,28,28),(2035,1085,7,137,147,0),(2036,1085,8,141,1,0),(2037,12449,1,125,31,31),(2038,12449,2,137,0,0),(2039,12449,3,141,1,0),(2040,12449,4,139,-6232,0),(2041,12449,5,139,-6264,0),(2042,12449,6,125,31,31),(2043,12449,7,137,147,0),(2044,12449,8,141,1,0),(2045,12450,1,125,34,34),(2046,12450,2,137,0,0),(2047,12450,3,141,1,0),(2048,12450,4,139,-6232,0),(2049,12450,5,139,-6264,0),(2050,12450,6,125,33,33),(2051,12450,7,137,147,0),(2052,12450,8,141,1,0),(2053,12451,1,125,37,37),(2054,12451,2,137,0,0),(2055,12451,3,141,1,0),(2056,12451,4,139,-6232,0),(2057,12451,5,139,-6264,0),(2058,12451,6,125,37,37),(2059,12451,7,137,147,0),(2060,12451,8,141,1,0),(2084,7501,1,172,1,0),(2085,7502,1,172,2,0),(2086,7503,1,172,3,0),(2087,7504,1,172,4,0),(2088,7505,1,172,5,0),(2089,12396,1,172,1,0),(2090,12397,1,172,2,0),(2091,12398,1,172,3,0),(2092,12399,1,172,4,0),(2093,12400,1,172,5,0),(2117,7506,1,259,1,0),(2118,7507,1,259,2,0),(2119,7508,1,259,3,0),(2120,7509,1,259,4,0),(2121,7510,1,259,5,0),(2122,12401,1,259,1,0),(2123,12402,1,259,2,0),(2124,12403,1,259,3,0),(2125,12404,1,259,4,0),(2126,12405,1,259,5,0),(2127,1389,1,328,50,0),(2128,1390,1,328,100,0),(2129,1391,1,328,150,0),(2130,1392,1,328,200,0),(2131,1393,1,328,250,0),(2132,4683,1,328,50,0),(2133,4684,1,328,100,0),(2134,4685,1,328,150,0),(2135,4686,1,328,200,0),(2136,4687,1,328,250,0),(2137,6523,1,328,50,0),(2138,6524,1,328,100,0),(2139,6525,1,328,150,0),(2140,6526,1,328,200,0),(2141,6527,1,328,250,0),(2142,7511,1,328,50,0),(2143,7512,1,328,100,0),(2144,7513,1,328,150,0),(2145,7514,1,328,200,0),(2146,7515,1,328,250,0),(2147,1056,1,317,1,0),(2148,1057,1,317,2,0),(2149,1058,1,317,3,0),(2150,1059,1,317,4,0),(2151,1060,1,317,5,0),(2157,6431,1,317,1,0),(2158,6432,1,317,2,0),(2159,6433,1,317,3,0),(2160,6434,1,317,4,0),(2161,6435,1,317,5,0),(2329,6119,1,69,500,0),(2343,7526,1,317,1,0),(2172,12406,1,69,500,0),(2173,12407,1,69,500,0),(2174,12408,1,69,500,0),(2175,12409,1,69,500,0),(2176,12410,1,69,500,0),(2177,7516,1,262,10,7),(2178,7516,2,262,10,8),(2179,7516,3,262,10,9),(2180,7516,4,262,10,10),(2181,7516,5,262,10,11),(2182,7517,1,262,20,7),(2183,7517,2,262,20,8),(2184,7517,3,262,20,9),(2185,7517,4,262,20,10),(2186,7517,5,262,20,11),(2187,7518,1,262,30,7),(2188,7518,2,262,30,8),(2189,7518,3,262,30,9),(2190,7518,4,262,30,10),(2191,7518,5,262,30,11),(2192,7519,1,262,40,7),(2193,7519,2,262,40,8),(2194,7519,3,262,40,9),(2195,7519,4,262,40,10),(2196,7519,5,262,40,11),(2197,7520,1,262,50,7),(2198,7520,2,262,50,8),(2199,7520,3,262,50,9),(2200,7520,4,262,50,10),(2201,7520,5,262,50,11),(2202,6521,1,327,1,0),(2203,6522,1,327,2,0),(2204,7539,1,327,1,0),(2205,7540,1,327,2,0),(2206,7541,1,327,3,0),(2207,7542,1,327,4,0),(2208,7543,1,327,5,0),(2209,5045,1,180,5,0),(2210,8228,1,378,5,22),(2211,8229,1,378,10,22),(2212,8230,1,378,15,22),(2213,7050,1,265,62,0),(2214,7051,1,265,64,0),(2215,7052,1,265,66,0),(2216,7053,1,265,67,0),(2217,7054,1,265,68,0),(2218,7055,1,265,69,0),(2219,7063,1,265,70,0),(2220,7064,1,265,71,0),(2221,7065,1,265,72,0),(2222,7622,1,265,74,0),(2223,7623,1,265,76,0),(2224,7624,1,265,79,0),(2300,581,9,254,0,0),(2301,581,10,254,0,0),(2302,581,11,254,0,0),(2303,582,1,267,1,4),(2304,582,2,267,1,8),(2305,582,3,267,1,5),(2306,582,4,267,1,2),(2307,582,5,267,1,7),(2308,582,6,267,1,6),(2309,582,7,267,1,9),(2310,582,8,267,1,12),(2311,582,9,267,1,11),(2312,582,10,267,1,28),(2313,582,11,267,1,1),(2314,1086,1,274,2,0),(2315,1087,1,274,4,0),(2316,1088,1,274,6,0),(2330,6120,1,69,500,0),(2331,6121,1,69,500,0),(2332,6122,1,69,500,0),(2333,6123,1,69,500,0),(2334,7527,1,69,500,0),(2335,7528,1,69,500,0),(2336,7529,1,69,500,0),(2337,7530,1,69,500,0),(2338,7531,1,69,500,0),(2339,7522,1,317,1,0),(2340,7523,1,317,1,0),(2341,7524,1,317,1,0),(2342,7525,1,317,1,0),(2345,8232,2,138,1,0),(2346,8232,3,140,1,0),(2347,8232,4,311,0,0),(2348,8232,5,411,66434,0),(2349,8232,6,137,-40,0),(2351,8233,2,138,1,0),(2352,8233,3,140,1,0),(2353,8233,4,311,0,0),(2354,8233,5,411,66434,0),(2355,8233,6,137,-40,0),(2357,8234,2,138,1,0),(2358,8234,3,140,1,0),(2359,8234,4,311,0,0),(2360,8234,5,411,66434,0),(2361,8234,6,137,-40,0),(2363,8261,2,138,1,0),(2364,8261,3,140,1,0),(2365,8261,4,311,0,0),(2366,8261,5,411,66434,0),(2367,8261,6,137,-40,0),(2368,1230,1,313,15,0),(2369,1231,1,313,20,0),(2370,1232,1,313,25,0),(2371,5000,1,313,50,0),(2372,5001,1,313,75,0),(2373,5002,1,313,100,0),(2374,997,1,331,5,0),(2375,998,1,331,15,0),(2376,999,1,331,25,0),(2377,226,1,272,2,0),(2378,227,1,272,3,0),(2379,275,1,260,2,50),(2380,276,1,260,4,50),(2381,277,1,260,6,50),(2382,701,1,260,8,50),(2383,213,1,260,2,23),(2384,213,2,260,2,24),(2385,213,3,260,2,25),(2386,213,4,260,2,26),(2387,214,1,260,4,23),(2388,214,2,260,4,24),(2389,214,3,260,4,25),(2390,214,4,260,4,26),(2391,215,1,260,6,23),(2392,215,2,260,6,24),(2393,215,3,260,6,25),(2394,215,4,260,6,26),(2395,700,1,260,2,23),(2396,700,2,260,2,24),(2397,700,3,260,2,25),(2398,700,4,260,2,26),(2399,695,1,247,10,53),(2400,696,1,247,20,53),(2401,697,1,247,30,53),(2402,698,1,247,40,53),(2403,699,1,247,50,53),(2404,762,1,247,10,53),(2405,764,1,247,30,53),(2406,763,1,247,20,53),(2407,765,1,247,40,53),(2408,766,1,247,50,53),(2409,412,1,263,1,0),(2410,413,1,263,2,0),(2411,414,1,263,3,0),(2412,415,1,263,4,0),(2413,416,1,263,5,0),(2414,417,1,263,6,0),(2415,68,1,233,110,0),(2416,69,1,233,125,0),(2417,70,1,233,150,0),(2418,5535,1,266,3,0),(228,367,1,46,12,0),(2419,858,1,220,50,10),(2420,859,1,220,75,10),(2437,606,1,285,20,0),(2438,607,1,285,40,0),(2439,608,1,285,60,0),(2440,609,1,285,80,0),(2441,610,1,285,100,0),(2442,895,1,59,-3,0),(2443,896,1,59,-6,0),(2444,897,1,59,-9,0),(2445,898,1,59,-12,0),(2446,899,1,59,-15,0),(2447,571,1,261,1,0),(2448,572,1,261,2,0),(2449,573,1,261,3,0),(2450,707,1,261,1,0),(2451,708,1,261,2,0),(2452,709,1,261,3,0),(2453,86,1,128,5,5),(2454,86,2,138,1,0),(2455,86,3,140,1,0),(2456,86,4,139,-2741,0),(2457,86,5,139,-16843,0),(2458,86,6,385,-16192,0),(2459,87,1,128,15,15),(2460,87,2,138,1,0),(2461,87,3,140,1,0),(2462,87,4,139,-2741,0),(2463,87,5,139,-16843,0),(2464,87,6,385,-16192,0),(2465,88,1,128,30,30),(2466,88,2,138,1,0),(2467,88,3,140,1,0),(2468,88,4,139,-2741,0),(2469,88,5,139,-16843,0),(2470,88,6,385,-16192,0),(2471,266,1,128,50,50),(2472,266,2,138,1,0),(2473,266,3,140,1,0),(2474,266,4,139,-2741,0),(2475,266,5,139,-16843,0),(2476,266,6,385,-16192,0),(2477,781,1,287,1,1),(2478,781,2,137,31,0),(2479,781,3,136,5,0),(2480,230,1,275,10,0),(2481,231,1,275,25,0),(2482,232,1,275,50,0),(2483,539,1,275,15,0),(2484,540,1,275,25,0),(2485,541,1,275,35,0),(2489,589,1,303,200,0),(2490,589,2,139,2766,0),(2492,590,2,139,2766,0),(2494,591,2,139,2766,0),(2495,893,1,303,800,0),(2496,893,2,139,2766,0),(2497,894,1,303,1000,0),(2498,894,2,139,2766,0),(2499,560,1,225,15,0),(2500,561,1,177,3,0),(2501,888,1,250,10,0),(2502,889,1,250,20,0),(2503,890,1,250,30,0),(2504,891,1,250,40,0),(2505,892,1,250,50,0),(2506,180,1,241,95,0),(2507,644,1,217,0,32000),(2508,644,2,346,46,0),(2509,1604,1,439,0,32000),(2510,1604,2,345,48,0),(2511,1605,1,439,0,32000),(2512,1605,2,345,51,0),(2513,1606,1,439,0,32000),(2514,1606,2,345,53,0),(2518,120,2,440,52,200),(2519,121,1,278,500,32000),(2520,121,2,440,54,200),(2521,158,1,238,1,0),(2522,818,1,279,17,0),(2523,819,1,279,19,0),(2524,691,1,248,100,0),(2525,4698,1,362,1,0),(2526,6545,1,362,1,0); + +DROP TABLE IF EXISTS `aa_required_level_cost`; +CREATE TABLE `aa_required_level_cost` ( + `skill_id` int(10) unsigned NOT NULL, + `level` int(10) unsigned NOT NULL, + `cost` int(10) unsigned NOT NULL DEFAULT '0', + `description` varchar(64) DEFAULT NULL COMMENT 'For reference only', + PRIMARY KEY (`skill_id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; +INSERT INTO `aa_required_level_cost` VALUES (7800,1,0,'Harm Touch'),(7801,6,0,'Harm Touch'),(7802,11,0,'Harm Touch'),(7803,16,0,'Harm Touch'),(7804,21,0,'Harm Touch'),(7805,26,0,'Harm Touch'),(7806,31,0,'Harm Touch'),(7807,36,0,'Harm Touch'),(7808,41,0,'Harm Touch'),(7809,46,0,'Harm Touch'),(7810,51,3,'Harm Touch'),(7811,56,4,'Harm Touch'),(7812,61,5,'Harm Touch'),(7813,66,6,'Harm Touch'),(7814,71,7,'Harm Touch'),(7815,76,8,'Harm Touch'),(7816,81,8,'Harm Touch'),(7817,83,9,'Harm Touch'),(7850,5,0,'Lay on Hands'),(7851,11,0,'Lay on Hands'),(7852,16,0,'Lay on Hands'),(7853,21,0,'Lay on Hands'),(7854,26,0,'Lay on Hands'),(7855,31,0,'Lay on Hands'),(7856,36,0,'Lay on Hands'),(7857,41,0,'Lay on Hands'),(7858,46,0,'Lay on Hands'),(7859,51,0,'Lay on Hands'),(7860,56,3,'Lay on Hands'),(7861,61,4,'Lay on Hands'),(7862,66,5,'Lay on Hands'),(7863,71,6,'Lay on Hands'),(7864,76,7,'Lay on Hands'),(7865,81,8,'Lay on Hands'),(7866,85,9,'Lay on Hands'),(593,61,2,'Fervent Blessing'),(594,63,4,'Fervent Blessing'),(595,65,6,'Fervent Blessing'),(292,61,1,'Advanced Innate Strength'),(293,61,1,'Advanced Innate Strength'),(294,62,1,'Advanced Innate Strength'),(295,62,1,'Advanced Innate Strength'),(296,63,1,'Advanced Innate Strength'),(297,63,1,'Advanced Innate Strength'),(298,64,1,'Advanced Innate Strength'),(299,64,1,'Advanced Innate Strength'),(300,65,1,'Advanced Innate Strength'),(301,65,1,'Advanced Innate Strength'),(302,61,1,'Advanced Innate Stamina'),(303,61,1,'Advanced Innate Stamina'),(304,62,1,'Advanced Innate Stamina'),(305,62,1,'Advanced Innate Stamina'),(306,63,1,'Advanced Innate Stamina'),(307,63,1,'Advanced Innate Stamina'),(308,64,1,'Advanced Innate Stamina'),(309,64,1,'Advanced Innate Stamina'),(310,65,1,'Advanced Innate Stamina'),(311,65,1,'Advanced Innate Stamina'),(312,61,1,'Advanced Innate Agility'),(313,61,1,'Advanced Innate Agility'),(314,62,1,'Advanced Innate Agility'),(315,62,1,'Advanced Innate Agility'),(316,63,1,'Advanced Innate Agility'),(317,63,1,'Advanced Innate Agility'),(318,64,1,'Advanced Innate Agility'),(319,64,1,'Advanced Innate Agility'),(320,65,1,'Advanced Innate Agility'),(321,65,1,'Advanced Innate Agility'),(322,61,1,'Advanced Innate Dexterity'),(323,61,1,'Advanced Innate Dexterity'),(324,62,1,'Advanced Innate Dexterity'),(325,62,1,'Advanced Innate Dexterity'),(326,63,1,'Advanced Innate Dexterity'),(327,63,1,'Advanced Innate Dexterity'),(328,64,1,'Advanced Innate Dexterity'),(329,64,1,'Advanced Innate Dexterity'),(330,65,1,'Advanced Innate Dexterity'),(331,65,1,'Advanced Innate Dexterity'),(332,61,1,'Advanced Innate Intelligence'),(333,61,1,'Advanced Innate Intelligence'),(334,62,1,'Advanced Innate Intelligence'),(335,62,1,'Advanced Innate Intelligence'),(336,63,1,'Advanced Innate Intelligence'),(337,63,1,'Advanced Innate Intelligence'),(338,64,1,'Advanced Innate Intelligence'),(339,64,1,'Advanced Innate Intelligence'),(340,65,1,'Advanced Innate Intelligence'),(341,65,1,'Advanced Innate Intelligence'),(342,61,1,'Advanced Innate Wisdom'),(343,61,1,'Advanced Innate Wisdom'),(344,62,1,'Advanced Innate Wisdom'),(345,62,1,'Advanced Innate Wisdom'),(346,63,1,'Advanced Innate Wisdom'),(347,63,1,'Advanced Innate Wisdom'),(348,64,1,'Advanced Innate Wisdom'),(349,64,1,'Advanced Innate Wisdom'),(350,65,1,'Advanced Innate Wisdom'),(351,65,1,'Advanced Innate Wisdom'),(352,61,1,'Advanced Innate Charisma'),(353,61,1,'Advanced Innate Charisma'),(354,62,1,'Advanced Innate Charisma'),(355,62,1,'Advanced Innate Charisma'),(356,63,1,'Advanced Innate Charisma'),(357,63,1,'Advanced Innate Charisma'),(358,64,1,'Advanced Innate Charisma'),(359,64,1,'Advanced Innate Charisma'),(360,65,1,'Advanced Innate Charisma'),(361,65,1,'Advanced Innate Charisma'),(362,61,1,'Warding of Solusek'),(363,61,1,'Warding of Solusek'),(364,62,1,'Warding of Solusek'),(365,62,1,'Warding of Solusek'),(366,63,1,'Warding of Solusek'),(367,63,1,'Warding of Solusek'),(368,64,1,'Warding of Solusek'),(369,64,1,'Warding of Solusek'),(370,65,1,'Warding of Solusek'),(371,65,1,'Warding of Solusek'),(372,61,1,'Blessing of E\'ci'),(373,61,1,'Blessing of E\'ci'),(374,62,1,'Blessing of E\'ci'),(375,62,1,'Blessing of E\'ci'),(376,63,1,'Blessing of E\'ci'),(377,63,1,'Blessing of E\'ci'),(378,64,1,'Blessing of E\'ci'),(379,64,1,'Blessing of E\'ci'),(380,65,1,'Blessing of E\'ci'),(381,65,1,'Blessing of E\'ci'),(382,61,1,'Marr\'s Protection'),(383,61,1,'Marr\'s Protection'),(384,62,1,'Marr\'s Protection'),(385,62,1,'Marr\'s Protection'),(386,63,1,'Marr\'s Protection'),(387,63,1,'Marr\'s Protection'),(388,64,1,'Marr\'s Protection'),(389,64,1,'Marr\'s Protection'),(390,65,1,'Marr\'s Protection'),(391,65,1,'Marr\'s Protection'),(392,61,1,'Shroud of the Faceless'),(393,61,1,'Shroud of the Faceless'),(394,62,1,'Shroud of the Faceless'),(395,62,1,'Shroud of the Faceless'),(396,63,1,'Shroud of the Faceless'),(397,63,1,'Shroud of the Faceless'),(398,64,1,'Shroud of the Faceless'),(399,64,1,'Shroud of the Faceless'),(400,65,1,'Shroud of the Faceless'),(401,65,1,'Shroud of the Faceless'),(402,61,1,'Bertoxxulous\' Gift'),(403,61,1,'Bertoxxulous\' Gift'),(404,62,1,'Bertoxxulous\' Gift'),(405,62,1,'Bertoxxulous\' Gift'),(406,63,1,'Bertoxxulous\' Gift'),(407,63,1,'Bertoxxulous\' Gift'),(408,64,1,'Bertoxxulous\' Gift'),(409,64,1,'Bertoxxulous\' Gift'),(410,65,1,'Bertoxxulous\' Gift'),(411,65,1,'Bertoxxulous\' Gift'),(418,61,2,'Planar Power'),(419,62,2,'Planar Power'),(420,63,2,'Planar Power'),(421,64,2,'Planar Power'),(422,65,2,'Planar Power'),(423,61,3,'Planar Durability'),(424,63,3,'Planar Durability'),(425,65,3,'Planar Durability'),(426,61,3,'Innate Enlightenment'),(427,62,3,'Innate Enlightenment'),(428,63,3,'Innate Enlightenment'),(429,64,3,'Innate Enlightenment'),(430,65,3,'Innate Enlightenment'),(434,62,2,'Advanced Healing Adept'),(435,63,4,'Advanced Healing Adept'),(436,64,6,'Advanced Healing Adept'),(437,62,2,'Advanced Healing Gift'),(438,63,4,'Advanced Healing Gift'),(439,64,6,'Advanced Healing Gift'),(440,62,2,'Coup de Grace'),(441,63,2,'Coup de Grace'),(442,64,2,'Coup de Grace'),(443,62,3,'Fury of the Ages'),(444,63,3,'Fury of the Ages'),(445,64,3,'Fury of the Ages'),(446,62,3,'Mastery of the Past'),(447,63,3,'Mastery of the Past'),(448,64,3,'Mastery of the Past'),(449,61,3,'Lightning Reflexes'),(7539,80,5,'Mystical Attuning III'),(1025,65,5,'Mystical Attuning'),(1024,65,5,'Mystical Attuning'),(1023,60,5,'Mystical Attuning'),(7540,85,5,'Mystical Attuning III'),(7541,85,5,'Mystical Attuning III'),(459,61,2,'Radiant Cure'),(460,63,4,'Radiant Cure'),(461,65,6,'Radiant Cure'),(462,63,2,'Hastened Divinity'),(463,64,2,'Hastened Divinity'),(464,65,2,'Hastened Divinity'),(468,63,2,'Hastened Purification of the Soul'),(469,64,2,'Hastened Purification of the Soul'),(470,65,2,'Hastened Purification of the Soul'),(471,63,2,'Hastened Gathering'),(472,64,2,'Hastened Gathering'),(473,65,2,'Hastened Gathering'),(474,63,2,'Hastened Rabidity'),(475,64,2,'Hastened Rabidity'),(476,65,2,'Hastened Rabidity'),(477,63,2,'Hastened Exodus'),(478,64,2,'Hastened Exodus'),(479,65,2,'Hastened Exodus'),(480,63,2,'Hastened Root'),(481,64,2,'Hastened Root'),(482,65,2,'Hastened Root'),(483,63,2,'Hastened Mending'),(484,64,2,'Hastened Mending'),(485,65,2,'Hastened Mending'),(486,63,2,'Hastened Banishment'),(487,64,4,'Hastened Banishment'),(488,65,6,'Hastened Banishment'),(489,63,2,'Hastened Instigation'),(490,64,2,'Hastened Instigation'),(491,65,2,'Hastened Instigation'),(492,63,2,'Furious Rampage'),(493,64,2,'Furious Rampage'),(494,65,2,'Furious Rampage'),(495,63,2,'Hastened Purification of the Body'),(496,64,2,'Hastened Purification of the Body'),(497,65,2,'Hastened Purification of the Body'),(498,63,2,'Hasty Exit'),(499,64,2,'Hasty Exit'),(500,65,2,'Hasty Exit'),(501,63,2,'Hastened Purification'),(502,64,2,'Hastened Purification'),(503,65,2,'Hastened Purification'),(504,62,3,'Flash of Steel'),(505,63,3,'Flash of Steel'),(506,64,3,'Flash of Steel'),(507,61,3,'Divine Arbitration'),(508,63,3,'Divine Arbitration'),(509,65,3,'Divine Arbitration'),(510,61,3,'Wrath of the Wild'),(511,63,3,'Wrath of the Wild'),(512,65,3,'Wrath of the Wild'),(513,61,3,'Virulent Paralysis'),(514,63,3,'Virulent Paralysis'),(515,65,3,'Virulent Paralysis'),(517,61,3,'Eldritch Rune'),(518,63,3,'Eldritch Rune'),(519,65,3,'Eldritch Rune'),(520,61,3,'Servant of Ro'),(521,63,3,'Servant of Ro'),(522,65,3,'Servant of Ro'),(523,61,3,'Wake the Dead'),(524,63,2,'Wake the Dead'),(525,65,1,'Wake the Dead'),(526,62,5,'Suspended Minion'),(527,64,3,'Suspended Minion'),(528,61,4,'Spirit Call'),(529,63,3,'Spirit Call'),(530,65,2,'Spirit Call'),(531,63,3,'Celestial Renewal'),(532,64,6,'Celestial Renewal'),(534,61,3,'Hand of Piety'),(535,63,3,'Hand of Piety'),(536,65,3,'Hand of Piety'),(537,63,3,'Mithaniel\'s Binding'),(538,64,3,'Mithaniel\'s Binding'),(539,63,2,'Mending of the Tranquil'),(540,64,4,'Mending of the Tranquil'),(541,65,6,'Mending of the Tranquil'),(542,63,2,'Raging Flurry'),(543,64,4,'Raging Flurry'),(544,65,6,'Raging Flurry'),(545,61,3,'Guardian of the Forest'),(546,63,3,'Guardian of the Forest'),(547,65,3,'Guradian of the Forest'),(548,61,4,'Spirit of the Wood'),(549,63,3,'Spirit of the Wood'),(550,65,2,'Spirit of the Wood'),(551,61,2,'Bestial Frenzy'),(552,62,2,'Bestial Frenzy'),(553,63,2,'Bestial Frenzy'),(554,64,2,'Bestial Frenzy'),(555,65,2,'Bestial Frenzy'),(556,61,2,'Harmonious Attack'),(557,62,2,'Harmonious Attack'),(558,63,2,'Harmonious Attack'),(559,64,2,'Harmonious Attack'),(560,65,2,'Harmonious Attack'),(561,61,2,'Knight\'s Advantage'),(562,63,4,'Knight\'s Advantage'),(563,65,6,'Knight\'s Advantage'),(564,61,3,'Ferocity'),(565,63,3,'Ferocity'),(566,65,3,'Ferocity'),(568,63,2,'Sionachie\'s Crescendo'),(569,64,2,'Sionachie\'s Crescendo'),(570,65,2,'Sionachie\'s Crescendo'),(571,63,2,'Ayonae\'s Tutelage'),(572,64,2,'Ayonae\'s Tutelage'),(573,65,2,'Ayonae\'s Tutelage'),(574,61,3,'Feigned Minion'),(575,63,3,'Feigned Minion'),(576,65,3,'Feigned Minion'),(577,61,2,'Unfailing Divinity'),(578,63,4,'Unfailing Divinity'),(579,65,6,'Unfailing Divinity'),(580,61,4,'Animation Empathy'),(581,63,3,'Animation Empathy'),(582,65,2,'Animation Empathy'),(583,63,2,'Rush to Judgment'),(584,64,2,'Rush to Judgment'),(585,65,2,'Rush to Judgment'),(586,61,2,'Living Shield'),(587,63,4,'Living Shield'),(588,65,6,'Living Shield'),(589,61,3,'Consumption of the Soul'),(590,63,3,'Consumption of the Soul'),(591,65,3,'Consumption of the Soul'),(596,61,2,'Touch of the Wicked'),(597,63,2,'Touch of the Wicked'),(598,65,2,'Touch of the Wicked'),(599,61,2,'Punishing Blade'),(600,63,4,'Punishing Blade'),(601,65,6,'Punishing Blade'),(602,61,3,'Speed of the Knight'),(603,63,3,'Speed of the Knight'),(604,65,3,'Speed of the Knight'),(606,61,1,'Nimble Evasion'),(607,62,1,'Nimble Evasion'),(608,63,1,'Nimble Evasion'),(609,64,1,'Nimble Evasion'),(610,65,1,'Nimble Evasion'),(616,63,5,'Host of the Elements'),(617,64,4,'Host of the Elements'),(618,65,3,'Host of the Elements'),(619,61,3,'Call of Xuzl'),(620,63,2,'Call of Xuzl'),(621,65,1,'Call of Xuzl'),(622,61,3,'Hastened Stealth'),(623,62,3,'Hastened Stealth'),(624,63,3,'Hastened Stealth'),(625,61,1,'Ingenuity'),(626,63,2,'Ingenuity'),(627,65,3,'Ingenuity'),(628,62,2,'Fleet of Foot'),(629,64,4,'Fleet of Foot'),(631,61,2,'Tactical Mastery'),(632,63,3,'Tactical Mastery'),(633,65,4,'Tactical Mastery'),(646,61,2,'Unholy Touch'),(647,63,4,'Unholy Touch'),(648,65,6,'Unholy Touch'),(652,61,2,'Stalwart Endurance'),(1022,55,5,'Mystical Attuning'),(1021,51,5,'Mystical Attuning'),(7542,85,5,'Mystical Attuning III'),(7543,85,5,'Mystical Attuning III'); + +DROP TABLE IF EXISTS `altadv_vars`; +CREATE TABLE `altadv_vars` ( + `skill_id` int(11) NOT NULL DEFAULT '0', + `name` varchar(128) DEFAULT NULL, + `cost` int(11) DEFAULT NULL, + `max_level` int(11) DEFAULT NULL, + `hotkey_sid` int(10) unsigned NOT NULL DEFAULT '0', + `hotkey_sid2` int(10) unsigned NOT NULL DEFAULT '0', + `title_sid` int(10) unsigned NOT NULL DEFAULT '0', + `desc_sid` int(10) unsigned NOT NULL DEFAULT '0', + `type` tinyint(3) unsigned NOT NULL DEFAULT '1', + `spellid` int(10) unsigned NOT NULL DEFAULT '0', + `prereq_skill` int(10) unsigned NOT NULL DEFAULT '0', + `prereq_minpoints` int(10) unsigned NOT NULL DEFAULT '0', + `spell_type` int(10) unsigned NOT NULL DEFAULT '0', + `spell_refresh` int(10) unsigned NOT NULL DEFAULT '0', + `classes` int(10) unsigned NOT NULL DEFAULT '65534', + `berserker` int(10) unsigned NOT NULL DEFAULT '0', + `class_type` int(10) unsigned NOT NULL DEFAULT '0', + `cost_inc` tinyint(4) NOT NULL DEFAULT '0', + `aa_expansion` smallint(3) unsigned NOT NULL DEFAULT '3', + `special_category` int(10) unsigned NOT NULL DEFAULT '4294967295', + `sof_type` tinyint(3) unsigned NOT NULL DEFAULT '1', + `sof_cost_inc` tinyint(3) NOT NULL DEFAULT '0', + `sof_max_level` tinyint(3) unsigned NOT NULL DEFAULT '1', + `sof_next_skill` int(10) unsigned NOT NULL DEFAULT '0', + `clientver` tinyint(3) unsigned NOT NULL DEFAULT '1', + `account_time_required` int(10) unsigned NOT NULL DEFAULT '0', + `sof_current_level` tinyint(3) unsigned NOT NULL DEFAULT '0', + `sof_next_id` int(10) unsigned NOT NULL DEFAULT '0', + `level_inc` tinyint(3) unsigned NOT NULL DEFAULT '0', + PRIMARY KEY (`skill_id`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO `altadv_vars` VALUES (2,'Innate Strength',1,5,4294967295,4294967295,13500,13501,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,2,1,0,0,292,0),(7,'Innate Stamina',1,5,4294967295,4294967295,13502,13503,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,7,1,0,0,302,0),(12,'Innate Agility',1,5,4294967295,4294967295,13504,13505,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,12,1,0,0,312,0),(17,'Innate Dexterity',1,5,4294967295,4294967295,13506,13507,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,17,1,0,0,322,0),(22,'Innate Intelligence',1,5,4294967295,4294967295,13508,13509,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,22,1,0,0,332,0),(27,'Innate Wisdom',1,5,4294967295,4294967295,13510,13511,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,27,1,0,0,342,0),(32,'Innate Charisma',1,5,4294967295,4294967295,13512,13513,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,32,1,0,0,352,0),(37,'Innate Fire Protection',1,5,4294967295,4294967295,13514,13515,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,37,1,0,0,362,0),(42,'Innate Cold Protection',1,5,4294967295,4294967295,13516,13517,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,42,1,0,0,372,0),(47,'Innate Magic Protection',1,5,4294967295,4294967295,13518,13519,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,47,1,0,0,382,0),(52,'Innate Poison Protection',1,5,4294967295,4294967295,13520,13521,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,52,1,0,0,392,0),(57,'Innate Disease Protection',1,5,4294967295,4294967295,13522,13523,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,15,57,1,0,0,402,0),(62,'Innate Run Speed',1,3,4294967295,4294967295,13524,13525,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,5,62,1,0,0,672,0),(65,'Innate Regeneration',1,3,4294967295,4294967295,13526,13527,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,5,65,1,0,0,674,0),(68,'Innate Metabolism',1,3,4294967295,4294967295,13528,13529,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,3,68,1,0,0,0,0),(71,'Innate Lung Capacity',1,3,4294967295,4294967295,13530,13531,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,6,71,1,0,0,676,0),(74,'First Aid',1,3,4294967295,4294967295,13532,13533,1,4294967295,0,0,0,0,65534,1,51,0,3,4294967295,1,0,6,74,1,0,0,263,0),(77,'Healing Adept',2,3,4294967295,4294967295,13534,13535,2,4294967295,4294967295,0,0,0,33884,0,55,2,3,4294967295,2,0,12,77,1,0,0,434,0),(80,'Healing Gift',2,3,4294967295,4294967295,13536,13537,2,4294967295,4294967295,0,0,0,33884,0,55,2,3,4294967295,2,0,21,80,1,0,0,437,0),(83,'Spell Casting Mastery',2,3,4294967295,4294967295,13538,13539,2,4294967295,4294967295,0,0,0,31812,0,55,2,3,4294967295,2,0,3,83,1,0,0,0,0),(86,'Spell Casting Reinforcement',2,3,4294967295,4294967295,13540,13541,2,4294967295,4294967295,0,0,0,50268,0,55,2,3,4294967295,2,0,3,86,1,0,0,0,0),(89,'Mental Clarity',2,3,4294967295,4294967295,13542,13543,2,4294967295,4294967295,0,0,0,64892,0,55,2,3,4294967295,2,0,3,89,1,0,0,0,0),(92,'Spell Casting Fury',2,3,4294967295,4294967295,13544,13545,2,4294967295,0,0,0,0,64892,0,55,2,3,4294967295,2,0,3,92,1,0,0,0,0),(95,'Channeling Focus',2,3,4294967295,4294967295,13546,13547,2,4294967295,4294967295,0,0,0,64892,0,55,2,3,4294967295,2,0,3,95,1,0,0,0,0),(98,'Spell Casting Subtlety',2,3,4294967295,4294967295,13548,13549,2,0,0,0,0,0,30976,0,55,2,3,4294967295,2,0,3,98,1,0,0,0,0),(101,'Spell Casting Expertise',2,3,4294967295,4294967295,13550,13551,2,4294967295,4294967295,0,0,0,31008,0,55,2,3,4294967295,2,0,3,101,1,0,0,0,0),(104,'Spell Casting Deftness',2,3,4294967295,4294967295,13552,13553,2,4294967295,4294967295,0,0,0,14368,0,55,2,3,4294967295,2,0,3,104,1,0,0,0,0),(107,'Natural Durability',2,3,4294967295,4294967295,13554,13555,2,4294967295,4294967295,0,0,0,65534,1,55,2,3,4294967295,1,0,3,107,1,0,0,0,0),(110,'Natural Healing',2,3,4294967295,4294967295,13556,13557,2,4294967295,4294967295,0,0,0,33722,1,55,2,3,4294967295,2,0,8,110,1,0,0,1031,0),(113,'Combat Fury',2,3,4294967295,4294967295,13558,13559,2,4294967295,4294967295,0,0,0,33722,1,55,2,3,4294967295,2,0,6,113,1,0,0,443,0),(116,'Fear Resistance',2,3,4294967295,4294967295,13560,13561,2,4294967295,4294967295,0,0,0,33722,1,55,2,3,4294967295,2,0,3,116,1,0,0,0,0),(119,'Finishing Blow',2,3,4294967295,4294967295,13562,13563,2,4294967295,0,0,0,0,33722,1,55,2,3,4294967295,2,0,21,119,1,0,0,440,0),(122,'Combat Stability',2,3,4294967295,4294967295,13564,13565,2,4294967295,4294967295,0,0,0,65534,1,55,2,3,4294967295,1,0,33,122,1,0,0,454,0),(125,'Combat Agility',2,3,4294967295,4294967295,13566,13567,2,4294967295,4294967295,0,0,0,65534,1,55,2,3,4294967295,1,0,33,125,1,0,0,449,0),(128,'Mass Group Buff',9,1,13811,13812,13809,13810,3,5228,4294967295,0,1,4320,60508,0,59,0,3,4294967295,3,0,1,128,1,0,0,0,0),(129,'Divine Resurrection',5,1,13570,13571,13568,13569,3,2738,80,3,2,259200,4,0,59,0,3,4294967295,3,0,1,129,1,0,0,0,0),(130,'Innate Invis to Undead',3,1,13574,13575,13572,13573,3,235,4294967295,0,3,7,2052,0,59,0,3,4294967295,3,0,1,130,1,0,1,0,0),(131,'Celestial Regeneration',5,1,13578,13579,13576,13577,3,2740,80,3,4,4320,4,0,59,0,3,4294967295,3,0,3,131,1,0,0,531,0),(132,'Bestow Divine Aura',6,1,13582,13583,13580,13581,3,2741,4294967295,0,5,8640,4,0,59,0,3,4294967295,3,0,1,132,1,0,0,0,0),(133,'Turn Undead',3,3,13586,13587,13584,13585,3,2776,4294967295,0,6,4320,4,0,59,3,3,4294967295,3,0,3,133,1,0,0,0,0),(136,'Purify Soul',5,1,13590,4294967295,13588,13589,3,2742,4294967295,0,7,4320,4,0,59,0,3,4294967295,3,0,1,136,1,0,0,0,0),(137,'Quick Evacuation',3,3,4294967295,4294967295,13592,13593,3,4294967295,4294967295,0,0,0,4160,0,59,3,3,4294967295,3,0,3,137,1,0,0,0,0),(140,'Exodus',6,1,13596,4294967295,13594,13595,3,2771,4294967295,0,3,4320,4160,0,59,0,3,4294967295,3,0,1,140,1,0,0,0,0),(141,'Quick Damage',3,3,4294967295,4294967295,13598,13599,3,4294967295,92,3,0,0,12352,0,59,3,3,4294967295,3,0,3,141,1,0,0,0,0),(144,'Enhanced Root',5,1,4294967295,4294967295,13600,13601,3,4294967295,4294967295,0,0,0,64,0,59,0,3,4294967295,3,0,1,144,1,0,0,0,0),(145,'Dire Charm',9,1,13604,13605,13602,13603,3,4294967295,4294967295,0,15,4320,18496,0,59,0,3,4294967295,3,0,1,145,1,0,0,0,0),(146,'Cannibalization',5,1,13608,13609,13606,13607,3,2749,89,3,2,180,1024,0,59,0,3,4294967295,3,0,1,146,1,0,0,0,0),(147,'Quick Buff',3,3,4294967295,4294967295,13610,13611,3,4294967295,4294967295,0,0,0,17408,0,59,3,3,4294967295,3,0,3,147,1,0,0,0,0),(150,'Alchemy Mastery',3,3,4294967295,4294967295,13612,13613,3,4294967295,4294967295,0,0,0,1024,0,59,3,3,4294967295,3,0,3,150,1,0,0,0,0),(153,'Rabid Bear',5,1,13616,13617,13614,13615,3,2750,4294967295,0,3,7200,1024,0,59,0,3,4294967295,3,0,1,153,1,0,0,0,0),(154,'Mana Burn',5,1,13620,13621,13618,13619,3,2751,89,3,7,7200,4096,0,59,0,3,4294967295,3,0,1,154,1,0,0,0,0),(155,'Improved Familiar',9,1,13624,13625,13622,13623,3,2758,0,0,8,420,4096,0,59,0,3,4294967295,3,0,2,155,1,0,0,533,0),(156,'Nexus Gate',6,1,13628,13629,13626,13627,3,2734,4294967295,0,2,4320,4096,0,59,0,3,4294967295,3,0,1,156,1,0,0,0,0),(158,'Permanent Illusion',3,1,4294967295,4294967295,13630,13631,3,4294967295,4294967295,0,0,0,16384,0,59,0,3,4294967295,3,0,1,158,1,0,0,0,0),(159,'Jewelcraft Mastery',3,3,4294967295,4294967295,13632,13633,3,4294967295,4294967295,0,0,0,16384,0,59,3,3,4294967295,3,0,3,159,1,0,0,0,0),(162,'Gather Mana',5,1,13636,13637,13634,13635,3,2753,89,3,3,8640,16384,0,59,0,3,4294967295,3,0,1,162,1,0,0,0,0),(163,'Mend Companion',5,1,13640,13641,13638,13639,3,2752,4294967295,0,2,2160,43008,0,59,0,3,4294967295,3,0,1,163,1,0,0,0,0),(164,'Quick Summoning',3,3,4294967295,4294967295,13642,13643,3,4294967295,4294967295,0,0,0,8192,0,59,3,3,4294967295,3,0,3,164,1,0,0,0,0),(167,'Frenzied Burnout',6,1,13646,13647,13644,13645,3,2754,4294967295,0,14,900,8192,0,59,0,3,4294967295,3,0,1,167,1,0,0,0,0),(168,'Elemental Form: Fire',3,3,13650,13651,13648,13649,3,2795,4294967295,0,4,900,8192,0,59,3,3,4294967295,3,0,3,168,1,0,0,0,0),(171,'Elemental Form: Water',3,3,13654,13655,13652,13653,3,2798,4294967295,0,4,900,8192,0,59,3,3,4294967295,3,0,3,171,1,0,0,0,0),(174,'Elemental Form: Earth',3,3,13658,13659,13656,13657,3,2792,4294967295,0,4,900,8192,0,59,3,3,4294967295,3,0,3,174,1,0,0,0,0),(177,'Elemental Form: Air',3,3,13662,13663,13660,13661,3,2789,4294967295,0,4,900,8192,0,59,3,3,4294967295,3,0,3,177,1,0,0,0,0),(180,'Improved Reclaim Energy',3,1,4294967295,4294967295,13664,13665,3,4294967295,4294967295,0,0,0,8192,0,59,0,3,4294967295,3,0,1,180,1,0,0,0,0),(181,'Turn Summoned',3,3,13668,13669,13666,13667,3,8133,4294967295,0,5,300,8192,0,59,3,3,4294967295,3,0,3,181,1,0,0,0,0),(182,'Elemental Pact',5,1,4294967295,4294967295,13670,13671,3,4294967295,4294967295,0,0,0,8192,0,59,0,3,4294967295,3,0,1,182,1,0,0,0,0),(183,'Lifeburn',9,1,13674,13675,13672,13673,3,2755,4294967295,0,4,8640,2048,0,59,0,3,4294967295,3,0,1,183,1,0,0,0,0),(184,'Dead Mesmerization',3,1,13678,13679,13676,13677,3,2756,4294967295,0,5,4320,2048,0,59,0,3,4294967295,3,0,1,184,1,0,0,0,0),(185,'Fearstorm',5,1,13682,13683,13680,13681,3,2757,4294967295,0,6,4320,2048,0,59,0,3,4294967295,3,0,1,185,1,0,0,0,0),(186,'Flesh to Bone',3,1,13686,13687,13684,13685,3,2772,4294967295,0,7,7,2048,0,59,0,3,4294967295,3,0,1,186,1,0,0,0,0),(187,'Call to Corpse',6,1,13690,13691,13688,13689,3,2764,4294967295,0,8,4320,2048,0,59,0,3,4294967295,3,0,1,187,1,0,0,0,0),(188,'Divine Stun',9,1,13694,13695,13692,13693,3,2190,4294967295,0,9,30,8,0,59,0,3,4294967295,3,0,1,188,1,0,0,0,0),(189,'Improved Lay on Hands',5,1,4294967295,4294967295,13696,13697,3,4294967295,4294967295,0,0,0,8,0,59,0,3,4294967295,3,0,1,189,1,0,0,0,0),(190,'Slay Undead',3,3,4294967295,4294967295,13698,13699,3,4294967295,113,3,0,0,8,0,59,3,3,4294967295,3,0,6,190,1,0,0,1524,0),(193,'Act of Valor',3,1,13702,13703,13700,13701,3,2775,4294967295,0,3,4320,8,0,59,0,3,4294967295,3,0,1,193,1,0,0,0,0),(194,'Holy Steed',5,1,13706,13707,13704,13705,3,2874,4294967295,0,0,1,8,0,59,0,3,4294967295,3,0,1,194,1,0,0,0,0),(195,'Fearless',6,1,4294967295,4294967295,13708,13709,3,4294967295,116,3,0,0,40,0,59,0,3,4294967295,3,0,1,195,1,0,0,0,0),(196,'2 Hand Bash',6,1,4294967295,4294967295,13710,13711,3,4294967295,4294967295,0,0,0,40,0,59,0,3,4294967295,3,0,1,196,1,0,0,0,0),(197,'Innate Camouflage',5,1,13714,13715,13712,13713,3,2765,4294967295,0,2,7,80,0,59,0,3,4294967295,3,0,1,197,1,0,0,0,0),(198,'Ambidexterity',9,1,4294967295,4294967295,13716,13717,3,4294967295,4294967295,0,0,0,33682,0,59,0,3,4294967295,3,0,1,198,1,0,0,0,0),(199,'Archery Mastery',3,3,4294967295,4294967295,13718,13719,3,4294967295,4294967295,0,0,0,16,0,59,3,3,4294967295,3,0,3,199,1,0,0,0,0),(205,'Endless Quiver',9,1,4294967295,4294967295,13722,13723,3,4294967295,4294967295,0,0,0,16,0,59,0,3,4294967295,3,0,1,205,1,0,0,0,0),(206,'Unholy Steed',5,1,13726,13727,13724,13725,3,2875,4294967295,0,0,1,32,0,59,0,3,4294967295,3,0,1,206,1,0,0,0,0),(207,'Improved Harm Touch',6,1,13730,13731,13728,13729,3,2821,4294967295,0,2,4320,32,0,59,0,3,4294967295,3,0,1,207,1,0,0,0,0),(208,'Leech Touch',6,1,13734,13735,13732,13733,3,2774,4294967295,0,2,4320,32,0,59,0,3,4294967295,3,0,1,208,1,0,0,0,0),(210,'Soul Abrasion',3,3,4294967295,4294967295,13740,13741,3,4294967295,4294967295,0,0,0,32,0,59,3,3,4294967295,3,0,3,210,1,0,0,0,0),(213,'Instrument Mastery',3,3,4294967295,4294967295,13742,13743,3,4294967295,4294967295,0,0,0,256,0,59,3,3,4294967295,3,0,4,213,1,0,0,700,0),(225,'Jam Fest',3,3,4294967295,4294967295,13750,13751,3,4294967295,4294967295,0,0,0,256,0,59,3,3,4294967295,3,0,3,225,1,0,0,0,0),(230,'Critical Mend',3,3,4294967295,4294967295,13757,13758,3,4294967295,74,3,0,0,128,0,59,3,3,4294967295,3,0,6,230,1,0,0,539,0),(233,'Purify Body',9,1,13761,13762,13759,13760,3,2742,4294967295,0,1,1800,128,0,59,0,3,4294967295,3,0,1,233,1,0,0,0,0),(237,'Rapid Feign',3,3,4294967295,4294967295,13765,13766,3,4294967295,4294967295,0,0,0,128,0,59,3,3,4294967295,3,0,3,237,1,0,0,0,0),(240,'Return Kick',3,3,4294967295,4294967295,13767,13768,3,4294967295,0,0,0,0,128,0,59,3,3,4294967295,3,0,3,240,1,0,0,0,0),(243,'Escape',9,1,13771,4294967295,13769,13770,3,5244,4294967295,0,1,4320,512,0,59,0,3,4294967295,3,0,1,243,1,0,0,0,0),(244,'Poison Mastery',3,3,4294967295,4294967295,13773,13774,3,4294967295,4294967295,0,0,0,512,0,59,3,3,6,3,0,3,244,1,0,0,0,0),(247,'Double Riposte',3,3,4294967295,4294967295,13775,13776,3,4294967295,4294967295,0,0,0,33466,1,59,3,3,4294967295,3,0,6,247,1,0,0,504,0),(254,'Purge Poison',9,1,13783,13784,13781,13782,3,5232,4294967295,0,12,4320,512,0,59,0,3,4294967295,3,0,1,254,1,0,0,0,0),(255,'Flurry',3,3,4294967295,4294967295,13785,13786,3,4294967295,113,3,0,0,2,1,59,3,3,4294967295,3,0,12,255,1,0,0,542,0),(258,'Rampage',5,1,13789,4294967295,13787,13788,3,5233,4294967295,0,1,600,2,1,59,0,3,4294967295,3,0,1,258,1,0,0,0,0),(259,'Area Taunt',5,1,13793,13794,13791,13792,3,0,4294967295,0,2,900,2,0,59,0,3,4294967295,3,0,1,259,1,0,0,0,0),(260,'Warcry',3,3,13797,4294967295,13795,13796,3,5229,116,3,3,2160,2,0,59,3,3,4294967295,3,0,3,260,1,0,0,0,0),(263,'Bandage Wound',3,3,4294967295,4294967295,13799,13800,3,4294967295,74,3,0,0,2,1,59,3,3,4294967295,3,0,6,74,1,0,3,0,0),(266,'Spell Casting Reinforcement Mastery',8,1,4294967295,4294967295,13801,13802,3,4294967295,86,3,0,0,17476,0,59,0,3,4294967295,3,0,1,266,1,0,0,0,0),(267,'Spell Casting Fury Mastery',3,3,4294967295,4294967295,13803,13804,3,4294967295,92,3,0,0,4096,0,59,3,3,4294967295,2,0,8,267,1,0,0,640,0),(270,'Extended Notes',3,3,4294967295,4294967295,13805,13806,3,4294967295,4294967295,0,0,0,256,0,59,3,3,4294967295,3,0,3,270,1,0,0,0,0),(273,'Dragon Punch',5,1,4294967295,4294967295,13807,13808,3,4294967295,4294967295,0,0,0,128,0,59,0,3,4294967295,3,0,1,273,1,0,0,0,0),(274,'Strong Root',5,1,13815,13816,13813,13814,3,2748,4294967295,0,4,4320,4096,0,59,0,3,4294967295,3,0,1,274,1,0,0,0,0),(275,'Singing Mastery',3,3,4294967295,4294967295,13817,13818,3,4294967295,4294967295,0,0,0,256,0,59,3,3,4294967295,3,0,4,275,1,0,0,701,0),(278,'Body and Mind Rejuvenation',5,1,4294967295,4294967295,13819,13820,3,4294967295,4294967295,0,0,0,33080,0,59,0,3,4294967295,3,0,1,278,1,0,0,0,0),(279,'Physical Enhancement',5,1,4294967295,4294967295,13821,13822,3,4294967295,4294967295,0,0,0,33722,1,59,0,3,4294967295,3,0,1,279,1,0,0,0,0),(280,'Adv. Trap Negotiation',3,3,4294967295,4294967295,13823,13824,3,4294967295,4294967295,0,0,0,768,0,59,3,3,4294967295,3,0,3,280,1,0,0,0,0),(283,'Acrobatics',3,3,4294967295,4294967295,13825,13826,3,4294967295,4294967295,0,0,0,896,0,59,3,3,4294967295,3,0,3,283,1,0,0,0,0),(286,'Scribble Notes',3,1,4294967295,4294967295,13827,13828,3,4294967295,4294967295,0,0,0,256,0,59,0,3,4294967295,3,0,1,286,1,0,0,0,0),(287,'Chaotic Stab',6,1,4294967295,4294967295,13829,13830,3,4294967295,4294967295,0,0,0,512,0,59,0,3,4294967295,3,0,1,287,1,0,0,0,0),(288,'Pet Discipline',6,1,4294967295,4294967295,13831,13832,3,4294967295,4294967295,0,0,0,44064,0,59,0,3,4294967295,2,0,3,288,1,0,0,1129,0),(289,'Hobble of Spirits',5,1,3300,3301,3298,3299,3,3290,4294967295,0,3,300,32768,0,59,0,3,4294967295,3,0,1,289,1,0,0,0,0),(290,'Frenzy of Spirit',4,1,3304,3305,3302,3303,3,3289,4294967295,0,4,720,32768,0,59,0,3,4294967295,3,0,2,290,1,0,0,0,0),(291,'Paragon of Spirit',6,1,3308,3309,3306,3307,3,3291,4294967295,0,5,900,32768,0,59,0,3,4294967295,3,0,1,291,1,0,0,0,0),(292,'Advanced Innate Strength',1,10,4294967295,4294967295,5546,5550,4,4294967295,2,5,0,0,65534,1,61,0,4,4294967295,1,0,15,2,1,0,5,0,0),(302,'Advanced Innate Stamina',1,10,4294967295,4294967295,5551,5552,4,4294967295,7,5,0,0,65534,1,61,0,4,4294967295,1,0,15,7,1,0,5,0,0),(312,'Advanced Innate Agility',1,10,4294967295,4294967295,5553,5554,4,4294967295,12,5,0,0,65534,1,61,0,4,4294967295,1,0,15,12,1,0,5,0,0),(322,'Advanced Innate Dexterity',1,10,4294967295,4294967295,5555,5556,4,4294967295,17,5,0,0,65534,1,61,0,4,4294967295,1,0,15,17,1,0,5,0,0),(332,'Advanced Innate Intelligence',1,10,4294967295,4294967295,5557,5558,4,4294967295,22,5,0,0,65534,1,61,0,4,4294967295,1,0,15,22,1,0,5,0,0),(342,'Advanced Innate Wisdom',1,10,4294967295,4294967295,5559,5560,4,4294967295,27,5,0,0,65534,1,61,0,4,4294967295,1,0,15,27,1,0,5,0,0),(352,'Advanced Innate Charisma',1,10,4294967295,4294967295,5564,5565,4,4294967295,32,5,0,0,65534,1,61,0,4,4294967295,1,0,15,32,1,0,5,0,0),(362,'Warding of Solusek',1,10,4294967295,4294967295,5568,5569,4,4294967295,37,5,0,0,65534,1,61,0,4,4294967295,1,0,15,37,1,0,5,0,0),(372,'Blessing of E\'ci',1,10,4294967295,4294967295,5570,5571,4,4294967295,42,5,0,0,65534,1,61,0,4,4294967295,1,0,15,42,1,0,5,0,0),(382,'Marr\'s Protection',1,10,4294967295,4294967295,5566,5567,4,4294967295,47,5,0,0,65534,1,61,0,4,4294967295,1,0,15,47,1,0,5,0,0),(392,'Shroud of The Faceless',1,10,4294967295,4294967295,5572,5573,4,4294967295,52,5,0,0,65534,1,61,0,4,4294967295,1,0,15,52,1,0,5,0,0),(402,'Bertoxxulous\' Gift',1,10,4294967295,4294967295,5574,5575,4,4294967295,57,5,0,0,65534,1,61,0,4,4294967295,1,0,15,57,1,0,5,0,0),(412,'New Tanaan Crafting Mastery',3,6,4294967295,4294967295,3286,3287,1,4294967295,0,0,0,0,65534,1,51,0,1,6,1,0,6,412,1,0,0,0,0),(418,'Planar Power',2,5,4294967295,4294967295,5547,5548,4,4294967295,0,0,0,0,65534,1,61,0,4,4294967295,1,0,20,418,1,0,0,1001,0),(423,'Planar Durability',3,3,4294967295,4294967295,5549,5560,4,4294967295,0,0,0,0,42,1,61,0,4,4294967295,1,0,3,423,1,0,0,0,0),(426,'Innate Enlightenment',3,5,4294967295,4294967295,5561,5562,4,4294967295,0,0,0,0,31812,0,61,0,4,4294967295,1,0,5,426,1,0,0,0,0),(434,'Advanced Healing Adept',2,3,4294967295,4294967295,5578,5579,5,4294967295,77,3,0,0,33884,0,62,2,4,4294967295,2,0,12,77,1,0,3,1083,0),(437,'Advanced Healing Gift',2,3,4294967295,4294967295,5580,5581,5,4294967295,80,3,0,0,33884,0,62,2,4,4294967295,2,0,21,80,1,0,3,1086,0),(440,'Coup de Grace',2,3,4294967295,4294967295,5618,5619,5,4294967295,119,3,0,0,33722,1,62,0,4,4294967295,2,0,21,119,1,0,3,1053,0),(443,'Fury of the Ages',3,3,4294967295,4294967295,5620,5621,5,4294967295,113,3,0,0,33722,1,62,0,4,4294967295,2,0,6,113,1,0,3,0,0),(446,'Mastery of the Past',3,3,4294967295,4294967295,446,446,5,4294967295,101,3,0,0,31008,0,62,0,4,4294967295,2,0,3,446,1,0,0,7050,0),(449,'Lightning Reflexes',3,5,4294967295,4294967295,5636,5637,5,4294967295,125,3,0,0,65534,1,61,0,4,4294967295,1,0,33,125,1,0,3,1061,1),(454,'Innate Defense',3,5,4294967295,4294967295,5638,5639,5,4294967295,122,3,0,0,65534,1,61,0,4,4294967295,1,0,33,122,1,0,3,1066,1),(459,'Radiant Cure',2,3,3310,4294967294,9283,9284,5,3297,0,0,8,180,1092,0,61,2,4,4294967295,3,0,3,459,1,0,0,0,0),(477,'Hastened Exodus',2,3,4294967295,4294967295,5594,5595,5,4294967295,140,1,0,0,4160,0,63,0,4,4294967295,3,0,3,477,1,0,0,0,0),(480,'Hastened Root',2,3,4294967295,4294967295,5596,5597,5,4294967295,274,1,0,0,4096,0,63,0,4,4294967295,3,0,3,480,1,0,0,0,0),(483,'Hastened Mending',2,3,4294967295,4294967295,5598,5599,5,4294967295,163,1,0,0,43008,0,63,0,4,4294967295,3,0,3,483,1,0,0,0,0),(462,'Hastened Divinity',2,3,4294967295,4294967295,5582,5583,5,4294967295,132,1,0,0,4,0,63,0,4,4294967295,3,0,3,462,1,0,0,0,0),(468,'Hastened Purification of the Soul',2,3,4294967295,4294967295,5588,5589,5,4294967295,136,1,0,0,4,0,63,0,4,4294967295,3,0,3,468,1,0,0,0,0),(471,'Hastened Gathering',2,3,4294967295,4294967295,5590,5591,5,4294967295,162,1,0,0,16384,0,63,0,4,4294967295,3,0,3,471,1,0,0,0,0),(474,'Hastened Rabidity',2,3,4294967295,4294967295,5592,5593,5,4294967295,153,1,0,0,1024,0,63,0,4,4294967295,3,0,3,474,1,0,0,0,0),(486,'Hastened Banishment',2,3,4294967295,4294967295,5600,5601,5,4294967295,182,1,0,0,8192,0,63,2,4,4294967295,3,0,3,486,1,0,0,0,0),(489,'Hastened Instigation',2,3,4294967295,4294967295,5608,5609,5,4294967295,259,1,0,0,2,0,63,0,4,4294967295,3,0,3,489,1,0,0,0,0),(492,'Furious Rampage',2,3,4294967295,4294967295,5610,5611,5,4294967295,258,1,0,0,2,1,63,0,4,4294967295,3,0,3,492,1,0,0,0,0),(495,'Hastened Purification of the Body',2,3,4294967295,4294967295,5612,5613,5,4294967295,233,1,0,0,128,0,63,0,4,4294967295,3,0,3,495,1,0,0,0,0),(498,'Hasty Exit',2,3,4294967295,4294967295,5614,5615,5,4294967295,243,1,0,0,512,0,63,0,4,4294967295,3,0,5,498,1,0,0,886,0),(501,'Hastened Purification',2,3,4294967295,4294967295,5616,5617,5,4294967295,254,1,0,0,512,0,63,0,4,4294967295,3,0,3,501,1,0,0,0,0),(504,'Flash of Steel',3,3,4294967295,4294967295,5622,5623,5,4294967295,249,3,0,0,33466,1,62,0,4,4294967295,3,0,6,247,1,0,3,0,0),(507,'Divine Arbitration',3,3,5696,5697,5532,5533,5,3252,0,0,9,180,4,0,61,0,4,4294967295,3,0,3,507,1,0,0,0,0),(510,'Wrath of the Wild',3,3,5698,5699,5534,5535,5,3255,0,0,4,240,64,0,61,0,4,4294967295,3,0,3,510,1,0,0,0,0),(513,'Virulent Paralysis',3,3,5700,5701,5536,5537,5,3274,0,0,4,120,1024,0,61,0,4,4294967295,3,0,3,513,1,0,0,0,0),(516,'Harvest of Druzzil',2,1,5702,5703,5538,5539,5,3338,0,0,5,480,4096,0,62,0,4,4294967295,3,0,1,516,1,0,0,0,0),(517,'Eldritch Rune',3,3,5706,5707,5542,5543,5,3258,0,0,4,600,16384,0,61,0,4,4294967295,3,0,3,517,1,0,0,0,0),(520,'Servant of Ro',3,3,5704,5705,5540,5541,5,3265,0,0,6,540,8192,0,61,0,4,4294967295,3,0,3,520,1,0,0,0,0),(523,'Wake the Dead',5,3,5708,5709,5544,5545,5,3268,0,0,9,540,2048,0,61,-1,4,4294967295,3,0,3,523,1,0,0,0,0),(526,'Suspended Minion',5,2,5710,5711,5626,5627,5,5844,0,0,0,1,60448,0,62,-2,4,4294967295,3,0,2,526,1,0,0,0,0),(528,'Spirit Call',4,3,5712,5713,5628,5629,5,3283,0,0,5,720,1024,0,61,-1,4,4294967295,3,0,3,528,1,0,0,0,0),(531,'Celestial Renewal',3,2,28226,28227,5630,5631,5,3250,131,1,4,900,4,0,63,3,4,4294967295,3,0,3,131,1,0,1,0,0),(533,'Allegiant Familiar',6,1,9244,4294967295,5632,5633,5,3264,155,1,8,420,4096,0,64,0,4,4294967295,3,0,2,155,1,0,1,0,0),(534,'Hand of Piety',3,3,5724,5725,5634,5635,5,3261,189,1,4,2160,8,0,61,0,4,4294967295,3,0,3,534,1,0,0,0,0),(537,'Mithaniel\'s Binding',3,2,4294967295,4294967295,5640,5641,5,4294967295,263,3,0,0,2,1,63,0,4,4294967295,3,0,2,537,1,0,0,0,0),(539,'Mending of the Tranquil',2,3,4294967295,4294967295,5642,5643,5,4294967295,230,3,0,0,128,0,63,2,4,4294967295,3,0,6,230,1,0,3,0,0),(542,'Raging Flurry',2,3,4294967295,4294967295,5644,5645,5,4294967295,255,3,0,0,2,1,63,2,4,4294967295,3,0,12,255,1,0,3,1163,0),(545,'Guardian of the Forest',3,3,5714,5715,5646,5647,5,3271,0,0,3,900,16,0,61,0,4,4294967295,3,0,3,545,1,0,0,0,0),(548,'Spirit of the Wood',4,3,5716,5717,5648,5649,5,3277,0,0,5,900,64,0,61,-1,4,4294967295,3,0,3,548,1,0,0,0,0),(551,'Bestial Frenzy',2,5,4294967295,4294967295,5650,5651,5,4294967295,0,0,0,0,32768,0,61,0,4,4294967295,3,0,5,551,1,0,0,0,0),(556,'Harmonious Attack',2,5,4294967295,4294967295,5652,5653,5,4294967295,0,0,0,0,256,0,61,0,4,4294967295,3,0,5,556,1,0,0,0,0),(561,'Knight\'s Advantage',2,3,4294967295,4294967295,5654,5655,5,4294967295,0,0,0,0,40,0,61,2,4,4294967295,3,0,6,561,1,0,0,1624,0),(564,'Ferocity',3,3,4294967295,4294967295,5656,5657,2,4294967295,0,0,0,0,658,1,61,2,4,4294967295,3,0,6,564,1,0,0,1621,0),(567,'Viscid Roots',5,1,4294967295,4294967295,5664,5665,5,4294967295,144,1,0,0,64,0,63,0,4,4294967295,3,0,2,567,1,0,0,5061,0),(568,'Sionachie\'s Crescendo',2,3,4294967295,4294967295,5666,5667,5,4294967295,270,3,0,0,256,0,63,0,4,4294967295,3,0,3,568,1,0,0,0,0),(571,'Ayonaes Tutelage',2,3,4294967295,4294967295,5668,5669,5,4294967295,0,0,0,0,256,0,63,0,4,4294967295,3,0,3,571,1,0,0,0,0),(574,'Feigned Minion',3,3,4294967295,4294967295,5674,5675,5,4294967295,288,1,0,0,2048,0,61,0,4,4294967295,3,0,3,574,1,0,0,0,0),(577,'Unfailing Divinity',2,3,4294967295,4294967295,5660,5661,5,4294967295,0,0,0,0,4,0,61,2,4,4294967295,3,0,3,577,1,0,0,0,0),(580,'Animation Empathy',4,3,4294967295,4294967295,5658,5659,5,4294967295,0,0,0,0,16384,0,61,-1,4,4294967295,3,0,3,580,1,0,0,0,0),(583,'Rush to Judgment',2,3,4294967295,4294967295,5672,5673,5,4294967295,188,1,0,0,8,0,63,0,4,4294967295,3,0,3,583,1,0,0,0,0),(586,'Living Shield',2,3,4294967295,4294967295,3275,3276,5,4294967295,279,1,0,0,2,0,61,2,4,4294967295,3,0,3,586,1,0,0,0,0),(589,'Consumption of the Soul',3,3,4294967295,4294967295,3283,3284,5,4294967295,208,1,0,0,32,0,61,0,4,4294967295,3,0,5,589,1,0,0,893,0),(592,'Boastful Bellow',6,1,5718,5719,5676,5677,5,3282,113,1,2,18,256,0,63,0,4,4294967295,3,0,1,592,1,0,0,0,0),(593,'Fervent Blessing',3,3,4294967295,4294967295,5680,5681,5,4294967295,0,0,0,0,8,0,61,0,4,4294967295,3,0,3,593,1,0,0,0,0),(596,'Touch of the Wicked',2,3,4294967295,4294967295,5682,5683,5,4294967295,0,0,0,0,32,0,61,2,4,4294967295,3,0,3,596,1,0,0,0,0),(599,'Punishing Blade',2,3,4294967295,4294967295,5684,5685,5,4294967295,0,0,0,0,146,1,61,2,4,4294967295,2,0,12,599,1,0,0,1536,0),(602,'Speed of the Knight',3,3,4294967295,4294967295,5686,5687,5,4294967295,0,0,0,0,40,0,61,0,4,4294967295,3,0,3,602,1,0,0,0,0),(605,'Shroud of Stealth',6,1,4294967295,4294967295,5688,5689,5,4294967295,0,0,0,0,512,0,63,0,4,4294967295,3,0,1,605,1,0,0,0,0),(606,'Nimble Evasion',1,5,4294967295,4294967295,5690,5691,5,4294967295,0,0,0,0,512,0,61,0,4,4294967295,3,0,5,606,1,0,0,0,0),(611,'Technique of Master Wu',2,5,4294967295,4294967295,5692,5693,5,4294967295,0,0,0,0,128,0,61,0,4,4294967295,3,0,5,611,1,0,0,0,0),(616,'Host of the Elements',5,3,5720,5721,5694,5695,5,3286,0,0,7,900,8192,0,63,-1,4,4294967295,3,0,3,616,1,0,0,0,0),(619,'Call of Xuzl',3,3,3290,3291,3288,3289,5,3292,0,0,6,900,4096,0,61,-1,4,4294967295,3,0,5,619,1,0,0,721,0),(622,'Hastened Stealth',3,3,4294967295,4294967295,3314,3315,5,4294967295,0,0,0,0,512,0,61,0,4,4294967295,3,0,3,622,1,0,0,0,0),(625,'Ingenuity',1,3,4294967295,4294967295,5523,5728,5,4294967295,0,0,0,0,642,1,61,1,4,4294967295,2,0,9,625,1,0,0,4733,0),(628,'Fleet of Foot',2,2,4294967295,4294967295,5584,5585,5,4294967295,0,0,0,0,256,0,62,2,4,4294967295,3,0,2,628,1,0,0,0,0),(630,'Fading Memories',6,1,5732,5733,5730,5731,5,5243,0,0,7,1,256,0,63,0,4,4294967295,3,0,1,630,1,0,0,0,0),(631,'Tactical Mastery',2,3,4294967295,4294967295,5734,5735,5,4294967295,0,0,0,0,2,1,61,1,4,4294967295,3,0,3,631,1,0,0,0,0),(634,'Theft of Life',1,3,4294967295,4294967295,5736,5737,5,4294967295,0,0,0,0,2080,0,61,1,4,4294967295,3,0,8,634,1,0,0,844,0),(637,'Fury of Magic',3,3,4294967295,4294967295,5738,5739,5,4294967295,92,3,0,0,27716,0,61,3,4,4294967295,2,0,6,637,1,0,0,770,0),(640,'Fury of Magic Mastery',2,3,4294967295,4294967295,5740,5741,5,4294967295,267,3,0,0,4096,0,61,2,4,4294967295,2,0,8,267,1,0,3,924,0),(643,'Project Illusion',4,1,5662,5663,5670,5742,5,5227,158,1,0,1,16384,0,62,0,4,4294967295,3,0,1,643,1,0,0,0,0),(644,'Headshot',4,1,4294967295,4294967295,5743,5744,5,4294967295,199,1,0,0,16,0,62,0,4,4294967295,3,0,1,644,1,0,0,0,0),(645,'Entrap',4,1,5749,4294967295,5747,5748,5,3614,0,0,4,5,16,0,64,0,4,4294967295,3,0,1,645,1,0,0,0,0),(646,'Unholy Touch',2,3,4294967295,4294967295,9000,9001,5,4294967295,207,1,0,0,32,0,61,2,4,4294967295,3,0,3,646,1,0,0,0,0),(649,'Total Domination',2,3,4294967295,4294967295,9002,9003,5,4294967295,0,0,0,0,16384,0,61,2,4,4294967295,3,0,3,649,1,0,0,0,0),(652,'Stalwart Endurance',2,3,4294967295,4294967295,5751,5752,5,4294967295,0,0,0,0,2,1,61,2,4,4294967295,3,0,3,652,1,0,0,0,2),(672,'Swift Journey',5,2,4294967295,4294967295,9151,9152,6,4294967295,62,3,0,0,65534,1,61,0,7,4294967295,1,0,5,62,1,0,3,0,0),(674,'Convalescence',3,2,4294967295,4294967295,9153,9154,6,4294967295,65,3,0,0,65534,1,61,0,7,4294967295,1,0,5,65,1,0,3,0,0),(676,'Lasting Breath',2,2,4294967295,4294967295,9155,9156,6,4294967295,71,3,0,0,65534,1,61,0,7,4294967295,1,0,6,71,1,0,3,978,0),(678,'Packrat',3,5,4294967295,4294967295,9325,9326,6,4294967295,0,0,0,0,65534,1,61,0,7,4294967295,1,0,5,678,1,0,0,0,0),(686,'Weapon Affinity',5,5,4294967295,4294967295,9159,9160,6,4294967295,0,0,0,0,33722,1,55,0,7,4294967295,2,0,5,686,1,0,0,0,0),(691,'Secondary Forte',15,1,4294967295,4294967295,9161,9162,6,4294967295,0,0,0,0,31812,0,55,0,7,4294967295,2,0,1,691,1,0,0,0,0),(692,'Persistent Casting',3,3,4294967295,4294967295,9187,9188,6,4294967295,0,0,0,0,64892,0,55,3,7,4294967295,2,0,9,692,1,0,0,7647,0),(718,'Bestial Alignment',3,3,9219,4294967295,9219,9220,6,4521,0,0,7,4320,32768,0,65,3,7,4294967295,2,0,3,718,1,0,0,0,0),(721,'Wrath of Xuzl',5,2,3290,3291,9199,9200,6,5110,619,3,6,900,4096,0,65,0,7,4294967295,2,0,5,619,1,0,3,0,0),(723,'Feral Swipe',9,1,9175,4294967295,9175,9176,6,4788,0,0,6,60,32768,0,65,0,7,4294967295,2,0,1,723,1,0,0,0,0),(724,'Warder\'s Fury',3,5,4294967295,4294967295,9177,9178,6,4294967295,0,0,0,0,32768,0,65,0,7,4294967295,3,0,5,724,1,0,0,0,0),(729,'Warder\'s Alacrity',3,5,4294967295,4294967295,9179,9180,6,4294967295,0,0,0,0,32768,0,65,0,7,4294967295,3,0,5,729,1,0,0,0,0),(734,'Pet Affinity',12,1,4294967295,4294967295,9285,9286,6,4294967295,0,0,0,0,60448,0,65,0,7,4294967295,2,0,1,734,1,0,0,0,0),(735,'Mastery of the Past',3,3,4294967295,4294967295,9163,9164,6,4294967295,0,0,0,0,33884,0,62,0,7,4294967295,2,0,15,735,1,0,0,7050,0),(767,'Critical Affliction',3,3,4294967295,4294967295,9191,9192,6,4294967295,0,0,0,0,36208,0,65,3,7,4294967295,2,0,9,767,1,0,0,1099,0),(806,'Sinister Strikes',15,1,4294967295,4294967295,9231,9232,6,4294967295,198,1,0,0,33682,0,65,0,7,4294967295,2,0,1,806,1,0,0,0,0),(921,'Ro\'s Flaming Familiar',15,1,9269,4294967295,9269,9270,6,4833,0,0,8,60,4096,0,65,0,7,4294967295,2,0,1,921,1,0,0,0,0),(922,'E\'ci\'s Icy Familiar',15,1,9227,4294967295,9227,9228,6,4834,0,0,8,60,4096,0,65,0,7,4294967295,2,0,1,922,1,0,0,0,0),(923,'Druzzil\'s Mystical Familiar',15,1,9279,4294967295,9279,9280,6,4835,0,0,8,60,4096,0,65,0,7,4294967295,2,0,1,923,1,0,0,0,0),(924,'Advanced Fury of Magic Mastery',5,2,4294967295,4294967295,9265,9266,6,4294967295,640,3,0,0,4096,0,65,0,7,4294967295,2,0,8,267,1,0,6,0,0),(926,'Ward of Destruction',3,5,9319,4294967295,9319,9320,6,4836,0,0,12,1800,4096,0,65,0,7,4294967295,2,0,5,926,1,0,0,0,0),(931,'Frenzied Devastation',5,3,9340,4294967295,9340,9341,6,5245,92,1,13,4320,4096,0,65,3,7,4294967295,2,0,3,931,1,0,0,0,0),(978,'Eternal Breath',5,1,4294967295,4294967295,30012,30013,7,4294967295,676,2,0,0,65534,1,68,0,8,4294967295,1,0,6,71,1,0,5,0,0),(979,'Blacksmithing Mastery',3,3,4294967295,4294967295,30016,30017,7,4294967295,0,0,0,0,65534,1,59,3,7,6,1,0,3,979,1,0,0,0,0),(982,'Baking Mastery',3,3,4294967295,4294967295,30028,30029,7,4294967295,0,0,0,0,65534,1,59,3,7,6,1,0,3,982,1,0,0,0,0),(985,'Brewing Mastery',3,3,4294967295,4294967295,30040,30041,7,4294967295,0,0,0,0,65534,1,59,3,7,6,1,0,3,985,1,0,0,0,0),(988,'Fletching Mastery',3,3,4294967295,4294967295,30052,30053,7,4294967295,0,0,0,0,65534,1,59,3,7,6,1,0,3,988,1,0,0,0,0),(991,'Pottery Mastery',3,3,4294967295,4294967295,30064,30065,7,4294967295,0,0,0,0,65534,1,59,3,7,6,1,0,3,991,1,0,0,0,0),(994,'Tailoring Mastery',3,3,4294967295,4294967295,30076,30077,7,4294967295,0,0,0,0,65534,1,59,3,7,6,1,0,3,994,1,0,0,0,0),(997,'Salvage',5,3,4294967295,4294967295,30088,30089,7,4294967295,0,0,0,0,65534,1,60,0,7,6,1,0,3,997,1,0,0,0,5),(1000,'Origin',7,1,30102,4294967295,30100,30101,7,5824,0,0,20,4320,65534,1,67,0,8,4294967295,1,0,1,1000,1,0,0,0,0),(1001,'Chaotic Potential',5,5,4294967295,4294967295,30104,30105,7,4294967295,418,5,0,0,65534,1,66,0,8,4294967295,1,0,20,418,1,0,5,4678,1),(1006,'Discordant Defiance',5,5,4294967295,4294967295,30124,30125,7,4294967295,0,0,0,0,65534,1,66,0,8,4294967295,1,0,10,1006,1,0,0,7516,0),(1021,'Mystical Attuning',5,5,4294967295,4294967295,30144,30145,7,4294967295,0,0,0,0,65534,1,51,0,8,4294967295,1,0,12,1021,1,0,0,6521,0),(1026,'Delay Death',3,5,4294967295,4294967295,30164,30165,7,4294967295,0,0,0,0,65534,1,66,0,8,4294967295,1,0,25,1026,1,0,0,1389,0),(1031,'Healthy Aura',3,5,4294967295,4294967295,30184,30185,7,4294967295,674,2,0,0,65534,1,66,0,8,4294967295,1,0,8,110,1,0,3,0,0),(1041,'Veteran\'s Wrath',3,3,4294967295,4294967295,30224,30225,7,4294967295,443,3,0,0,33192,0,67,3,8,4294967295,2,0,3,1041,1,0,0,0,0),(1053,'Deathblow',3,3,4294967295,4294967295,30272,30273,7,4294967295,440,3,0,0,33722,1,66,0,8,4294967295,2,0,21,119,1,0,6,4721,0),(1061,'Reflexive Mastery',5,5,4294967295,4294967295,30304,30305,7,4294967295,449,5,0,0,65534,1,66,0,8,4294967295,1,0,33,125,1,0,8,1394,1),(1066,'Defensive Instincts',5,5,4294967295,4294967295,30324,30325,7,4294967295,454,5,0,0,65534,1,66,0,8,4294967295,1,0,33,122,1,0,8,1399,1),(1071,'Mnemonic Retention',3,1,4294967295,4294967295,30344,30345,7,4294967295,0,0,0,0,64892,0,55,0,8,4294967295,2,0,1,1071,1,0,0,0,0),(1072,'Expansive Mind',5,5,4294967295,4294967295,30348,30349,7,4294967295,0,0,0,0,64892,0,66,0,8,4294967295,2,0,5,1072,1,0,0,0,0),(1083,'Healing Adept Mastery',3,3,4294967295,4294967295,30392,30393,7,4294967295,434,3,0,0,33884,0,66,3,8,4294967295,2,0,12,77,1,0,6,12449,0),(1086,'Healing Gift Mastery',3,3,4294967295,4294967295,30404,30405,7,4294967295,437,3,0,0,33884,0,66,3,8,4294967295,2,0,21,80,1,0,6,4779,0),(1089,'Arcane Tongues',3,3,4294967295,4294967295,30416,30417,7,4294967295,0,0,0,0,30720,0,66,3,8,4294967295,2,0,3,1089,1,0,0,0,0),(1093,'Slippery Attacks',3,5,4294967295,4294967295,30432,30433,7,4294967295,0,0,0,0,33682,0,66,0,8,4294967295,2,0,5,1093,1,0,0,0,0),(1099,'Improved Critical Affliction',3,3,4294967295,4294967295,30456,30457,7,4294967295,767,3,0,0,36192,0,67,3,8,4294967295,2,0,9,767,1,0,3,12423,0),(1107,'Fury of Magic',3,3,4294967295,4294967295,5738,5739,7,4294967295,92,3,0,0,33080,0,66,3,8,4294967295,2,0,3,1107,1,0,0,0,0),(1119,'Roar of Thunder',3,3,30538,30539,30536,30537,7,5841,0,0,8,900,32768,0,68,3,8,4294967295,2,0,3,1119,1,0,0,0,0),(1122,'Persistent Minion',7,1,4294967295,4294967295,30548,30549,7,4294967295,526,2,0,0,60448,0,67,0,8,4294967295,2,0,1,1122,1,0,0,0,0),(1123,'Perfection of Spirit',5,3,30554,30555,30552,30553,7,5854,291,1,5,900,32768,0,66,0,8,4294967295,2,0,3,1123,1,0,0,0,0),(1126,'Replenish Companion',3,3,30566,30567,30564,30565,7,5845,163,1,2,2160,43008,0,66,3,8,4294967295,2,0,3,1126,1,0,0,0,0),(1129,'Advanced Pet Discipline',5,2,4294967295,4294967295,30576,30577,7,4294967295,288,1,0,0,44064,0,67,2,8,4294967295,2,0,3,288,1,0,1,0,0),(1181,'Shielding Resistance',3,5,4294967295,4294967295,1181,1181,7,4294967295,0,0,0,0,33682,0,70,0,8,4294967295,2,0,5,1181,1,0,0,0,0),(1210,'Destructive Fury',3,3,4294967295,4294967295,30900,30901,7,4294967295,640,3,0,0,4096,0,66,3,8,4294967295,2,0,9,1210,1,0,0,4755,0),(1229,'Secondary Recall',7,1,30978,30979,30976,30977,7,6094,0,0,10,600,4160,0,66,0,8,4294967295,2,0,1,1229,1,0,0,0,0),(1334,'Mind Crash',3,3,31410,31411,31408,31409,7,5943,0,0,11,4320,4096,0,66,3,8,4294967295,2,0,3,1334,1,0,0,0,0),(1337,'Prolonged Destruction',5,3,31422,31423,31420,31421,7,5946,931,3,13,4320,4096,0,67,0,8,4294967295,2,0,3,1337,1,0,0,0,0),(1340,'Ro\'s Greater Familiar',12,1,31434,31435,31432,31433,7,5950,921,1,8,60,4096,0,69,0,8,4294967295,2,0,1,921,1,0,0,0,0),(1341,'E\'ci\'s Greater Familiar',12,1,31438,31439,31436,31437,7,5951,922,1,8,60,4096,0,69,0,8,4294967295,2,0,1,922,1,0,0,0,0),(1342,'Druzzil\'s Greater Familiar',12,1,31442,31443,31440,31441,7,5952,923,1,8,60,4096,0,69,0,8,4294967295,2,0,1,923,1,0,0,0,0),(1343,'Teleport Bind',9,1,31446,31447,31444,31445,7,5953,0,0,9,300,4096,0,69,0,8,4294967295,2,0,1,1343,1,0,0,0,0),(1344,'Devoted Familiar',12,1,31450,31451,31448,31449,7,5949,533,1,8,60,4096,0,70,0,8,4294967295,2,0,2,155,1,0,0,0,0),(807,'Strikethrough',3,3,4294967295,4294967295,807,807,6,4294967295,0,0,0,0,128,0,65,3,7,4294967295,2,0,3,807,1,0,0,0,0),(810,'Stonewall',5,5,2147483647,2147483647,2147483647,0,6,0,0,0,0,0,128,0,65,0,7,4294967295,2,0,5,810,1,0,0,0,0),(815,'Rapid Strikes',4,5,4294967295,4294967295,815,815,6,0,0,0,0,0,128,0,65,0,7,4294967295,3,0,5,815,1,0,0,0,0),(820,'Kick Mastery',3,3,4294967295,4294967295,820,820,6,4294967295,0,0,0,0,128,0,65,3,7,4294967295,3,0,3,820,1,0,0,0,0),(823,'Heightened Awareness',5,5,4294967295,4294967295,823,823,6,4294967295,0,0,0,0,128,0,65,0,7,4294967295,3,0,5,823,1,0,0,0,0),(828,'Destructive Force',3,3,2147483647,2147483647,2147483647,2147483647,6,5240,0,0,2,3600,128,0,65,3,7,4294967295,2,0,3,828,1,0,0,0,0),(770,'Fury of Magic Mastery',3,3,4294967295,4294967295,4294967295,4294967295,6,0,637,3,0,0,27716,0,65,3,7,4294967295,2,0,6,637,1,0,3,0,0),(831,'Swarm of Decay',3,3,2147483647,2147483647,2147483647,2147483647,6,0,0,0,10,1800,2048,0,65,3,7,4294967295,2,0,3,831,1,0,0,0,0),(834,'Deaths Fury',3,5,4294967295,4294967295,834,834,6,0,0,0,0,0,2080,0,65,0,7,4294967295,3,0,5,834,1,0,0,0,0),(839,'Quickening of Death',5,5,4294967295,4294967295,839,839,6,0,0,0,0,0,2048,0,65,0,7,4294967295,3,0,5,839,1,0,0,0,0),(864,'Precision of the Pathfinder',3,3,4294967295,4294967295,864,864,6,0,0,0,0,0,16,0,65,3,7,4294967295,3,0,6,864,1,0,0,0,0),(867,'Coat of Thistles',5,5,4294967295,4294967295,867,867,6,0,0,0,0,0,16,0,65,0,7,4294967295,2,0,5,867,1,0,0,0,0),(872,'Flaming Arrows',3,3,2147483647,2147483647,2147483647,2147483647,6,4802,0,0,5,180,16,0,65,3,7,4294967295,2,0,3,872,1,0,0,0,0),(875,'Frost Arrows',3,3,2147483647,2147483647,2147483647,2147483647,6,4805,0,0,5,180,16,0,65,3,7,4294967295,2,0,3,875,1,0,0,0,0),(915,'Strengthened Strike',3,3,4294967295,4294967295,915,915,6,4294967295,0,0,0,0,16,0,65,3,7,4294967295,3,0,3,915,1,0,0,0,0),(683,'Heightened Endurance',3,3,4294967295,4294967295,683,683,6,4294967295,0,0,0,0,642,1,61,3,7,4294967295,2,0,15,683,1,0,0,1036,0),(846,'Triple Backstab',3,3,4294967295,4294967295,846,846,6,4294967295,0,0,0,0,512,0,65,3,7,4294967295,3,0,6,846,1,0,0,1301,0),(878,'Seized Opportunity',3,3,4294967295,4294967295,878,878,6,4294967295,0,0,0,0,512,0,65,3,7,4294967295,3,0,6,878,1,0,0,1539,0),(881,'Trap Circumvention',3,5,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,512,0,65,0,7,4294967295,2,0,5,881,1,0,0,0,0),(886,'Improved Hasty Exit',5,2,2147483647,2147483647,2147483647,2147483647,6,0,498,3,0,0,512,0,65,0,7,4294967295,2,0,5,498,1,0,2,0,0),(888,'Virulent Venom',3,5,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,512,0,65,0,7,4294967295,2,0,5,888,1,0,0,0,0),(893,'Improved Consumption of the Soul',5,2,2147483647,2147483647,2147483647,2147483647,6,0,589,3,0,0,32,0,65,0,7,4294967295,2,0,5,589,1,0,3,0,0),(849,'Hastened Piety',3,3,2147483647,2147483647,2147483647,2147483647,6,0,534,3,0,0,8,0,65,3,7,4294967295,2,0,3,849,1,0,0,0,0),(852,'Immobilizing Bash',5,3,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,40,0,65,0,7,4294967295,2,0,3,852,1,0,0,0,0),(855,'Vicious Smash',5,5,4294967295,4294967295,855,855,6,4294967295,0,0,0,0,40,0,65,0,7,4294967295,3,0,5,855,1,0,0,0,0),(860,'Radiant Cure',3,3,2147483647,2147483647,2147483647,2147483647,6,3297,0,0,5,180,8,0,65,3,7,4294967295,2,0,3,860,1,0,0,0,0),(863,'Purification',12,1,2147483647,2147483647,2147483647,2147483647,6,5248,0,0,6,4320,8,0,65,0,7,4294967295,2,0,1,863,1,0,0,0,0),(738,'Spell Casting Subtlety',3,3,4294967295,4294967295,738,738,6,0,0,0,0,0,1092,0,65,3,7,4294967295,2,0,12,738,1,0,0,5317,0),(754,'Quickened Curing',3,3,2147483647,2147483647,2147483647,2147483647,6,0,459,3,0,0,1092,0,65,3,7,4294967295,2,0,3,754,1,0,0,0,0),(900,'Advanced Spirit Call',5,2,2147483647,2147483647,2147483647,2147483647,6,4826,528,3,5,720,1024,0,65,0,7,4294967295,2,0,2,900,1,0,0,0,0),(907,'Sturdiness',5,5,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,2,0,65,0,7,4294967295,2,0,5,907,1,0,0,0,0),(918,'Extended Shielding',3,3,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,2,0,65,3,7,4294967295,2,0,3,918,1,0,0,0,0),(741,'Touch of the Divine',5,5,4294967295,4294967295,741,741,6,0,0,0,0,0,4,0,65,0,7,4294967295,3,0,5,741,1,0,0,0,0),(746,'Divine Avatar',3,3,2147483647,2147483647,2147483647,2147483647,6,4549,0,0,10,2160,4,0,65,3,7,4294967295,2,0,3,746,1,0,0,0,0),(749,'Exquisite Benediction',5,5,2147483647,2147483647,2147483647,2147483647,6,4790,0,0,13,1800,4,0,65,0,7,4294967295,2,0,5,749,1,0,0,0,0),(757,'Natures Boon',5,5,2147483647,2147483647,2147483647,2147483647,6,4796,0,0,6,1800,64,0,65,0,7,4294967295,2,0,5,757,1,0,0,0,0),(762,'Advanced Tracking',4,5,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,64,0,65,0,7,4294967295,2,0,5,762,1,0,0,0,0),(844,'Advanced Theft of Life',5,2,4294967295,4294967295,31453,31454,6,4294967295,634,3,0,0,2080,0,65,0,7,4294967295,3,0,8,634,1,0,3,1319,0),(1140,'Dead Aim',3,3,4294967295,4294967295,1140,1140,5,4294967295,0,0,0,0,0,1,61,0,5,4294967295,3,0,3,1140,1,0,0,0,0),(1149,'Desperation',12,1,2147483647,2147483647,2147483647,2147483647,6,5853,0,0,5,1320,0,1,65,0,6,4294967295,2,0,1,1149,1,0,0,0,0),(1150,'Untamed Rage',3,3,1150,1150,1150,1150,6,5848,564,3,2,2160,0,1,65,3,7,4294967295,3,0,3,1150,1,0,0,0,0),(1155,'Echoing Cries',3,3,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,0,1,65,3,6,4294967295,2,0,3,1155,1,0,0,0,0),(773,'Doppelganger',3,3,2147483647,0,2147483647,2147483647,6,4552,643,1,5,1800,16384,0,65,3,7,4294967295,2,0,3,773,1,0,0,0,0),(776,'Enhanced Forgetfulness',3,5,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,16384,0,65,0,7,4294967295,2,0,5,776,1,0,0,0,0),(781,'Mesmerization Mastery',12,1,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,16384,0,65,0,7,4294967295,2,0,1,781,1,0,0,0,0),(782,'Quick Mass Group Buff',3,3,2147483647,2147483647,2147483647,2147483647,6,0,128,1,0,0,16384,0,65,3,7,4294967295,2,0,3,782,1,0,0,0,0),(785,'Shared Health',5,5,2147483647,2147483647,2147483647,2147483647,6,5235,0,0,8,900,8192,0,65,0,7,4294967295,2,0,5,785,1,0,0,0,0),(790,'Elemental Fury',3,5,4294967295,4294967295,790,790,6,0,0,0,0,0,8192,0,65,0,7,4294967295,3,0,5,790,1,0,0,0,0),(795,'Elemental Alacrity',5,5,4294967295,4294967295,795,795,6,0,0,0,0,0,8192,0,65,0,7,4294967295,3,0,5,795,1,0,0,0,0),(800,'Elemental Agility',3,3,4294967295,4294967295,800,800,6,0,0,0,0,0,8192,0,65,3,7,4294967295,3,0,3,800,1,0,0,0,0),(803,'Elemental Durability',3,3,4294967295,4294967295,803,803,6,0,0,0,0,0,8192,0,65,3,7,4294967295,3,0,3,803,1,0,0,0,0),(695,'Tune of Pursuance',4,5,2147483647,2147483647,2147483647,2147483647,6,0,0,0,0,0,256,0,65,0,7,4294967295,2,0,5,695,1,0,0,0,0),(700,'Improved Instrument Mastery',9,1,4294967295,4294967295,2147483647,2147483647,6,0,213,3,0,0,256,0,61,0,7,4294967295,3,0,4,213,1,0,3,0,0),(701,'Improved Singing Mastery',9,1,4294967295,4294967295,2147483647,2147483647,6,0,275,3,0,0,256,0,61,0,7,4294967295,3,0,4,275,1,0,3,0,0),(702,'Exultant Bellowing',3,5,2147483647,2147483647,2147483647,2147483647,6,4842,592,1,2,18,256,0,65,0,7,4294967295,2,0,5,702,1,0,0,0,0),(707,'Echo of Taelosia',5,3,2147483647,2147483647,2147483647,2147483647,6,0,571,3,0,0,256,0,65,0,7,4294967295,2,0,3,707,1,0,0,0,0),(710,'Internal Metronome',5,5,4294967295,4294967295,710,710,6,0,96,3,0,0,256,0,65,0,7,4294967295,2,0,5,710,1,0,0,0,0),(1319,'Soul Thief',5,3,4294967295,4294967295,31457,31458,7,4294967295,844,2,0,0,2080,0,68,0,8,4294967295,3,0,8,634,1,0,5,0,0),(209,'Death Peace',5,1,13738,13739,13736,13737,7,4294967295,0,0,12,5,2080,0,65,0,3,4294967295,2,0,1,209,1,0,0,0,0),(1134,'Blur of Axes',3,3,4294967295,4294967295,1134,1134,5,4294967295,0,0,0,0,0,1,61,1,4,4294967295,3,0,8,1134,1,0,0,1158,0),(1158,'Vicious Frenzy',4,5,4294967295,4294967295,1158,1158,7,4294967295,1134,3,0,0,0,1,67,0,8,4294967295,3,0,8,1134,1,0,3,0,0),(1373,'Chaotic Jester',0,1,1373,1373,1373,1373,8,6882,0,0,25,72000,65534,1,1,0,8,5,4,0,1,1373,1,0,0,0,0),(1371,'Lesson of the Devoted',0,1,1371,1371,1371,1371,8,6880,0,0,23,72000,65534,1,1,0,8,5,4,0,1,1371,1,0,0,0,0),(1372,'Infusion of the Faithful',0,1,1372,1372,1372,1372,8,6881,0,0,24,72000,65534,1,1,0,8,5,4,0,1,1372,1,0,0,0,0),(1374,'Expedient Recovery',0,1,1374,1374,1374,1374,8,6883,0,0,26,590400,65534,1,1,0,8,5,4,0,1,1374,1,0,0,0,0),(1376,'Staunch Recovery',0,1,1376,1376,1376,1376,8,6885,0,0,28,259200,65534,1,1,0,8,5,4,0,1,1376,1,0,0,0,0),(1375,'Steadfast Servant',0,1,1375,1375,1375,1375,8,6884,0,0,27,72000,65534,1,1,0,8,5,4,0,1,1375,1,0,0,0,0),(1377,'Intensity of the Resolute',0,1,1377,1377,1377,1377,8,6886,0,0,29,14400,65534,1,1,0,8,5,4,0,1,1377,1,0,0,0,0),(1287,'Shield Block',3,3,4294967295,4294967295,1287,1287,7,0,0,0,0,0,42,0,67,3,8,4294967295,2,3,9,1287,1,0,0,5516,0),(7800,'Harm Touch',0,17,7800,7800,7800,7800,3,13531,0,0,39,4320,32,0,1,1,0,9,3,0,17,7800,4,0,0,0,0),(7850,'Lay on Hands',0,17,7850,7850,7850,7850,3,13546,0,0,39,4320,8,0,1,1,0,9,3,0,17,7850,4,0,0,0,0),(1131,'Throwing Mastery',3,3,4294967295,4294967295,1131,1131,5,4294967295,0,0,0,0,0,1,61,3,3,4294967295,3,0,3,1131,1,0,0,0,0),(1146,'Tireless Sprint',3,3,2147483647,2147483647,2147483647,2147483647,5,0,0,0,0,0,0,1,61,3,5,4294967295,3,0,3,1146,1,0,0,0,0),(1137,'Hastened War Cry',3,3,2147483647,2147483647,2147483647,2147483647,5,0,0,0,0,0,0,1,61,3,5,4294967295,3,0,3,1137,1,0,0,0,0),(1435,'Gift of Mana',3,3,4294967295,4294967295,1435,1435,7,0,83,3,0,0,31812,0,66,3,10,4294967295,2,3,3,1435,1,0,0,0,0),(1486,'Abundant Healing',5,5,4294967295,4294967295,1486,1486,7,0,1086,3,0,0,1092,0,66,0,10,4294967295,2,0,15,1486,1,0,0,5529,0),(1478,'Pyromancy',3,3,1478,4294967295,1478,1478,7,8406,0,0,99,1,4096,0,66,0,10,4294967295,3,0,3,1478,2,0,0,0,0),(1378,'Curse of Blood',0,1,4294967295,4294967295,1378,1378,8,0,0,0,0,0,65534,1,0,0,5,2,4,0,1,1378,1,0,0,0,0),(1379,'Affliction of Blood',0,1,4294967295,4294967295,1379,1379,8,0,1378,1,0,0,65534,1,0,0,5,2,4,0,1,1379,1,0,0,0,0),(1380,'Torment of Blood',0,1,4294967295,4294967295,1380,1380,8,0,1379,1,0,0,65534,1,0,0,5,2,4,0,1,1380,1,0,0,0,0),(1381,'Temptation of Blood',0,1,4294967295,4294967295,1381,1381,8,0,1380,1,0,0,65534,1,0,0,5,2,4,0,1,1381,1,0,0,0,0),(1382,'Invitation of Blood',0,1,4294967295,4294967295,1382,1382,8,0,1381,1,0,0,65534,1,0,0,5,2,4,0,1,1382,1,0,0,0,0),(1011,'Trials of Mata Muram',0,6,4294967295,4294967295,1011,1011,7,0,0,0,0,0,65534,1,0,0,0,1,4,0,6,1011,1,0,0,0,0),(1361,'Gift of the Dark Reign',0,1,4294967295,4294967295,1361,1361,8,0,0,0,0,0,65534,1,0,0,0,2,4,0,1,1361,1,0,0,0,0),(1362,'Tenacity of the Dark Reign',0,1,4294967295,4294967295,1362,1362,8,0,1361,1,0,0,65534,1,0,0,0,2,4,0,1,1362,1,0,0,0,0),(1363,'Embrace of the Dark Reign',0,1,4294967295,4294967295,1363,1363,8,0,1362,1,0,0,65534,1,0,0,0,2,4,0,1,1363,1,0,0,0,0),(1364,'Power of the Dark Reign',0,1,4294967295,4294967295,1364,1364,8,0,1363,1,0,0,65534,1,0,0,0,2,4,0,1,1364,1,0,0,0,0),(1365,'Fervor of the Dark Reign',0,1,4294967295,4294967295,1365,1365,8,0,1364,1,0,0,65534,1,0,0,0,2,4,0,1,1365,1,0,0,0,0),(1366,'Gift of the Keepers',0,1,4294967295,4294967295,1366,1366,8,0,0,0,0,0,65534,1,0,0,0,2,4,0,1,1366,1,0,0,0,0),(1367,'Valor of the Keepers',0,1,4294967295,4294967295,1367,1367,8,0,1366,1,0,0,65534,1,0,0,0,2,4,0,1,1367,1,0,0,0,0),(1368,'Embrace of the Keepers',0,1,4294967295,4294967295,1368,1368,8,0,1367,1,0,0,65534,1,0,0,0,2,4,0,1,1368,1,0,0,0,0),(1369,'Power of the Keepers',0,1,4294967295,4294967295,1369,1369,8,0,1368,1,0,0,65534,1,0,0,0,2,4,0,1,1369,1,0,0,0,0),(1370,'Sanctity of the Keepers',0,1,4294967295,4294967295,1370,1370,8,0,1369,1,0,0,65534,1,0,0,0,2,4,0,1,1370,1,0,0,0,0),(5150,'Breath of Atathus',0,15,5150,5150,5150,5150,10,11112,0,0,80,300,65534,1,0,0,523,8,4,0,15,5150,4,0,0,0,0),(5165,'Breath of Draton\'ra',0,15,5165,5165,5165,5165,10,11127,0,0,80,300,65534,1,0,0,524,8,4,0,15,5165,4,0,0,0,0),(5180,'Breath of Osh\'vir',0,15,5180,5180,5180,5180,10,11142,0,0,80,300,65534,1,0,0,525,8,4,0,15,5180,4,0,0,0,0),(5195,'Breath of Venesh',0,15,5195,5195,5195,5195,10,11157,0,0,80,300,65534,1,0,0,526,8,4,0,15,5195,4,0,0,0,0),(5210,'Breath of Mysaphar',0,15,5210,5210,5210,5210,10,11172,0,0,80,300,65534,1,0,0,527,8,4,0,15,5210,4,0,0,0,0),(5225,'Breath of Keikolin',0,15,5225,5225,5225,5225,10,11187,0,0,80,300,65534,1,0,0,528,8,4,0,15,5225,4,0,0,0,0),(1647,'Harmonic Dissonance',0,1,1647,1647,1647,1647,10,8771,0,0,81,30,65534,1,0,0,0,2,4,0,1,1647,4,0,0,0,0),(4702,'Glyph of Dragon Scales',3,1,4702,4702,4702,4702,4,9475,0,0,98,600,65534,1,71,0,12,7,4,0,1,4702,4,0,0,0,0),(4703,'Glyph of Angry Thoughts',4,1,52004,52004,52004,52004,4,12752,0,0,97,600,65534,1,76,0,14,7,4,0,1,52004,4,0,0,0,0),(4704,'Glyph of Arcane Secrets',3,1,4704,4704,4704,4704,4,9477,0,0,96,600,65534,1,71,0,12,7,4,0,1,4704,4,0,0,0,0),(4705,'Glyph of Draconic Potential',3,1,4705,4705,4705,4705,4,9478,0,0,95,600,65534,1,71,0,12,7,4,0,1,4705,4,0,0,0,0),(4706,'Glyph of Destruction',3,1,4706,4706,4706,4706,4,9479,0,0,94,600,65534,1,71,0,12,7,4,0,1,4706,4,0,0,0,0),(4707,'Glyph of Courage',4,1,52000,52000,52000,52000,4,12748,0,0,93,600,65534,1,76,0,14,7,4,0,1,52000,4,0,0,0,0),(4708,'Glyph of Frantic Infusion',4,1,52003,52003,52003,52003,4,12751,0,0,92,600,65534,1,76,0,14,7,4,0,1,52003,4,0,0,0,0),(4709,'Glyph of Stored Life',4,1,52002,52002,52002,52002,4,12750,0,0,91,600,65534,1,76,0,14,7,4,0,1,52002,4,0,0,0,0),(4710,'Glyph of Recovery',4,1,52001,52001,52001,52001,4,12749,0,0,90,600,65534,1,76,0,14,7,4,0,1,52001,4,0,0,0,0),(4665,'Throne of Heroes',0,1,4665,4665,4665,4665,8,9177,0,0,30,4320,65534,1,1,0,8,4294967295,4,0,1,4665,4,0,0,0,0),(1597,'Call of Challenge',9,1,1597,1597,1597,1597,7,8271,0,0,6,10,2,0,70,0,10,4294967295,3,0,1,1597,1,0,0,0,0),(912,'Warlord\'s Tenacity',3,3,912,912,912,912,6,4925,0,0,4,3600,2,0,65,3,7,4294967295,3,0,6,912,1,0,0,0,0),(1583,'Hastened Defiance',5,1,4294967295,4294967295,1583,1583,7,4294967295,912,3,0,0,2,0,70,0,10,4294967295,3,0,1,1583,1,0,0,0,0),(1586,'Dauntless Perseverance',12,1,4294967295,4294967295,1586,1586,7,4294967295,222,3,0,0,2,0,70,0,10,4294967295,3,0,1,1586,1,0,0,0,0),(1611,'Field Dressing',3,5,4294967295,4294967295,1611,1611,7,4294967295,537,2,0,0,2,1,66,0,10,4294967295,3,0,5,1417,1,0,0,0,0),(5136,'Combat Medic',3,5,4294967295,4294967295,4688,4688,3,4294967295,1611,5,0,0,2,1,71,0,12,4294967295,3,0,5,4688,4,0,0,0,1),(1587,'Concentration',3,3,4294967295,4294967295,1587,1587,7,4294967295,0,0,0,0,642,1,66,0,10,4294967295,2,0,3,1587,1,0,0,0,0),(1621,'Relentless Assault',7,3,4294967295,4294967295,1621,1621,7,4294967295,564,3,0,0,658,1,70,0,10,4294967295,2,0,6,564,1,0,3,0,0),(1330,'Resolute Defiance',3,3,912,912,1330,1330,7,4925,912,3,4,3600,2,0,70,3,8,4294967295,3,0,6,912,1,0,0,0,0),(1536,'Wicked Blade',3,3,4294967295,4294967295,1536,1536,7,4294967295,599,3,0,0,146,1,70,0,10,4294967295,2,0,12,599,1,0,3,5534,0),(1388,'Innate See Invis',9,1,4294967295,4294967295,1388,1388,7,0,0,0,0,0,65534,1,70,0,10,4294967295,1,0,1,1388,1,0,0,0,0),(1627,'Selo\'s Enduring Cadence',3,3,4294967295,4294967295,1627,1627,7,4294967295,672,2,0,0,256,0,66,0,10,4294967295,3,0,3,1627,1,0,0,0,0),(1301,'Flurry of Knives',3,3,4294967295,4294967295,1301,1301,7,4294967295,846,3,0,0,512,0,66,3,8,4294967295,3,0,6,846,1,0,3,0,0),(1539,'Forced Opening',3,3,4294967295,4294967295,1539,1539,7,4294967295,878,3,0,0,512,0,70,0,10,4294967295,3,0,6,878,1,0,3,0,0),(1624,'Knight\'s Expertise',7,3,4294967295,4294967295,1624,1624,7,4294967295,561,3,0,0,40,0,70,0,10,4294967295,3,0,6,561,1,0,3,0,0),(1050,'Veteran\'s Wrath',3,3,4294967295,4294967295,1050,1050,7,4294967295,443,3,0,0,2,1,67,3,8,4294967295,2,0,3,1050,1,0,0,0,0),(1044,'Veteran\'s Wrath',3,3,4294967295,4294967295,1044,1044,7,4294967295,443,3,0,0,512,0,67,3,8,4294967295,2,0,3,1044,1,0,0,0,0),(1047,'Veteran\'s Wrath',3,3,4294967295,4294967295,1047,1047,7,4294967295,443,3,0,0,16,0,67,3,8,4294967295,2,0,3,1047,1,0,0,0,0),(1524,'Vanquish Undead',7,3,4294967295,4294967295,1524,1524,10,4294967295,190,3,0,0,8,0,70,0,10,4294967295,3,0,6,190,1,0,3,0,0),(8201,'Companion\'s Fury',7,3,4294967295,4294967295,8201,8201,6,4294967295,0,0,0,0,60448,0,81,0,15,4294967295,2,0,3,8201,5,0,0,0,2),(1327,'Ancestral Aid',5,3,1327,1327,1327,1327,7,5933,0,0,2,900,1024,0,67,0,8,4294967295,3,0,3,1327,1,0,0,0,0),(1323,'Spiritual Channeling',12,1,1323,1323,1323,1323,1,5932,146,1,0,2160,1024,0,70,0,8,4294967295,3,0,1,1323,1,0,0,0,0),(1394,'Precognition',5,5,4294967295,4294967295,1394,1394,7,4294967295,1061,5,0,0,65534,1,70,0,10,4294967295,1,0,33,125,1,0,13,5519,0),(5519,'Combat Agility V',6,5,4294967295,4294967295,125,125,7,4294967295,1394,5,0,0,65534,1,76,1,14,4294967295,1,0,33,125,4,0,18,7501,1),(1290,'Scout\'s Efficiency',3,3,4294967295,4294967295,5519,1290,7,4294967295,864,3,0,0,16,0,70,3,8,4294967295,3,0,6,864,1,0,3,0,0),(1399,'Thick Skin',5,5,4294967295,4294967295,1399,1399,7,4294967295,1066,5,0,0,65534,1,70,0,10,4294967295,1,0,33,122,1,0,13,5524,0),(5524,'Combat Stability V',6,5,4294967295,4294967295,122,122,7,4294967295,1399,5,0,0,65534,1,76,1,14,4294967295,1,0,33,122,4,0,18,7506,1),(6287,'Warlord\'s Return Kick',6,3,4294967295,4294967295,6566,6566,3,4294967295,0,0,0,0,2,0,73,0,14,4294967295,3,0,3,6566,4,0,0,0,0),(7106,'Beastlords Feral Kick',6,3,4294967295,4294967295,7106,7106,3,4294967295,0,0,0,0,32768,0,73,0,14,4294967295,3,0,3,7106,4,0,0,0,0),(6269,'Hunter\'s Return Kick',6,3,4294967295,4294967295,6558,6558,3,4294967295,0,0,0,0,16,0,73,0,14,4294967295,3,0,3,6558,4,0,0,0,0),(6266,'Knight\'s Return Strike',6,3,4294967295,4294967295,6557,6557,3,4294967295,0,0,0,0,40,0,73,0,14,4294967295,3,0,3,6557,4,0,0,0,0),(1166,'Frenzied Defense',3,3,4294967295,4294967295,1143,1143,6,4294967295,0,0,0,0,0,1,65,0,7,4294967295,3,0,3,1143,4,0,0,0,0),(6275,'Knave\'s Return Strike',6,3,4294967295,4294967295,6560,6560,3,4294967295,0,0,0,0,512,0,73,0,14,4294967295,3,0,3,6560,4,0,0,0,0),(1163,'Crazed Onslaught',5,3,4294967295,4294967295,116,116,7,4294967295,542,3,0,0,2,1,67,0,8,4294967295,3,0,12,255,1,0,6,4795,0),(4795,'Flurry IV',5,3,4294967295,4294967295,255,255,7,4294967295,1163,3,0,0,2,1,71,0,12,4294967295,3,0,12,255,4,0,9,0,2),(1616,'Lightning Strikes',5,3,4294967295,4294967295,13785,13786,7,4294967295,0,0,0,0,16,1,66,3,10,4294967295,3,0,3,1616,1,0,0,0,0),(5061,'Petrified Roots ',5,1,4294967295,4294967295,567,567,7,4294967295,567,1,0,0,64,0,71,0,14,4294967295,3,0,2,567,4,0,1,0,0),(8263,'Earthen Brawn',5,5,4294967295,4294967295,8263,8263,1,4294967295,7357,5,0,0,65534,1,81,0,16,4294967295,1,0,5,8263,6,0,0,0,0),(8268,'Earthen Stability',5,5,4294967295,4294967295,8268,8268,1,4294967295,7357,5,0,0,65534,1,81,0,16,4294967295,1,0,5,8268,6,0,0,0,0),(8273,'Earthen Alacrity',5,5,4294967295,4294967295,8273,8273,1,4294967295,7357,5,0,0,65534,1,81,0,16,4294967295,1,0,5,8273,6,0,0,0,0),(8278,'Earthen Artistry',5,5,4294967295,4294967295,8278,8278,1,4294967295,7357,5,0,0,65534,1,81,0,16,4294967295,1,0,5,8278,6,0,0,0,0),(8283,'Earthen Sagacity',5,5,4294967295,4294967295,8283,8283,1,4294967295,7357,5,0,0,65534,1,81,0,16,4294967295,1,0,5,8283,6,0,0,0,0),(8288,'Earthen Brilliance',5,5,4294967295,4294967295,8288,8288,1,4294967295,7357,5,0,0,65534,1,81,0,16,4294967295,1,0,5,8288,6,0,0,0,0),(8293,'Earthen Allure',5,5,4294967295,4294967295,8293,8293,1,4294967295,7357,5,0,0,65534,1,81,0,16,4294967295,1,0,5,8293,6,0,0,0,0),(4678,'Planar Power III',5,5,4294967295,4294967295,418,418,4,4294967295,1001,5,0,0,65534,1,71,0,12,4294967295,1,0,20,418,4,0,10,7547,1),(7547,'Planar Power IV',5,5,4294967295,4294967295,418,418,4,4294967295,4678,5,0,0,65534,1,81,0,15,4294967295,1,0,20,418,5,0,15,0,1),(12423,'Critical Affliction III',9,3,4294967295,4294967295,767,767,7,4294967295,1099,3,0,0,36192,0,81,3,16,4294967295,2,0,9,767,6,0,6,0,0),(7647,'Persistent Casting II',6,3,4294967295,4294967295,692,692,6,4294967295,692,3,0,0,64892,0,81,0,15,4294967295,2,0,9,692,5,0,3,7670,2),(7670,'Persistent Casting III',7,3,4294967295,4294967295,692,692,6,4294967295,7647,3,0,0,64892,0,85,2,16,4294967295,2,0,9,692,6,0,6,0,0),(8224,'Cascade of Life',5,3,4294967295,4294967295,8224,8224,7,4294967295,1486,5,0,0,1092,0,75,0,15,4294967295,2,0,3,8224,5,0,0,0,5),(5529,'Abundant Healing II',7,5,4294967295,4294967295,1486,1486,7,0,1486,5,0,0,1092,0,76,2,14,4294967295,2,0,15,1486,4,0,5,7554,1),(7554,'Abundant Healing III',7,5,4294967295,4294967295,1486,1486,7,0,5529,5,0,0,1092,0,81,2,15,4294967295,2,0,15,1486,5,0,10,0,1),(4755,'Destructive Fury II',6,3,4294967295,4294967295,1210,1210,7,4294967295,1210,3,0,0,4096,0,71,1,12,4294967295,2,0,9,1210,4,0,3,0,2),(5557,'Enhanced Aggression III',5,5,4294967295,4294967295,1592,8232,7,4294967295,4725,5,0,0,33722,1,76,0,14,4294967295,2,0,25,1592,4,0,10,7568,1),(7582,'Destructive Fury IV',8,3,4294967295,4294967295,1210,1210,7,4294967295,5557,3,0,0,27716,0,81,2,15,4294967295,2,0,9,1210,5,0,9,0,2),(4779,'Healing Gift IV',6,3,4294967295,4294967295,30404,30405,7,4294967295,1086,3,0,0,33884,0,71,1,12,4294967295,2,0,21,80,4,0,9,5592,2),(5592,'Healing Gift V',7,3,4294967295,4294967295,30404,30405,7,4294967295,4779,3,0,0,33884,0,76,2,14,4294967295,2,0,21,80,4,0,12,7590,2),(7590,'Healing Gift VI',7,3,4294967295,4294967295,30404,30405,7,4294967295,5592,3,0,0,33884,0,81,2,15,4294967295,2,0,21,80,5,0,15,12454,2),(12454,'Healing Gift VII',12,3,4294967295,4294967295,30404,30405,7,4294967295,7590,0,0,0,33884,0,85,2,16,4294967295,2,0,21,80,6,0,18,0,0),(4773,'Gift of Radiant Mana',9,1,4294967295,4294967295,4773,4773,7,0,1435,3,0,0,31812,0,71,0,12,4294967295,2,0,1,4773,4,0,0,0,0),(6517,'Gift of Exquisite Radiant Mana',9,1,4294967295,4294967295,4773,4773,7,0,4773,1,0,0,31812,0,76,0,14,4294967295,2,0,1,6517,4,0,0,0,0),(7621,'Gift of Amazing Exquisite Radiant Mana',9,1,4294967295,4294967295,4773,4773,7,0,6517,1,0,0,31812,0,81,0,15,4294967295,2,0,1,6517,5,0,0,0,0),(8235,'Armor of Wisdom',6,5,4294967295,4294967295,8235,8235,7,4294967295,0,0,0,0,2,0,81,0,15,4294967295,2,0,10,8235,5,0,0,8305,1),(8305,'Armor of Wisdom II',6,5,4294967295,4294967295,8235,8235,7,4294967295,0,0,0,0,2,0,85,0,16,4294967295,2,0,10,8235,6,0,5,0,0),(4721,'Finishing Blow IV',3,3,4294967295,4294967295,119,119,7,4294967295,1053,3,0,0,33722,1,71,0,12,4294967295,2,0,21,119,4,0,9,5554,2),(5554,'Finishing Blow V',7,3,4294967295,4294967295,119,119,7,4294967295,4721,3,0,0,33722,1,76,2,14,4294967295,2,0,21,119,4,0,12,7565,2),(7565,'Finishing Blow VI',7,3,4294967295,4294967295,119,119,7,4294967295,5554,3,0,0,33722,1,81,2,15,4294967295,2,0,21,119,4,0,15,6023,2),(6023,'Finishing Blow VII',12,3,4294967295,4294967295,119,119,7,4294967295,7565,3,0,0,33722,1,85,0,16,4294967295,2,0,21,119,6,0,18,0,0),(4739,'Killing Spree',3,3,4294967295,4294967295,4739,4739,7,4294967295,0,0,0,0,642,1,71,3,12,4294967295,2,0,9,4739,4,0,0,5562,2),(5562,'Killing Spree II',7,3,4294967295,4294967295,4739,4739,7,4294967295,4739,3,0,0,642,1,76,2,14,4294967295,2,0,9,4739,4,0,3,7573,2),(7573,'Killing Spree III',7,3,4294967295,4294967295,4739,4739,7,4294967295,5562,3,0,0,642,1,76,2,15,4294967295,2,0,9,4739,5,0,6,0,0),(8198,'Empowered Ingenuity',4,3,4294967295,4294967295,8198,8198,7,4294967295,625,3,0,0,642,1,65,0,15,4294967295,2,0,3,8198,5,0,0,0,0),(4733,'Ingenuity II',3,3,4294967295,4294967295,4733,4733,5,4294967295,625,3,0,0,642,1,71,0,12,4294967295,2,0,9,625,4,0,3,7641,2),(7641,'Ingenuity III',3,3,4294967295,4294967295,4733,4733,5,4294967295,4733,3,0,0,642,1,76,0,15,4294967295,2,0,9,625,5,0,6,0,0),(1592,'Enhanced Aggression',5,5,4294967295,4294967295,1592,1592,7,4294967295,0,0,0,0,33722,1,70,0,10,4294967295,2,0,25,1592,1,0,0,4725,0),(4725,'Enhanced Aggression II',5,5,4294967295,4294967295,1592,1592,7,4294967295,1592,5,0,0,33722,1,71,0,12,4294967295,2,0,25,1592,4,0,5,5557,1),(7568,'Enhanced Aggression IV',5,5,4294967295,4294967295,1592,1592,7,4294967295,5557,5,0,0,33722,1,81,0,15,4294967295,2,0,25,1592,5,0,15,12439,1),(12439,'Enhanced Aggression V',5,5,4294967295,4294967295,1592,1592,7,4294967295,7568,5,0,0,33722,1,85,0,16,4294967295,2,0,25,1592,6,0,20,0,0),(8232,'Extended Ingenuity',4,3,4294967295,4294967295,8232,8232,7,4294967295,0,0,0,0,642,1,65,0,15,4294967295,2,0,4,8232,5,0,0,8261,0),(8261,'Extended Ingenuity',4,1,4294967295,4294967295,8232,8232,7,4294967295,8232,5,0,0,642,1,80,0,16,4294967295,2,0,4,8232,6,0,3,0,0),(10367,'Ageless Enmity',12,1,10367,10367,10367,10367,7,21751,0,0,30,360,2,0,85,0,16,4294967295,2,0,1,10367,6,0,0,0,0),(5534,'Punishing Blade III',8,3,4294967295,4294967295,599,599,7,4294967295,1536,3,0,0,146,1,76,2,14,4294967295,2,0,12,599,4,0,6,7659,2),(7659,'Punishing Blade IV',8,3,4294967295,4294967295,599,599,7,4294967295,5534,3,0,0,146,1,81,2,15,4294967295,2,0,12,599,5,0,9,0,2),(8215,'Hard Endurance',5,5,4294967295,4294967295,8215,8215,7,4294967295,0,0,0,0,642,1,65,0,15,4294967295,2,0,5,8215,5,0,0,0,5),(1036,'Fitness',5,3,4294967295,4294967295,1036,1036,7,4294967295,683,3,0,0,642,1,66,0,8,4294967295,2,0,15,683,1,0,3,5130,0),(5130,'Heightened Endurance III',5,5,4294967295,4294967295,683,683,7,4294967295,1036,3,0,0,642,1,71,0,12,4294967295,2,0,15,683,1,0,6,6080,1),(6080,'Heightened Endurance IV',5,2,4294967295,4294967295,683,683,7,4294967295,5130,5,0,0,642,1,75,0,14,4294967295,2,0,15,683,4,0,11,7604,0),(7604,'Heightened Endurance IV',5,2,4294967295,4294967295,683,683,7,4294967295,6080,2,0,0,642,1,80,0,15,4294967295,2,0,15,683,5,0,13,0,0),(5516,'Shield Block II',8,3,4294967295,4294967295,1287,1287,7,0,1287,3,0,0,42,0,76,1,14,4294967295,2,0,9,1287,4,0,3,12469,2),(12469,'Shield Block III',7,3,4294967295,4294967295,1287,1287,7,0,5516,3,0,0,42,0,81,2,16,4294967295,2,0,9,1287,6,0,6,0,0),(5317,'Spell Casting Subtlety II',4,3,4294967295,4294967295,738,738,6,0,738,3,0,0,1092,0,71,0,12,4294967295,2,0,12,738,4,0,3,5628,2),(5628,'Spell Casting Subtlety III',7,3,4294967295,4294967295,738,738,6,0,5317,3,0,0,1092,0,76,2,14,4294967295,2,0,12,738,4,0,6,7583,2),(7583,'Spell Casting Subtlety IV',7,3,4294967295,4294967295,738,738,6,0,5628,3,0,0,1092,0,81,2,15,4294967295,2,0,12,738,5,0,9,0,2),(12449,'Healing Adept IV',10,3,4294967295,4294967295,77,77,7,4294967295,1083,3,0,0,33884,0,81,1,16,4294967295,2,0,12,77,6,0,9,0,0),(7501,'Combat Agility VI',7,5,4294967295,4294967295,125,125,7,4294967295,5519,5,0,0,65534,1,81,1,15,4294967295,1,0,33,125,5,0,23,12396,1),(12396,'Combat Agility VII',8,5,4294967295,4294967295,125,125,7,4294967295,7501,5,0,0,65534,1,85,1,16,4294967295,1,0,33,125,6,0,28,0,0),(7506,'Combat Stability VI',7,5,4294967295,4294967295,122,122,7,4294967295,5524,5,0,0,65534,1,81,1,15,4294967295,1,0,33,122,5,0,23,12401,1),(12401,'Combat Stability VII',8,5,4294967295,4294967295,122,122,7,4294967295,7506,5,0,0,65534,1,85,1,16,4294967295,1,0,33,122,6,0,28,0,0),(1389,'Delay Death II',3,5,4294967295,4294967295,1206,1206,1,4294967295,1026,5,0,0,65534,1,70,0,10,4294967295,1,0,25,1026,1,0,5,4683,0),(4683,'Delay Death III',3,5,4294967295,4294967295,1206,1206,1,4294967295,1389,5,0,0,65534,1,71,0,12,4294967295,1,0,25,1026,4,0,10,6523,1),(6523,'Delay Death IV',3,5,4294967295,4294967295,1206,1206,1,4294967295,4683,5,0,0,65534,1,76,0,14,4294967295,1,0,25,1026,4,0,15,7511,1),(7511,'Delay Death V',3,5,4294967295,4294967295,1206,1206,1,4294967295,6523,5,0,0,65534,1,81,0,15,4294967295,1,0,25,1026,5,0,20,0,1),(1056,'Energetic Attunement',3,5,4294967295,4294967295,1206,1206,1,4294967295,0,0,0,0,65534,1,71,0,12,4294967295,1,0,15,1056,4,0,0,6431,1),(6431,'Energetic Attunement II',3,5,4294967295,4294967295,1643,1643,1,4294967295,1056,5,0,0,65534,1,76,0,14,4294967295,1,0,15,1056,4,0,5,7522,1),(7522,'Energetic Attunement III',3,5,4294967295,4294967295,1643,1643,1,4294967295,6431,5,0,0,65534,1,81,0,15,4294967295,1,0,15,1056,5,0,10,0,1),(6119,'General Sturdiness',6,5,4294967295,4294967295,6119,6119,1,4294967295,0,0,0,0,65534,1,76,0,14,4294967295,1,0,15,6119,4,0,0,7527,1),(7527,'General Sturdiness II',6,5,4294967295,4294967295,6119,6119,1,4294967295,6119,5,0,0,65534,1,81,0,15,4294967295,1,0,15,6119,5,0,5,12406,1),(12406,'General Sturdiness III',6,5,4294967295,4294967295,6119,6119,1,4294967295,7526,5,0,0,65534,1,81,0,16,4294967295,1,0,15,6119,6,0,10,0,0),(7516,'Discordant Defiance II',5,5,4294967295,4294967295,1006,1006,1,4294967295,1006,5,0,0,65534,1,71,0,15,4294967295,1,0,10,1006,5,0,5,0,0),(6521,'Mystical Attuning II',5,2,4294967295,4294967295,1021,1021,1,4294967295,1021,5,0,0,65534,1,75,0,14,4294967295,1,0,12,1021,4,0,5,7539,5),(7539,'Mystical Attuning III',5,5,4294967295,4294967295,1021,1021,1,4294967295,6521,2,0,0,65534,1,80,0,16,4294967295,1,0,12,1021,6,0,7,0,0),(5045,'Mystical Shielding',9,1,4294967295,4294967295,1057,1057,1,4294967295,0,0,0,0,65534,1,85,0,16,4294967295,1,0,1,1057,6,0,0,0,0),(8228,'Mental Fortitude',5,3,4294967295,4294967295,8828,8828,1,4294967295,0,0,0,0,65534,1,75,0,15,4294967295,1,0,3,8228,5,0,0,0,5),(7050,'Mastery of the Past II',5,3,4294967295,4294967295,735,735,5,4294967295,0,0,0,0,33884,0,67,0,8,4294967295,2,0,15,735,1,0,3,7053,0),(7053,'Mastery of the Past III',7,3,4294967295,4294967295,735,735,5,4294967295,7050,3,0,0,33884,0,71,0,12,4294967295,2,0,15,735,4,0,6,7063,2),(7063,'Mastery of the Past IV',8,3,4294967295,4294967295,735,735,5,4294967295,7053,3,0,0,33884,0,76,0,14,4294967295,2,0,15,735,4,0,9,7622,2),(7622,'Mastery of the Past V',8,3,4294967295,4294967295,735,735,5,4294967295,7063,3,0,0,33884,0,81,0,15,4294967295,2,0,15,735,5,0,12,0,2),(895,'Intense Hatred',5,5,4294967295,4294967295,895,895,3,4294967295,0,0,0,0,32,0,65,0,7,4294967295,3,0,5,895,1,0,0,0,0),(1230,'Nature\'s Bounty',1,3,4294967295,4294967295,1230,1230,7,0,0,0,0,0,80,0,51,1,8,4294967295,3,0,6,1230,1,0,0,5000,2),(5000,'Survivalist',2,3,4294967295,4294967295,5000,5000,7,4294967295,1230,3,0,0,80,0,71,0,12,4294967295,3,0,6,1230,1,0,3,0,2),(1604,'Anatomy',5,3,4294967295,4294967295,1604,1604,1,4294967295,0,0,0,0,512,0,60,1,10,4294967295,3,0,3,1604,1,0,0,0,0),(1209,'Sanctuary',12,1,1209,4294967295,1209,1209,3,5912,0,0,14,4320,4,0,70,0,8,4294967295,3,0,1,1209,1,0,0,0,0),(4698,'Quick Draw',5,1,4294967295,4294967295,4698,4698,1,4294967295,0,0,0,0,65534,1,51,0,12,4294967295,1,0,2,4698,4,0,0,6545,0),(6545,'Quick Draw II',5,1,4294967295,4294967295,4698,4698,1,4294967295,4698,1,0,0,65534,1,56,0,14,4294967295,1,0,2,4698,5,0,1,0,0); + +DROP TABLE IF EXISTS `aa_actions`; +CREATE TABLE `aa_actions` ( + `aaid` mediumint(8) unsigned NOT NULL DEFAULT '0', + `rank` tinyint(3) unsigned NOT NULL DEFAULT '0', + `reuse_time` mediumint(8) unsigned NOT NULL DEFAULT '0', + `spell_id` mediumint(8) unsigned NOT NULL DEFAULT '0', + `target` tinyint(3) unsigned NOT NULL DEFAULT '0', + `nonspell_action` tinyint(3) unsigned NOT NULL DEFAULT '0', + `nonspell_mana` mediumint(8) unsigned NOT NULL DEFAULT '0', + `nonspell_duration` mediumint(8) unsigned NOT NULL DEFAULT '0', + `redux_aa` mediumint(8) unsigned NOT NULL DEFAULT '0', + `redux_rate` tinyint(4) NOT NULL DEFAULT '0', + `redux_aa2` mediumint(8) unsigned NOT NULL DEFAULT '0', + `redux_rate2` tinyint(4) NOT NULL DEFAULT '0', + PRIMARY KEY (`aaid`,`rank`) +) ENGINE=MyISAM DEFAULT CHARSET=latin1; +INSERT INTO `aa_actions` VALUES (128,0,4320,5228,1,0,0,100000,782,10,0,0),(129,0,259200,2738,2,0,0,0,0,0,0,0),(130,0,7,2739,1,0,0,0,0,0,0,0),(131,0,4320,2740,2,0,0,0,0,0,0,0),(132,0,8640,2741,2,0,0,0,462,10,0,0),(133,0,4320,2776,1,0,0,0,0,0,0,0),(133,1,4320,2777,1,0,0,0,0,0,0,0),(133,2,4320,2778,1,0,0,0,0,0,0,0),(136,0,4320,2742,2,0,0,0,468,10,0,0),(140,0,4320,2771,1,0,0,0,477,10,0,0),(145,0,4320,0,2,8,0,0,0,0,0,0),(146,0,180,2749,1,0,0,0,0,0,0,0),(153,0,7200,2750,1,0,0,0,474,10,0,0),(154,0,7200,2751,2,0,0,0,0,0,0,0),(155,0,420,2758,1,0,0,0,0,0,0,0),(156,0,4320,2734,1,0,0,0,0,0,0,0),(162,0,8640,2753,1,0,0,0,471,10,0,0),(163,0,2160,2752,5,0,0,0,483,10,0,0),(167,0,900,2754,5,0,0,0,0,0,0,0),(168,0,900,2795,1,0,0,0,0,0,0,0),(168,1,900,2796,1,0,0,0,0,0,0,0),(168,2,900,2797,1,0,0,0,0,0,0,0),(171,0,900,2798,1,0,0,0,0,0,0,0),(171,1,900,2799,1,0,0,0,0,0,0,0),(171,2,900,2800,1,0,0,0,0,0,0,0),(174,0,900,2792,1,0,0,0,0,0,0,0),(174,1,900,2793,1,0,0,0,0,0,0,0),(174,2,900,2794,1,0,0,0,0,0,0,0),(177,0,900,2789,1,0,0,0,0,0,0,0),(177,1,900,2790,1,0,0,0,0,0,0,0),(177,2,900,2791,1,0,0,0,0,0,0,0),(181,0,300,8133,2,0,0,0,486,10,0,0),(181,1,300,8134,2,0,0,0,486,10,0,0),(181,2,300,8135,2,0,0,0,486,10,0,0),(183,0,8640,2755,2,0,0,0,0,0,0,0),(184,0,4320,2756,2,0,0,0,0,0,0,0),(185,0,4320,2757,1,0,0,0,0,0,0,0),(186,0,7,2772,1,0,0,0,0,0,0,0),(187,0,4320,2764,2,0,0,0,0,0,0,0),(188,0,30,2190,2,0,0,0,583,24,0,0),(193,0,4320,0,2,10,0,0,0,0,0,0),(194,0,1,2874,1,0,0,0,0,0,0,0),(197,0,7,2765,1,0,0,0,0,0,0,0),(206,0,1,2875,1,0,0,0,0,0,0,0),(207,0,4320,2774,2,0,0,0,596,17,0,0),(208,0,4320,2766,2,0,0,0,596,17,0,0),(233,0,1800,2742,1,0,0,0,495,10,0,0),(243,0,4320,5244,1,0,0,0,498,10,886,10),(254,0,4320,5232,1,0,0,0,501,10,0,0),(258,0,600,5233,1,0,0,0,492,10,0,0),(259,0,900,0,1,1,0,0,489,10,0,0),(260,0,2160,5229,1,0,0,0,0,0,0,0),(260,1,2160,5230,1,0,0,0,0,0,0,0),(260,2,2160,5231,1,0,0,0,0,0,0,0),(274,0,4320,2748,2,0,0,0,480,10,0,0),(289,0,300,3290,5,0,0,0,0,0,0,0),(290,0,720,3289,1,0,0,0,0,0,0,0),(291,0,900,3291,3,0,0,0,0,0,0,0),(459,0,180,3297,1,0,0,0,754,10,0,0),(459,1,180,3298,1,0,0,0,754,10,0,0),(459,2,180,3299,1,0,0,0,754,10,0,0),(507,0,180,3252,1,0,0,0,0,0,0,0),(507,1,180,3253,1,0,0,0,0,0,0,0),(507,2,180,3254,1,0,0,0,0,0,0,0),(510,0,240,3255,2,0,0,0,0,0,0,0),(510,1,240,3256,2,0,0,0,0,0,0,0),(510,2,240,3257,2,0,0,0,0,0,0,0),(513,0,120,3274,2,0,0,0,0,0,0,0),(513,1,120,3275,2,0,0,0,0,0,0,0),(513,2,120,3276,2,0,0,0,0,0,0,0),(516,0,480,3338,1,0,0,0,0,0,0,0),(517,0,600,3258,1,0,0,0,0,0,0,0),(517,1,600,3259,1,0,0,0,0,0,0,0),(517,2,600,3260,1,0,0,0,0,0,0,0),(520,0,540,3265,2,0,0,0,0,0,0,0),(520,1,540,3266,2,0,0,0,0,0,0,0),(520,2,540,3267,2,0,0,0,0,0,0,0),(523,0,540,3268,2,0,0,0,0,0,0,0),(523,1,540,3269,2,0,0,0,0,0,0,0),(523,2,540,3270,2,0,0,0,0,0,0,0),(526,0,1,5844,1,0,0,0,0,0,0,0),(526,1,1,3248,1,0,0,0,0,0,0,0),(526,2,1,3249,1,0,0,0,0,0,0,0),(528,0,720,3283,2,0,0,0,0,0,0,0),(528,1,720,3284,2,0,0,0,0,0,0,0),(528,2,720,3285,2,0,0,0,0,0,0,0),(531,0,900,3250,2,0,0,0,0,0,0,0),(531,1,900,3251,2,0,0,0,0,0,0,0),(533,0,420,3264,1,0,0,0,0,0,0,0),(534,0,2160,3261,1,0,0,0,849,10,0,0),(534,1,2160,3262,1,0,0,0,849,10,0,0),(534,2,2160,3263,1,0,0,0,849,10,0,0),(545,0,900,3271,1,0,0,0,0,0,0,0),(545,1,900,3272,1,0,0,0,0,0,0,0),(545,2,900,3273,1,0,0,0,0,0,0,0),(548,0,900,3277,1,0,0,0,0,0,0,0),(548,1,900,3278,1,0,0,0,0,0,0,0),(548,2,900,3279,1,0,0,0,0,0,0,0),(592,0,18,3282,2,0,0,0,0,0,0,0),(616,0,900,3286,2,0,0,0,0,0,0,0),(616,1,900,3287,2,0,0,0,0,0,0,0),(616,2,900,3288,2,0,0,0,0,0,0,0),(619,0,900,3292,2,0,0,0,0,0,0,0),(619,1,900,3293,2,0,0,0,0,0,0,0),(619,2,900,3294,2,0,0,0,0,0,0,0),(630,0,1,5243,1,16,900,0,0,0,0,0),(643,0,0,5227,1,0,0,0,0,0,0,0),(645,0,5,3614,2,0,0,0,0,0,0,0),(718,0,120,4521,1,0,0,0,0,0,0,0),(718,1,120,4522,1,0,0,0,0,0,0,0),(718,2,120,4523,1,0,0,0,0,0,0,0),(721,0,900,5110,2,0,0,0,0,0,0,0),(721,1,900,5111,2,0,0,0,0,0,0,0),(921,0,60,4833,1,0,0,0,0,0,0,0),(922,0,60,4834,1,0,0,0,0,0,0,0),(923,0,60,4835,1,0,0,0,0,0,0,0),(926,0,1800,4836,2,0,0,0,0,0,0,0),(926,1,1800,4837,2,0,0,0,0,0,0,0),(926,2,1800,4848,2,0,0,0,0,0,0,0),(926,3,1800,4839,2,0,0,0,0,0,0,0),(926,4,1800,4840,2,0,0,0,0,0,0,0),(931,0,4320,5245,1,0,0,0,0,0,0,0),(931,1,4320,5246,1,0,0,0,0,0,0,0),(931,2,4320,5247,1,0,0,0,0,0,0,0),(1000,0,4320,5824,1,0,0,0,0,0,0,0),(1119,0,900,5841,2,0,0,0,0,0,0,0),(1119,1,900,5842,2,0,0,0,0,0,0,0),(1119,2,900,5843,2,0,0,0,0,0,0,0),(1123,0,900,5854,1,0,0,0,0,0,0,0),(1123,1,900,5855,1,0,0,0,0,0,0,0),(1123,2,900,5856,1,0,0,0,0,0,0,0),(1126,0,2160,5845,5,0,0,0,483,10,0,0),(1126,1,2160,5846,5,0,0,0,483,10,0,0),(1126,2,2160,5847,5,0,0,0,483,10,0,0),(1229,0,4160,6094,1,0,0,0,0,0,0,0),(1334,0,4320,5943,2,0,0,0,0,0,0,0),(1334,1,4320,5944,2,0,0,0,0,0,0,0),(1334,2,4320,5945,2,0,0,0,0,0,0,0),(1337,0,4320,5946,1,0,0,0,0,0,0,0),(1337,1,4320,5947,1,0,0,0,0,0,0,0),(1337,2,4320,5948,1,0,0,0,0,0,0,0),(1340,0,60,5950,1,0,0,0,0,0,0,0),(1341,0,60,5951,1,0,0,0,0,0,0,0),(1342,0,60,5952,1,0,0,0,0,0,0,0),(1343,0,300,5953,3,0,0,0,0,0,0,0),(1344,0,60,5949,1,0,0,0,0,0,0,0),(828,0,3600,5240,1,0,0,0,0,0,0,0),(828,1,3600,5241,1,0,0,0,0,0,0,0),(828,2,3600,5242,1,0,0,0,0,0,0,0),(831,0,1800,4564,2,0,0,0,0,0,0,0),(831,1,1800,4565,2,0,0,0,0,0,0,0),(831,2,1800,4566,2,0,0,0,0,0,0,0),(872,0,180,4802,0,0,0,0,0,0,0,0),(872,1,180,4803,0,0,0,0,0,0,0,0),(872,2,180,4804,0,0,0,0,0,0,0,0),(875,0,180,4805,0,0,0,0,0,0,0,0),(875,1,180,4806,0,0,0,0,0,0,0,0),(875,2,180,4807,0,0,0,0,0,0,0,0),(860,0,180,3297,0,0,0,0,0,0,0,0),(860,1,180,3298,0,0,0,0,0,0,0,0),(860,2,180,3299,0,0,0,0,0,0,0,0),(863,0,4320,5248,0,0,0,0,0,0,0,0),(900,0,720,4826,2,0,0,0,0,0,0,0),(900,1,720,4827,2,0,0,0,0,0,0,0),(1478,2,1,8408,1,0,0,0,0,0,0,0),(1478,1,1,8407,1,0,0,0,0,0,0,0),(1478,0,1,8406,1,0,0,0,0,0,0,0),(746,0,2160,4549,1,0,0,0,0,0,0,0),(746,1,2160,4550,1,0,0,0,0,0,0,0),(746,2,2160,4551,1,0,0,0,0,0,0,0),(749,0,1800,4790,1,0,0,0,0,0,0,0),(749,1,1800,4791,1,0,0,0,0,0,0,0),(749,2,1800,4792,1,0,0,0,0,0,0,0),(749,3,1800,4793,1,0,0,0,0,0,0,0),(749,4,1800,4794,1,0,0,0,0,0,0,0),(757,0,1800,4796,1,0,0,0,0,0,0,0),(757,1,1800,4797,1,0,0,0,0,0,0,0),(757,2,1800,4798,1,0,0,0,0,0,0,0),(757,3,1800,4799,1,0,0,0,0,0,0,0),(757,4,1800,4800,1,0,0,0,0,0,0,0),(1149,0,1320,5853,1,0,0,0,0,0,0,0),(1150,0,2160,5848,1,0,0,0,0,0,0,0),(1150,1,2160,5849,1,0,0,0,0,0,0,0),(1150,2,2160,5850,1,0,0,0,0,0,0,0),(773,0,1800,4552,2,0,0,0,0,0,0,0),(773,1,1800,4553,2,0,0,0,0,0,0,0),(773,2,1800,4554,2,0,0,0,0,0,0,0),(785,0,900,5235,1,0,0,0,0,0,0,0),(785,1,900,5236,1,0,0,0,0,0,0,0),(785,2,900,5237,1,0,0,0,0,0,0,0),(785,3,900,5238,1,0,0,0,0,0,0,0),(785,4,900,5239,1,0,0,0,0,0,0,0),(702,0,18,4842,2,0,0,0,0,0,0,0),(702,1,18,4843,2,0,0,0,0,0,0,0),(702,2,18,4844,2,0,0,0,0,0,0,0),(702,3,18,4845,2,0,0,0,0,0,0,0),(702,4,18,4846,2,0,0,0,0,0,0,0),(209,0,5,5919,0,0,0,0,0,0,0,0),(1373,0,72000,6882,0,0,0,0,0,0,0,0),(1371,0,72000,6880,0,0,0,0,0,0,0,0),(1372,0,72000,6881,0,0,0,0,0,0,0,0),(1374,0,590400,6883,0,0,0,0,0,0,0,0),(1376,0,259200,6885,0,0,0,0,0,0,0,0),(1375,0,72000,6884,0,0,0,0,0,0,0,0),(1377,0,14400,6886,0,0,0,0,0,0,0,0),(7800,0,4320,13531,2,0,0,0,596,17,0,0),(7800,1,4320,13532,2,0,0,0,596,17,0,0),(7800,2,4320,13533,2,0,0,0,596,17,0,0),(7800,3,4320,13534,2,0,0,0,596,17,0,0),(7800,4,4320,13535,2,0,0,0,596,17,0,0),(7800,5,4320,13536,2,0,0,0,596,17,0,0),(7800,6,4320,13537,2,0,0,0,596,17,0,0),(7800,7,4320,13538,2,0,0,0,596,17,0,0),(7800,8,4320,13539,2,0,0,0,596,17,0,0),(7800,9,4320,13540,2,0,0,0,596,17,0,0),(7800,10,4320,13541,2,0,0,0,596,17,0,0),(7800,11,4320,13542,2,0,0,0,596,17,0,0),(7800,12,4320,13543,2,0,0,0,596,17,0,0),(7800,13,4320,13544,2,0,0,0,596,17,0,0),(7800,14,4320,13545,2,0,0,0,596,17,0,0),(7800,15,4320,13562,2,0,0,0,596,17,0,0),(7800,16,4320,13563,2,0,0,0,596,17,0,0),(7850,0,4320,13546,2,0,0,0,593,17,0,0),(7850,1,4320,13547,2,0,0,0,593,17,0,0),(7850,2,4320,13548,2,0,0,0,593,17,0,0),(7850,3,4320,13549,2,0,0,0,593,17,0,0),(7850,4,4320,13550,2,0,0,0,593,17,0,0),(7850,5,4320,13551,2,0,0,0,593,17,0,0),(7850,6,4320,13552,2,0,0,0,593,17,0,0),(7850,7,4320,13553,2,0,0,0,593,17,0,0),(7850,8,4320,13554,2,0,0,0,593,17,0,0),(7850,9,4320,13555,2,0,0,0,593,17,0,0),(7850,10,4320,13556,2,0,0,0,593,17,0,0),(7850,11,4320,13557,2,0,0,0,593,17,0,0),(7850,12,4320,13558,2,0,0,0,593,17,0,0),(7850,13,4320,13559,2,0,0,0,593,17,0,0),(7850,14,4320,13560,2,0,0,0,593,17,0,0),(7850,15,4320,13561,2,0,0,0,593,17,0,0),(7850,16,4320,16028,2,0,0,0,593,17,0,0),(5150,0,300,11112,1,0,0,0,0,0,0,0),(5150,1,300,11113,1,0,0,0,0,0,0,0),(5150,2,300,11114,1,0,0,0,0,0,0,0),(5150,3,300,11115,1,0,0,0,0,0,0,0),(5150,4,300,11116,1,0,0,0,0,0,0,0),(5150,5,300,11117,1,0,0,0,0,0,0,0),(5150,6,300,11118,1,0,0,0,0,0,0,0),(5150,7,300,11119,1,0,0,0,0,0,0,0),(5150,8,300,11120,1,0,0,0,0,0,0,0),(5150,9,300,11121,1,0,0,0,0,0,0,0),(5150,10,300,11122,1,0,0,0,0,0,0,0),(5150,11,300,11123,1,0,0,0,0,0,0,0),(5150,12,300,11124,1,0,0,0,0,0,0,0),(5150,13,300,11125,1,0,0,0,0,0,0,0),(5150,14,300,11126,1,0,0,0,0,0,0,0),(5165,0,300,11127,1,0,0,0,0,0,0,0),(5165,1,300,11128,1,0,0,0,0,0,0,0),(5165,2,300,11129,1,0,0,0,0,0,0,0),(5165,3,300,11130,1,0,0,0,0,0,0,0),(5165,4,300,11131,1,0,0,0,0,0,0,0),(5165,5,300,11132,1,0,0,0,0,0,0,0),(5165,6,300,11133,1,0,0,0,0,0,0,0),(5165,7,300,11134,1,0,0,0,0,0,0,0),(5165,8,300,11135,1,0,0,0,0,0,0,0),(5165,9,300,11136,1,0,0,0,0,0,0,0),(5165,10,300,11137,1,0,0,0,0,0,0,0),(5165,11,300,11138,1,0,0,0,0,0,0,0),(5165,12,300,11139,1,0,0,0,0,0,0,0),(5165,13,300,11140,1,0,0,0,0,0,0,0),(5165,14,300,11141,1,0,0,0,0,0,0,0),(5180,0,300,11142,1,0,0,0,0,0,0,0),(5180,1,300,11143,1,0,0,0,0,0,0,0),(5180,2,300,11144,1,0,0,0,0,0,0,0),(5180,3,300,11145,1,0,0,0,0,0,0,0),(5180,4,300,11146,1,0,0,0,0,0,0,0),(5180,5,300,11147,1,0,0,0,0,0,0,0),(5180,6,300,11148,1,0,0,0,0,0,0,0),(5180,7,300,11149,1,0,0,0,0,0,0,0),(5180,8,300,11150,1,0,0,0,0,0,0,0),(5180,9,300,11151,1,0,0,0,0,0,0,0),(5180,10,300,11152,1,0,0,0,0,0,0,0),(5180,11,300,11153,1,0,0,0,0,0,0,0),(5180,12,300,11154,1,0,0,0,0,0,0,0),(5180,13,300,11155,1,0,0,0,0,0,0,0),(5180,14,300,11156,1,0,0,0,0,0,0,0),(5195,0,300,11157,1,0,0,0,0,0,0,0),(5195,1,300,11158,1,0,0,0,0,0,0,0),(5195,2,300,11159,1,0,0,0,0,0,0,0),(5195,3,300,11160,1,0,0,0,0,0,0,0),(5195,4,300,11161,1,0,0,0,0,0,0,0),(5195,5,300,11162,1,0,0,0,0,0,0,0),(5195,6,300,11163,1,0,0,0,0,0,0,0),(5195,7,300,11164,1,0,0,0,0,0,0,0),(5195,8,300,11165,1,0,0,0,0,0,0,0),(5195,9,300,11166,1,0,0,0,0,0,0,0),(5195,10,300,11167,1,0,0,0,0,0,0,0),(5195,11,300,11168,1,0,0,0,0,0,0,0),(5195,12,300,11169,1,0,0,0,0,0,0,0),(5195,13,300,11170,1,0,0,0,0,0,0,0),(5195,14,300,11171,1,0,0,0,0,0,0,0),(5210,0,300,11172,1,0,0,0,0,0,0,0),(5210,1,300,11173,1,0,0,0,0,0,0,0),(5210,2,300,11174,1,0,0,0,0,0,0,0),(5210,3,300,11175,1,0,0,0,0,0,0,0),(5210,4,300,11176,1,0,0,0,0,0,0,0),(5210,5,300,11177,1,0,0,0,0,0,0,0),(5210,6,300,11178,1,0,0,0,0,0,0,0),(5210,7,300,11179,1,0,0,0,0,0,0,0),(5210,8,300,11180,1,0,0,0,0,0,0,0),(5210,9,300,11181,1,0,0,0,0,0,0,0),(5210,10,300,11182,1,0,0,0,0,0,0,0),(5210,11,300,11183,1,0,0,0,0,0,0,0),(5210,12,300,11184,1,0,0,0,0,0,0,0),(5210,13,300,11185,1,0,0,0,0,0,0,0),(5210,14,300,11186,1,0,0,0,0,0,0,0),(5225,0,300,11187,1,0,0,0,0,0,0,0),(5225,1,300,11188,1,0,0,0,0,0,0,0),(5225,2,300,11189,1,0,0,0,0,0,0,0),(5225,3,300,11190,1,0,0,0,0,0,0,0),(5225,4,300,11191,1,0,0,0,0,0,0,0),(5225,5,300,11192,1,0,0,0,0,0,0,0),(5225,6,300,11193,1,0,0,0,0,0,0,0),(5225,7,300,11194,1,0,0,0,0,0,0,0),(5225,8,300,11195,1,0,0,0,0,0,0,0),(5225,9,300,11196,1,0,0,0,0,0,0,0),(5225,10,300,11197,1,0,0,0,0,0,0,0),(5225,11,300,11198,1,0,0,0,0,0,0,0),(5225,12,300,11199,1,0,0,0,0,0,0,0),(5225,13,300,11200,1,0,0,0,0,0,0,0),(5225,14,300,11201,1,0,0,0,0,0,0,0),(1647,0,30,8771,1,0,0,0,0,0,0,0),(4702,0,600,9475,1,0,0,0,0,0,0,0),(4703,0,600,12752,1,0,0,0,0,0,0,0),(4704,0,600,9477,1,0,0,0,0,0,0,0),(4705,0,600,9478,1,0,0,0,0,0,0,0),(4706,0,600,9479,1,0,0,0,0,0,0,0),(4707,0,600,12748,1,0,0,0,0,0,0,0),(4708,0,600,12751,5,0,0,0,0,0,0,0),(4709,0,600,12750,1,0,0,0,0,0,0,0),(4710,0,600,12749,1,0,0,0,0,0,0,0),(723,0,60,4788,2,0,0,0,0,0,0,0),(4665,0,4320,9177,0,0,0,0,0,0,0,0),(1597,0,10,8271,2,0,0,0,0,0,0,0),(912,0,3600,4925,1,0,0,0,1583,10,0,0),(912,1,3600,4926,1,0,0,0,1583,10,0,0),(912,2,3600,4927,1,0,0,0,1583,10,0,0),(1330,1,3600,5936,1,0,0,0,1583,10,0,0),(1330,2,3600,5937,1,0,0,0,1583,10,0,0),(1330,3,3600,5938,1,0,0,0,1583,10,0,0),(1327,0,900,5933,2,0,0,0,0,0,0,0),(1327,1,900,5934,2,0,0,0,0,0,0,0),(1327,2,900,5935,2,0,0,0,0,0,0,0),(1323,0,2160,5932,1,0,0,0,0,0,0,0),(10367,1,360,21751,2,0,0,0,0,0,0,0),(1209,0,4320,5912,1,0,0,0,0,0,0,0); diff --git a/web_interface/web_interface.h b/web_interface/web_interface.h index c81d86d8d..19cba376d 100644 --- a/web_interface/web_interface.h +++ b/web_interface/web_interface.h @@ -20,14 +20,14 @@ #include "../common/debug.h" #include "../common/opcodemgr.h" -#include "../common/EQStreamFactory.h" +#include "../common/eq_stream_factory.h" #include "../common/rulesys.h" #include "../common/servertalk.h" #include "../common/platform.h" #include "../common/crash.h" -#include "../common/EQEmuConfig.h" +#include "../common/eqemu_config.h" #include "../common/web_interface_utils.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "../common/uuid.h" #include "../common/shareddb.h" #include "worldserver.h" diff --git a/world/CMakeLists.txt b/world/CMakeLists.txt index a40f79734..92220edbb 100644 --- a/world/CMakeLists.txt +++ b/world/CMakeLists.txt @@ -1,68 +1,68 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) SET(world_sources - Adventure.cpp - AdventureManager.cpp + adventure.cpp + adventure_manager.cpp client.cpp cliententry.cpp clientlist.cpp CMakeLists.txt console.cpp - EQLConfig.cpp - EQW.cpp - EQWHTTPHandler.cpp - EQWParser.cpp - HTTPRequest.cpp - LauncherLink.cpp - LauncherList.cpp + eql_config.cpp + eqw.cpp + eqw_http_handler.cpp + eqw_parser.cpp + http_request.cpp + launcher_link.cpp + launcher_list.cpp lfplist.cpp - LoginServer.cpp - LoginServerList.cpp + login_server.cpp + login_server_list.cpp net.cpp - perl_EQLConfig.cpp - perl_EQW.cpp - perl_HTTPRequest.cpp + perl_eql_config.cpp + perl_eqw.cpp + perl_http_request.cpp queryserv.cpp remote_call.cpp ucs.cpp web_interface.cpp wguild_mgr.cpp world_logsys.cpp - WorldConfig.cpp + world_config.cpp worlddb.cpp zonelist.cpp zoneserver.cpp ) SET(world_headers - Adventure.h - AdventureManager.h - AdventureTemplate.h + adventure.h + adventure_manager.h + adventure_template.h client.h cliententry.h clientlist.h CMakeLists.txt console.h - EQLConfig.h - EQW.h - EQWHTTPHandler.h - EQWParser.h - HTTPRequest.h - LauncherLink.h - LauncherList.h + eql_config.h + eqw.h + eqw_http_handler.h + eqw_parser.h + http_request.h + launcher_link.h + launcher_list.h lfplist.h - LoginServer.h - LoginServerList.h + login_server.h + login_server_list.h net.h queryserv.h remote_call.h - SoFCharCreateData.h + sof_char_create_data.h ucs.h web_interface.h wguild_mgr.h - WorldConfig.h + world_config.h worlddb.h - WorldTCPConnection.h + world_tcp_connection.h zonelist.h zoneserver.h ) diff --git a/world/Adventure.cpp b/world/adventure.cpp similarity index 85% rename from world/Adventure.cpp rename to world/adventure.cpp index 83fd8fa2f..80b1dcaca 100644 --- a/world/Adventure.cpp +++ b/world/adventure.cpp @@ -2,10 +2,10 @@ #include "../common/servertalk.h" #include "../common/extprofile.h" #include "../common/rulesys.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" -#include "Adventure.h" -#include "AdventureManager.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" +#include "adventure.h" +#include "adventure_manager.h" #include "worlddb.h" #include "zonelist.h" #include "clientlist.h" @@ -54,10 +54,10 @@ void Adventure::AddPlayer(std::string character_name, bool add_client_to_instanc { if(!PlayerExists(character_name)) { - int client_id = database.GetCharacterID(character_name.c_str()); - if(add_client_to_instance) + int32 character_id = database.GetCharacterID(character_name.c_str()); + if(character_id && add_client_to_instance) { - database.AddClientToInstance(instance_id, client_id); + database.AddClientToInstance(instance_id, character_id); } players.push_back(character_name); } @@ -70,8 +70,12 @@ void Adventure::RemovePlayer(std::string character_name) { if((*iter).compare(character_name) == 0) { - database.RemoveClientFromInstance(instance_id, database.GetCharacterID(character_name.c_str())); - players.erase(iter); + int32 character_id = database.GetCharacterID(character_name.c_str()); + if (character_id) + { + database.RemoveClientFromInstance(instance_id, character_id); + players.erase(iter); + } return; } ++iter; @@ -358,6 +362,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; @@ -374,49 +379,35 @@ void Adventure::MoveCorpsesToGraveyard() std::list dbid_list; std::list charid_list; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT id, charid FROM player_corpses WHERE instanceid=%d", GetInstanceID()), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - dbid_list.push_back(atoi(row[0])); - charid_list.push_back(atoi(row[1])); - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::MoveCorpsesToGraveyard: %s (%s)", query, errbuf); - safe_delete_array(query); - } + std::string query = StringFormat("SELECT id, charid FROM character_corpses WHERE instanceid=%d", GetInstanceID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::MoveCorpsesToGraveyard: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); - std::list::iterator iter = dbid_list.begin(); - while(iter != dbid_list.end()) + for(auto row = results.begin(); row != results.end(); ++row) { + dbid_list.push_back(atoi(row[0])); + charid_list.push_back(atoi(row[1])); + } + + for (auto iter = dbid_list.begin(); iter != dbid_list.end(); ++iter) { float x = GetTemplate()->graveyard_x + MakeRandomFloat(-GetTemplate()->graveyard_radius, GetTemplate()->graveyard_radius); float y = GetTemplate()->graveyard_y + MakeRandomFloat(-GetTemplate()->graveyard_radius, GetTemplate()->graveyard_radius); float z = GetTemplate()->graveyard_z; - if(database.RunQuery(query,MakeAnyLenString(&query, "UPDATE player_corpses SET zoneid=%d, instanceid=0, x=%f, y=%f, z=%f WHERE instanceid=%d", - GetTemplate()->graveyard_zone_id, x, y, z, GetInstanceID()), errbuf)) - { - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::MoveCorpsesToGraveyard: %s (%s)", query, errbuf); - safe_delete_array(query); - } - ++iter; + + query = StringFormat("UPDATE character_corpses " + "SET zoneid = %d, instanceid = 0, " + "x = %f, y = %f, z = %f WHERE instanceid = %d", + GetTemplate()->graveyard_zone_id, + x, y, z, GetInstanceID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::MoveCorpsesToGraveyard: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); } - iter = dbid_list.begin(); - std::list::iterator c_iter = charid_list.begin(); - while(iter != dbid_list.end()) + auto c_iter = charid_list.begin(); + for (auto iter = dbid_list.begin(); iter != dbid_list.end(); ++iter, ++c_iter) { ServerPacket* pack = new ServerPacket(ServerOP_DepopAllPlayersCorpses, sizeof(ServerDepopAllPlayersCorpses_Struct)); ServerDepopAllPlayersCorpses_Struct *dpc = (ServerDepopAllPlayersCorpses_Struct*)pack->pBuffer; @@ -433,8 +424,6 @@ void Adventure::MoveCorpsesToGraveyard() zoneserver_list.SendPacket(spc->zone_id, 0, pack); delete pack; - ++iter; - ++c_iter; } } diff --git a/world/Adventure.h b/world/adventure.h similarity index 98% rename from world/Adventure.h rename to world/adventure.h index cb14609ae..dec691eda 100644 --- a/world/Adventure.h +++ b/world/adventure.h @@ -4,7 +4,7 @@ #include "../common/debug.h" #include "../common/types.h" #include "../common/timer.h" -#include "AdventureTemplate.h" +#include "adventure_template.h" #include #include #include diff --git a/world/AdventureManager.cpp b/world/adventure_manager.cpp similarity index 90% rename from world/AdventureManager.cpp rename to world/adventure_manager.cpp index 4ce2fbc53..aeee8e3c5 100644 --- a/world/AdventureManager.cpp +++ b/world/adventure_manager.cpp @@ -1,10 +1,10 @@ #include "../common/debug.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "../common/servertalk.h" #include "../common/rulesys.h" -#include "Adventure.h" -#include "AdventureManager.h" +#include "adventure.h" +#include "adventure_manager.h" #include "worlddb.h" #include "zonelist.h" #include "clientlist.h" @@ -639,119 +639,90 @@ AdventureTemplate *AdventureManager::GetAdventureTemplate(int id) bool AdventureManager::LoadAdventureTemplates() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT id, zone, zone_version, " + std::string query = "SELECT id, zone, zone_version, " "is_hard, min_level, max_level, type, type_data, type_count, assa_x, " "assa_y, assa_z, assa_h, text, duration, zone_in_time, win_points, lose_points, " - "theme, zone_in_zone_id, zone_in_x, zone_in_y, zone_in_object_id, dest_x, dest_y," - " dest_z, dest_h, graveyard_zone_id, graveyard_x, graveyard_y, graveyard_z, " - "graveyard_radius FROM adventure_template"), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - uint8 x = 0; - AdventureTemplate *t = new AdventureTemplate; - t->id = atoi(row[x++]); - strcpy(t->zone, row[x++]); - t->zone_version = atoi(row[x++]); - t->is_hard = atoi(row[x++]); - t->min_level = atoi(row[x++]); - t->max_level = atoi(row[x++]); - t->type = atoi(row[x++]); - t->type_data = atoi(row[x++]); - t->type_count = atoi(row[x++]); - t->assa_x = atof(row[x++]); - t->assa_y = atof(row[x++]); - t->assa_z = atof(row[x++]); - t->assa_h = atof(row[x++]); - strn0cpy(t->text, row[x++], sizeof(t->text)); - t->duration = atoi(row[x++]); - t->zone_in_time = atoi(row[x++]); - t->win_points = atoi(row[x++]); - t->lose_points = atoi(row[x++]); - t->theme = atoi(row[x++]); - t->zone_in_zone_id = atoi(row[x++]); - t->zone_in_x = atof(row[x++]); - t->zone_in_y = atof(row[x++]); - t->zone_in_object_id = atoi(row[x++]); - t->dest_x = atof(row[x++]); - t->dest_y = atof(row[x++]); - t->dest_z = atof(row[x++]); - t->dest_h = atof(row[x++]); - t->graveyard_zone_id = atoi(row[x++]); - t->graveyard_x = atof(row[x++]); - t->graveyard_y = atof(row[x++]); - t->graveyard_z = atof(row[x++]); - t->graveyard_radius = atof(row[x++]); - adventure_templates[t->id] = t; - } - mysql_free_result(result); - safe_delete_array(query); - return true; - } - else - { - LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::LoadAdventures: %s (%s)", query, errbuf); - safe_delete_array(query); + "theme, zone_in_zone_id, zone_in_x, zone_in_y, zone_in_object_id, dest_x, dest_y, " + "dest_z, dest_h, graveyard_zone_id, graveyard_x, graveyard_y, graveyard_z, " + "graveyard_radius FROM adventure_template"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::LoadAdventures: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return false; - } - return false; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + AdventureTemplate *aTemplate = new AdventureTemplate; + aTemplate->id = atoi(row[0]); + strcpy(aTemplate->zone, row[1]); + aTemplate->zone_version = atoi(row[2]); + aTemplate->is_hard = atoi(row[3]); + aTemplate->min_level = atoi(row[4]); + aTemplate->max_level = atoi(row[5]); + aTemplate->type = atoi(row[6]); + aTemplate->type_data = atoi(row[7]); + aTemplate->type_count = atoi(row[8]); + aTemplate->assa_x = atof(row[9]); + aTemplate->assa_y = atof(row[10]); + aTemplate->assa_z = atof(row[11]); + aTemplate->assa_h = atof(row[12]); + strn0cpy(aTemplate->text, row[13], sizeof(aTemplate->text)); + aTemplate->duration = atoi(row[14]); + aTemplate->zone_in_time = atoi(row[15]); + aTemplate->win_points = atoi(row[16]); + aTemplate->lose_points = atoi(row[17]); + aTemplate->theme = atoi(row[18]); + aTemplate->zone_in_zone_id = atoi(row[19]); + aTemplate->zone_in_x = atof(row[20]); + aTemplate->zone_in_y = atof(row[21]); + aTemplate->zone_in_object_id = atoi(row[22]); + aTemplate->dest_x = atof(row[23]); + aTemplate->dest_y = atof(row[24]); + aTemplate->dest_z = atof(row[25]); + aTemplate->dest_h = atof(row[26]); + aTemplate->graveyard_zone_id = atoi(row[27]); + aTemplate->graveyard_x = atof(row[28]); + aTemplate->graveyard_y = atof(row[29]); + aTemplate->graveyard_z = atof(row[30]); + aTemplate->graveyard_radius = atof(row[31]); + adventure_templates[aTemplate->id] = aTemplate; + } + + return true; } bool AdventureManager::LoadAdventureEntries() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT id, template_id FROM adventure_template_entry"), errbuf, &result)) + std::string query = "SELECT id, template_id FROM adventure_template_entry"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { - while((row = mysql_fetch_row(result))) - { - int id = atoi(row[0]); - int template_id = atoi(row[1]); - AdventureTemplate* tid = nullptr; - - std::map::iterator t_iter = adventure_templates.find(template_id); - if(t_iter == adventure_templates.end()) - { - continue; - } - else - { - tid = adventure_templates[template_id]; - } - - std::list temp; - std::map >::iterator iter = adventure_entries.find(id); - if(iter == adventure_entries.end()) - { - temp.push_back(tid); - adventure_entries[id] = temp; - } - else - { - temp = adventure_entries[id]; - temp.push_back(tid); - adventure_entries[id] = temp; - } - } - mysql_free_result(result); - safe_delete_array(query); - return true; - } - else - { - LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::LoadAdventureEntries: %s (%s)", query, errbuf); - safe_delete_array(query); + LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::LoadAdventureEntries: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return false; } - return false; + + for (auto row = results.begin(); row != results.end(); ++row) + { + int id = atoi(row[0]); + int template_id = atoi(row[1]); + AdventureTemplate* tid = nullptr; + + auto t_iter = adventure_templates.find(template_id); + if(t_iter == adventure_templates.end()) + continue; + + tid = adventure_templates[template_id]; + + std::list temp; + auto iter = adventure_entries.find(id); + if(iter != adventure_entries.end()) + temp = adventure_entries[id]; + + temp.push_back(tid); + adventure_entries[id] = temp; + } + + return true; } void AdventureManager::PlayerClickedDoor(const char *player, int zone_id, int door_id) @@ -1096,79 +1067,69 @@ void AdventureManager::LoadLeaderboardInfo() leaderboard_info_percentage_ruj.clear(); leaderboard_info_wins_tak.clear(); leaderboard_info_percentage_tak.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(database.RunQuery(query,MakeAnyLenString(&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;"), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - if(row[0]) - { - LeaderboardInfo lbi; - lbi.name = row[0]; - lbi.wins = atoi(row[3]); - lbi.guk_wins = atoi(row[3]); - lbi.wins += atoi(row[4]); - lbi.mir_wins = atoi(row[4]); - lbi.wins += atoi(row[5]); - lbi.mmc_wins = atoi(row[5]); - lbi.wins += atoi(row[6]); - lbi.ruj_wins = atoi(row[6]); - lbi.wins += atoi(row[7]); - lbi.tak_wins = atoi(row[7]); - lbi.losses = atoi(row[8]); - lbi.guk_losses = atoi(row[8]); - lbi.losses += atoi(row[9]); - lbi.mir_losses = atoi(row[9]); - lbi.losses += atoi(row[10]); - lbi.mmc_losses = atoi(row[10]); - lbi.losses += atoi(row[11]); - lbi.ruj_losses = atoi(row[11]); - lbi.losses += atoi(row[12]); - lbi.tak_losses = atoi(row[12]); - - leaderboard_info_wins.push_back(lbi); - leaderboard_info_percentage.push_back(lbi); - leaderboard_info_wins_guk.push_back(lbi); - leaderboard_info_percentage_guk.push_back(lbi); - leaderboard_info_wins_mir.push_back(lbi); - leaderboard_info_percentage_mir.push_back(lbi); - leaderboard_info_wins_mmc.push_back(lbi); - leaderboard_info_percentage_mmc.push_back(lbi); - leaderboard_info_wins_ruj.push_back(lbi); - leaderboard_info_percentage_ruj.push_back(lbi); - leaderboard_info_wins_tak.push_back(lbi); - leaderboard_info_percentage_tak.push_back(lbi); - - leaderboard_sorted_wins = false; - leaderboard_sorted_percentage = false; - leaderboard_sorted_wins_guk = false; - leaderboard_sorted_percentage_guk = false; - leaderboard_sorted_wins_mir = false; - leaderboard_sorted_percentage_mir = false; - leaderboard_sorted_wins_mmc = false; - leaderboard_sorted_percentage_mmc = false; - leaderboard_sorted_wins_ruj = false; - leaderboard_sorted_percentage_ruj = false; - leaderboard_sorted_wins_tak = false; - leaderboard_sorted_percentage_tak = false; - } - } - mysql_free_result(result); - safe_delete_array(query); + std::string query = "SELECT ch.name, ch.id, adv_stats.* FROM adventure_stats " + "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()); return; } - else - { - LogFile->write(EQEMuLog::Error, "Error in AdventureManager:::GetLeaderboardInfo: %s (%s)", query, errbuf); - safe_delete_array(query); - return; + + for (auto row = results.begin(); row != results.end(); ++row) + { + if(!row[0]) + continue; + + LeaderboardInfo lbi; + lbi.name = row[0]; + lbi.wins = atoi(row[3]); + lbi.guk_wins = atoi(row[3]); + lbi.wins += atoi(row[4]); + lbi.mir_wins = atoi(row[4]); + lbi.wins += atoi(row[5]); + lbi.mmc_wins = atoi(row[5]); + lbi.wins += atoi(row[6]); + lbi.ruj_wins = atoi(row[6]); + lbi.wins += atoi(row[7]); + lbi.tak_wins = atoi(row[7]); + lbi.losses = atoi(row[8]); + lbi.guk_losses = atoi(row[8]); + lbi.losses += atoi(row[9]); + lbi.mir_losses = atoi(row[9]); + lbi.losses += atoi(row[10]); + lbi.mmc_losses = atoi(row[10]); + lbi.losses += atoi(row[11]); + lbi.ruj_losses = atoi(row[11]); + lbi.losses += atoi(row[12]); + lbi.tak_losses = atoi(row[12]); + + leaderboard_info_wins.push_back(lbi); + leaderboard_info_percentage.push_back(lbi); + leaderboard_info_wins_guk.push_back(lbi); + leaderboard_info_percentage_guk.push_back(lbi); + leaderboard_info_wins_mir.push_back(lbi); + leaderboard_info_percentage_mir.push_back(lbi); + leaderboard_info_wins_mmc.push_back(lbi); + leaderboard_info_percentage_mmc.push_back(lbi); + leaderboard_info_wins_ruj.push_back(lbi); + leaderboard_info_percentage_ruj.push_back(lbi); + leaderboard_info_wins_tak.push_back(lbi); + leaderboard_info_percentage_tak.push_back(lbi); + + leaderboard_sorted_wins = false; + leaderboard_sorted_percentage = false; + leaderboard_sorted_wins_guk = false; + leaderboard_sorted_percentage_guk = false; + leaderboard_sorted_wins_mir = false; + leaderboard_sorted_percentage_mir = false; + leaderboard_sorted_wins_mmc = false; + leaderboard_sorted_percentage_mmc = false; + leaderboard_sorted_wins_ruj = false; + leaderboard_sorted_percentage_ruj = false; + leaderboard_sorted_wins_tak = false; + leaderboard_sorted_percentage_tak = false; } - return; }; void AdventureManager::DoLeaderboardRequest(const char* player, uint8 type) diff --git a/world/AdventureManager.h b/world/adventure_manager.h similarity index 98% rename from world/AdventureManager.h rename to world/adventure_manager.h index e7055ac8d..ce6d7331a 100644 --- a/world/AdventureManager.h +++ b/world/adventure_manager.h @@ -4,8 +4,8 @@ #include "../common/debug.h" #include "../common/types.h" #include "../common/timer.h" -#include "Adventure.h" -#include "AdventureTemplate.h" +#include "adventure.h" +#include "adventure_template.h" #include #include diff --git a/world/AdventureTemplate.h b/world/adventure_template.h similarity index 100% rename from world/AdventureTemplate.h rename to world/adventure_template.h diff --git a/world/client.cpp b/world/client.cpp index fd45567c6..f3f1a78c8 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -1,31 +1,31 @@ #include "../common/debug.h" -#include "../common/EQPacket.h" -#include "../common/EQStreamIntf.h" +#include "../common/eq_packet.h" +#include "../common/eq_stream_intf.h" #include "../common/misc.h" #include "../common/rulesys.h" #include "../common/emu_opcodes.h" #include "../common/eq_packet_structs.h" #include "../common/packet_dump.h" -#include "../common/EQStreamIntf.h" -#include "../common/Item.h" +#include "../common/eq_stream_intf.h" +#include "../common/item.h" #include "../common/races.h" #include "../common/classes.h" #include "../common/languages.h" #include "../common/skills.h" #include "../common/extprofile.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "../common/clientversions.h" #include "client.h" #include "worlddb.h" -#include "WorldConfig.h" -#include "LoginServer.h" -#include "LoginServerList.h" +#include "world_config.h" +#include "login_server.h" +#include "login_server_list.h" #include "zoneserver.h" #include "zonelist.h" #include "clientlist.h" #include "wguild_mgr.h" -#include "SoFCharCreateData.h" +#include "sof_char_create_data.h" #include #include @@ -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,149 +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; } @@ -1584,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; } @@ -1605,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; } @@ -1648,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; } @@ -1727,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..."); @@ -1741,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 @@ -1781,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++; } @@ -1831,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); } @@ -2034,3 +1964,15 @@ void Client::SetRacialLanguages( PlayerProfile_Struct *pp ) } } +void Client::SetClassLanguages(PlayerProfile_Struct *pp) +{ + // we only need to handle one class, but custom server might want to do more + switch(pp->class_) { + case ROGUE: + pp->languages[LANG_THIEVES_CANT] = 100; + break; + default: + break; + } +} + diff --git a/world/client.h b/world/client.h index 8d17864ec..9014051ed 100644 --- a/world/client.h +++ b/world/client.h @@ -20,7 +20,7 @@ #include -//#include "../common/EQStream.h" +//#include "../common/eq_stream.h" #include "../common/linked_list.h" #include "../common/timer.h" //#include "zoneserver.h" @@ -90,6 +90,7 @@ private: void SetClassStartingSkills( PlayerProfile_Struct *pp ); void SetRaceStartingSkills( PlayerProfile_Struct *pp ); void SetRacialLanguages( PlayerProfile_Struct *pp ); + void SetClassLanguages(PlayerProfile_Struct *pp); ClientListEntry* cle; Timer CLE_keepalive_timer; diff --git a/world/cliententry.cpp b/world/cliententry.cpp index a125b706f..fa5b60c43 100644 --- a/world/cliententry.cpp +++ b/world/cliententry.cpp @@ -18,13 +18,13 @@ #include "../common/debug.h" #include "cliententry.h" #include "clientlist.h" -#include "LoginServer.h" -#include "LoginServerList.h" +#include "login_server.h" +#include "login_server_list.h" #include "worlddb.h" #include "zoneserver.h" -#include "WorldConfig.h" +#include "world_config.h" #include "../common/guilds.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" extern uint32 numplayers; extern LoginServerList loginserverlist; @@ -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/clientlist.cpp b/world/clientlist.cpp index acb582d22..6de17d176 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -22,7 +22,7 @@ #include "client.h" #include "console.h" #include "worlddb.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "../common/guilds.h" #include "../common/races.h" #include "../common/classes.h" diff --git a/world/console.cpp b/world/console.cpp index afbdf6dba..5cec258fd 100644 --- a/world/console.cpp +++ b/world/console.cpp @@ -30,20 +30,20 @@ #include "../common/packet_dump.h" #include "../common/seperator.h" #include "../common/eq_packet_structs.h" -#include "../common/EQPacket.h" -#include "LoginServer.h" -#include "LoginServerList.h" +#include "../common/eq_packet.h" +#include "login_server.h" +#include "login_server_list.h" #include "../common/serverinfo.h" #include "../common/md5.h" #include "../common/opcodemgr.h" #include "../common/rulesys.h" #include "../common/ruletypes.h" -#include "../common/StringUtil.h" -#include "WorldConfig.h" +#include "../common/string_util.h" +#include "world_config.h" #include "zoneserver.h" #include "zonelist.h" #include "clientlist.h" -#include "LauncherList.h" +#include "launcher_list.h" #include "ucs.h" #include "queryserv.h" #include "web_interface.h" @@ -115,7 +115,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; @@ -855,6 +855,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/console.h b/world/console.h index 26aa95648..e0a72f590 100644 --- a/world/console.h +++ b/world/console.h @@ -38,9 +38,9 @@ enum { #include "../common/linked_list.h" #include "../common/timer.h" #include "../common/queue.h" -#include "../common/EmuTCPConnection.h" -#include "WorldTCPConnection.h" -#include "../common/Mutex.h" +#include "../common/emu_tcp_connection.h" +#include "world_tcp_connection.h" +#include "../common/mutex.h" struct ServerChannelMessage_Struct; diff --git a/world/EQLConfig.cpp b/world/eql_config.cpp similarity index 67% rename from world/EQLConfig.cpp rename to world/eql_config.cpp index 90955a620..f99ba0fb6 100644 --- a/world/EQLConfig.cpp +++ b/world/eql_config.cpp @@ -16,11 +16,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "EQLConfig.h" +#include "eql_config.h" #include "worlddb.h" -#include "LauncherLink.h" -#include "LauncherList.h" -#include "../common/StringUtil.h" +#include "launcher_link.h" +#include "launcher_list.h" +#include "../common/string_util.h" #include #include @@ -33,66 +33,51 @@ EQLConfig::EQLConfig(const char *launcher_name) } void EQLConfig::LoadSettings() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - LauncherZone tmp; + LauncherZone tmp; char namebuf[128]; database.DoEscapeString(namebuf, m_name.c_str(), m_name.length()&0x3F); //limit len to 64 namebuf[127] = '\0'; - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT dynamics FROM launcher WHERE name='%s'", - namebuf) - , errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) { - m_dynamics = atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "EQLConfig::LoadSettings: %s", errbuf); - } - safe_delete_array(query); + std::string query = StringFormat("SELECT dynamics FROM launcher WHERE name = '%s'", namebuf); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "EQLConfig::LoadSettings: %s", results.ErrorMessage().c_str()); + else { + auto row = results.begin(); + m_dynamics = atoi(row[0]); + } + + query = StringFormat("SELECT zone, port FROM launcher_zones WHERE launcher = '%s'", namebuf); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "EQLConfig::LoadSettings: %s", results.ErrorMessage().c_str()); + return; + } + + LauncherZone zs; + for (auto row = results.begin(); row != results.end(); ++row) { + zs.name = row[0]; + zs.port = atoi(row[1]); + m_zones[zs.name] = zs; + } - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT zone,port FROM launcher_zones WHERE launcher='%s'", - namebuf) - , errbuf, &result)) - { - LauncherZone zs; - while ((row = mysql_fetch_row(result))) { - zs.name = row[0]; - zs.port = atoi(row[1]); - m_zones[zs.name] = zs; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "EQLConfig::LoadSettings: %s", errbuf); - } - safe_delete_array(query); } EQLConfig *EQLConfig::CreateLauncher(const char *name, uint8 dynamic_count) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; char namebuf[128]; database.DoEscapeString(namebuf, name, strlen(name)&0x3F); //limit len to 64 namebuf[127] = '\0'; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO launcher (name,dynamics) VALUES('%s', %d)", - namebuf, dynamic_count), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in CreateLauncher query: %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO launcher (name, dynamics) VALUES('%s', %d)", namebuf, dynamic_count); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in CreateLauncher query: %s", results.ErrorMessage().c_str()); return nullptr; } - safe_delete_array(query); - return(new EQLConfig(name)); + return new EQLConfig(name); } void EQLConfig::GetZones(std::vector &result) { @@ -126,30 +111,23 @@ void EQLConfig::DeleteLauncher() { launcher_list.Remove(m_name.c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char namebuf[128]; database.DoEscapeString(namebuf, m_name.c_str(), m_name.length()&0x3F); //limit len to 64 namebuf[127] = '\0'; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM launcher WHERE name='%s'", - namebuf), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in DeleteLauncher 1 query: %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM launcher WHERE name = '%s'", namebuf); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in DeleteLauncher 1st query: %s", results.ErrorMessage().c_str()); return; } - safe_delete_array(query); - if (!database.RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM launcher_zones WHERE launcher='%s'", - namebuf), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in DeleteLauncher 2 query: %s", errbuf); - safe_delete_array(query); + query = StringFormat("DELETE FROM launcher_zones WHERE launcher = '%s'", namebuf); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in DeleteLauncher 2nd query: %s", results.ErrorMessage().c_str()); return; } - safe_delete_array(query); } bool EQLConfig::IsConnected() const { @@ -181,12 +159,9 @@ void EQLConfig::StartZone(Const_char *zone_ref) { bool EQLConfig::BootStaticZone(Const_char *short_name, uint16 port) { //make sure the short name is valid. if(database.GetZoneID(short_name) == 0) - return(false); + return false; //database update - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char namebuf[128]; database.DoEscapeString(namebuf, m_name.c_str(), m_name.length()&0x3F); //limit len to 64 namebuf[127] = '\0'; @@ -194,14 +169,13 @@ bool EQLConfig::BootStaticZone(Const_char *short_name, uint16 port) { database.DoEscapeString(zonebuf, short_name, strlen(short_name)&0xF); //limit len to 16 zonebuf[31] = '\0'; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO launcher_zones (launcher,zone,port) VALUES('%s', '%s', %d)", - namebuf, zonebuf, port), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in BootStaticZone query: %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO launcher_zones (launcher, zone, port) " + "VALUES('%s', '%s', %d)", namebuf, zonebuf, port); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in BootStaticZone query: %s", results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); //update our internal state. LauncherZone lz; @@ -215,13 +189,13 @@ bool EQLConfig::BootStaticZone(Const_char *short_name, uint16 port) { ll->BootZone(short_name, port); } - return(true); + return true; } bool EQLConfig::ChangeStaticZone(Const_char *short_name, uint16 port) { //make sure the short name is valid. if(database.GetZoneID(short_name) == 0) - return(false); + return false; //check internal state std::map::iterator res; @@ -229,14 +203,9 @@ bool EQLConfig::ChangeStaticZone(Const_char *short_name, uint16 port) { if(res == m_zones.end()) { //not found. LogFile->write(EQEMuLog::Error, "Update for unknown zone %s", short_name); - return(false); + return false; } - - //database update - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char namebuf[128]; database.DoEscapeString(namebuf, m_name.c_str(), m_name.length()&0x3F); //limit len to 64 namebuf[127] = '\0'; @@ -244,15 +213,13 @@ bool EQLConfig::ChangeStaticZone(Const_char *short_name, uint16 port) { database.DoEscapeString(zonebuf, short_name, strlen(short_name)&0xF); //limit len to 16 zonebuf[31] = '\0'; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "UPDATE launcher_zones SET port=%d WHERE launcher='%s' AND zone='%s'", - port, namebuf, zonebuf), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in ChangeStaticZone query: %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("UPDATE launcher_zones SET port=%d WHERE " + "launcher = '%s' AND zone = '%s'",port, namebuf, zonebuf); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ChangeStaticZone query: %s", results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - //update internal state res->second.port = port; @@ -263,7 +230,7 @@ bool EQLConfig::ChangeStaticZone(Const_char *short_name, uint16 port) { ll->RestartZone(short_name); } - return(true); + return true; } bool EQLConfig::DeleteStaticZone(Const_char *short_name) { @@ -273,13 +240,9 @@ bool EQLConfig::DeleteStaticZone(Const_char *short_name) { if(res == m_zones.end()) { //not found. LogFile->write(EQEMuLog::Error, "Update for unknown zone %s", short_name); - return(false); + return false; } - //database update - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - char namebuf[128]; database.DoEscapeString(namebuf, m_name.c_str(), m_name.length()&0x3F); //limit len to 64 namebuf[127] = '\0'; @@ -287,14 +250,13 @@ bool EQLConfig::DeleteStaticZone(Const_char *short_name) { database.DoEscapeString(zonebuf, short_name, strlen(short_name)&0xF); //limit len to 16 zonebuf[31] = '\0'; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM launcher_zones WHERE launcher='%s' AND zone='%s'", - namebuf, zonebuf), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in DeleteStaticZone query: %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM launcher_zones WHERE " + "launcher = '%s' AND zone = '%s'", namebuf, zonebuf); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in DeleteStaticZone query: %s", results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); //internal update. m_zones.erase(res); @@ -309,21 +271,17 @@ bool EQLConfig::DeleteStaticZone(Const_char *short_name) { } bool EQLConfig::SetDynamicCount(int count) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; char namebuf[128]; database.DoEscapeString(namebuf, m_name.c_str(), m_name.length()&0x3F); //limit len to 64 namebuf[127] = '\0'; - if (!database.RunQuery(query, MakeAnyLenString(&query, - "UPDATE launcher SET dynamics=%d WHERE name='%s'", - count, namebuf), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in SetDynamicCount query: %s", errbuf); - safe_delete_array(query); + std::string query = StringFormat("UPDATE launcher SET dynamics=%d WHERE name='%s'", count, namebuf); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in SetDynamicCount query: %s", results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); //update in-memory version. m_dynamics = count; @@ -334,7 +292,7 @@ bool EQLConfig::SetDynamicCount(int count) { ll->BootDynamics(count); } - return(false); + return false; } int EQLConfig::GetDynamicCount() const { diff --git a/world/EQLConfig.h b/world/eql_config.h similarity index 100% rename from world/EQLConfig.h rename to world/eql_config.h diff --git a/world/EQW.cpp b/world/eqw.cpp similarity index 85% rename from world/EQW.cpp rename to world/eqw.cpp index 02196e49c..343aa9ebd 100644 --- a/world/EQW.cpp +++ b/world/eqw.cpp @@ -19,23 +19,23 @@ #ifdef EMBPERL #include "../common/debug.h" -#include "EQW.h" -#include "EQWParser.h" -#include "WorldConfig.h" +#include "eqw.h" +#include "eqw_parser.h" +#include "world_config.h" #include "../common/races.h" #include "../common/classes.h" #include "../common/misc.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "zoneserver.h" #include "zonelist.h" #include "clientlist.h" #include "cliententry.h" -#include "LoginServer.h" -#include "LoginServerList.h" +#include "login_server.h" +#include "login_server_list.h" #include "worlddb.h" #include "client.h" -#include "LauncherList.h" -#include "LauncherLink.h" +#include "launcher_list.h" +#include "launcher_link.h" #include "wguild_mgr.h" #ifdef seed @@ -384,72 +384,57 @@ bool EQW::SetPublicNote(uint32 charid, const char *note) { } int EQW::CountBugs() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT count(*) FROM bugs where status = 0"), errbuf, &result)) { - safe_delete_array(query); - if((row = mysql_fetch_row(result))) { - int count = atoi(row[0]); - mysql_free_result(result); - return count; - } - mysql_free_result(result); - } - safe_delete_array(query); - return 0; + std::string query = "SELECT count(*) FROM bugs where status = 0"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + return 0; + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } std::vector EQW::ListBugs(uint32 offset) { std::vector res; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT id FROM bugs WHERE status = 0 limit %d, 30", offset), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - res.push_back(row[0]); - } - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("SELECT id FROM bugs WHERE status = 0 limit %d, 30", offset); + auto results = database.QueryDatabase(query); + + if (!results.Success()) + return res; + + for (auto row = results.begin();row != results.end(); ++row) + res.push_back(row[0]); + return res; } std::map EQW::GetBugDetails(Const_char *id) { std::map res; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(database.RunQuery(query, MakeAnyLenString(&query, "select name, zone, x, y, z, target, bug from bugs where id = %s", id), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - res["name"] = row[0]; - res["zone"] = row[1]; - res["x"] = row[2]; - res["y"] = row[3]; - res["z"] = row[4]; - res["target"] = row[5]; - res["bug"] = row[6]; - res["id"] = id; - } - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("SELECT name, zone, x, y, z, target, bug FROM bugs WHERE id = %s", id); + auto results = database.QueryDatabase(query); + + if (!results.Success()) + return res; + + for(auto row = results.begin(); row != results.end(); ++row) { + res["name"] = row[0]; + res["zone"] = row[1]; + res["x"] = row[2]; + res["y"] = row[3]; + res["z"] = row[4]; + res["target"] = row[5]; + res["bug"] = row[6]; + res["id"] = id; + } return res; } void EQW::ResolveBug(const char *id) { std::vector res; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if(database.RunQuery(query, MakeAnyLenString(&query, "UPDATE bugs SET status=1 WHERE id=%s", id), errbuf)) { - safe_delete_array(query); - } - safe_delete_array(query); + std::string query = StringFormat("UPDATE bugs SET status=1 WHERE id=%s", id); + database.QueryDatabase(query); } void EQW::SendMessage(uint32 type, const char *msg) { diff --git a/world/EQW.h b/world/eqw.h similarity index 100% rename from world/EQW.h rename to world/eqw.h diff --git a/world/EQWHTTPHandler.cpp b/world/eqw_http_handler.cpp similarity index 98% rename from world/EQWHTTPHandler.cpp rename to world/eqw_http_handler.cpp index f3126e6a3..6a703172a 100644 --- a/world/EQWHTTPHandler.cpp +++ b/world/eqw_http_handler.cpp @@ -16,11 +16,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "EQWHTTPHandler.h" +#include "eqw_http_handler.h" #include "../common/SocketLib/Base64.h" -#include "EQWParser.h" -#include "EQW.h" -#include "HTTPRequest.h" +#include "eqw_parser.h" +#include "eqw.h" +#include "http_request.h" #include "../common/logsys.h" #include "worlddb.h" #include "console.h" diff --git a/world/EQWHTTPHandler.h b/world/eqw_http_handler.h similarity index 97% rename from world/EQWHTTPHandler.h rename to world/eqw_http_handler.h index ab88a6e0b..0545adb42 100644 --- a/world/EQWHTTPHandler.h +++ b/world/eqw_http_handler.h @@ -18,8 +18,8 @@ #ifndef EQWHTTPHandler_H #define EQWHTTPHandler_H -#include "../common/TCPServer.h" -#include "../common/TCPConnection.h" +#include "../common/tcp_server.h" +#include "../common/tcp_connection.h" #include "../common/SocketLib/HttpdSocket.h" #include "../common/SocketLib/Mime.h" #include "../common/types.h" diff --git a/world/EQWParser.cpp b/world/eqw_parser.cpp similarity index 99% rename from world/EQWParser.cpp rename to world/eqw_parser.cpp index 840c01fe3..9aa372308 100644 --- a/world/EQWParser.cpp +++ b/world/eqw_parser.cpp @@ -21,9 +21,9 @@ #ifdef EMBPERL #include "../common/debug.h" -#include "EQWParser.h" -#include "EQW.h" -#include "../common/EQDB.h" +#include "eqw_parser.h" +#include "eqw.h" +#include "../common/eqdb.h" #include "../common/logsys.h" #include "worlddb.h" diff --git a/world/EQWParser.h b/world/eqw_parser.h similarity index 100% rename from world/EQWParser.h rename to world/eqw_parser.h diff --git a/world/HTTPRequest.cpp b/world/http_request.cpp similarity index 96% rename from world/HTTPRequest.cpp rename to world/http_request.cpp index b05aa5d66..fa7bc78f1 100644 --- a/world/HTTPRequest.cpp +++ b/world/http_request.cpp @@ -16,9 +16,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "HTTPRequest.h" -#include "EQWHTTPHandler.h" -#include "../common/EQDB.h" +#include "http_request.h" +#include "eqw_http_handler.h" +#include "../common/eqdb.h" #include "../common/SocketLib/HttpdForm.h" #include diff --git a/world/HTTPRequest.h b/world/http_request.h similarity index 100% rename from world/HTTPRequest.h rename to world/http_request.h diff --git a/world/LauncherLink.cpp b/world/launcher_link.cpp similarity index 98% rename from world/LauncherLink.cpp rename to world/launcher_link.cpp index 00d93da35..180e742a5 100644 --- a/world/LauncherLink.cpp +++ b/world/launcher_link.cpp @@ -17,17 +17,17 @@ */ #include "../common/debug.h" -#include "LauncherLink.h" -#include "LauncherList.h" -#include "WorldConfig.h" +#include "launcher_link.h" +#include "launcher_list.h" +#include "world_config.h" #include "../common/logsys.h" #include "../common/md5.h" #include "../common/packet_dump.h" #include "../common/servertalk.h" -#include "../common/EmuTCPConnection.h" -#include "../common/StringUtil.h" +#include "../common/emu_tcp_connection.h" +#include "../common/string_util.h" #include "worlddb.h" -#include "EQLConfig.h" +#include "eql_config.h" #include #include diff --git a/world/LauncherLink.h b/world/launcher_link.h similarity index 98% rename from world/LauncherLink.h rename to world/launcher_link.h index c5936bd3d..d1d8b6d61 100644 --- a/world/LauncherLink.h +++ b/world/launcher_link.h @@ -18,7 +18,7 @@ #ifndef LAUNCHERLINK_H_ #define LAUNCHERLINK_H_ -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/timer.h" #include #include diff --git a/world/LauncherList.cpp b/world/launcher_list.cpp similarity index 98% rename from world/LauncherList.cpp rename to world/launcher_list.cpp index f22476343..9c3bada2b 100644 --- a/world/LauncherList.cpp +++ b/world/launcher_list.cpp @@ -18,10 +18,10 @@ #include "../common/debug.h" -#include "LauncherList.h" -#include "LauncherLink.h" +#include "launcher_list.h" +#include "launcher_link.h" #include "../common/logsys.h" -#include "EQLConfig.h" +#include "eql_config.h" LauncherList::LauncherList() : nextID(1) diff --git a/world/LauncherList.h b/world/launcher_list.h similarity index 100% rename from world/LauncherList.h rename to world/launcher_list.h diff --git a/world/lfplist.cpp b/world/lfplist.cpp index 3e2e7611c..52474dfda 100644 --- a/world/lfplist.cpp +++ b/world/lfplist.cpp @@ -22,7 +22,7 @@ #include "zoneserver.h" #include "zonelist.h" #include "../common/logsys.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" extern ClientList client_list; extern ZSList zoneserver_list; diff --git a/world/LoginServer.cpp b/world/login_server.cpp similarity index 98% rename from world/LoginServer.cpp rename to world/login_server.cpp index 992f1d50a..83f432ca0 100644 --- a/world/LoginServer.cpp +++ b/world/login_server.cpp @@ -52,16 +52,16 @@ #define IGNORE_LS_FATAL_ERROR #include "../common/servertalk.h" -#include "LoginServer.h" -#include "LoginServerList.h" +#include "login_server.h" +#include "login_server_list.h" #include "../common/eq_packet_structs.h" #include "../common/packet_dump.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "zoneserver.h" #include "worlddb.h" #include "zonelist.h" #include "clientlist.h" -#include "WorldConfig.h" +#include "world_config.h" extern ZSList zoneserver_list; extern ClientList client_list; diff --git a/world/LoginServer.h b/world/login_server.h similarity index 96% rename from world/LoginServer.h rename to world/login_server.h index 6cb6957cf..766bdbf38 100644 --- a/world/LoginServer.h +++ b/world/login_server.h @@ -23,8 +23,8 @@ #include "../common/timer.h" #include "../common/queue.h" #include "../common/eq_packet_structs.h" -#include "../common/Mutex.h" -#include "../common/EmuTCPConnection.h" +#include "../common/mutex.h" +#include "../common/emu_tcp_connection.h" class LoginServer{ public: diff --git a/world/LoginServerList.cpp b/world/login_server_list.cpp similarity index 98% rename from world/LoginServerList.cpp rename to world/login_server_list.cpp index 5316804de..8e3780463 100644 --- a/world/LoginServerList.cpp +++ b/world/login_server_list.cpp @@ -26,15 +26,15 @@ #define IGNORE_LS_FATAL_ERROR #include "../common/servertalk.h" -#include "LoginServer.h" -#include "LoginServerList.h" +#include "login_server.h" +#include "login_server_list.h" #include "../common/eq_packet_structs.h" #include "../common/packet_dump.h" #include "zoneserver.h" #include "worlddb.h" #include "zonelist.h" #include "clientlist.h" -#include "WorldConfig.h" +#include "world_config.h" extern ZSList zoneserver_list; extern LoginServerList loginserverlist; diff --git a/world/LoginServerList.h b/world/login_server_list.h similarity index 92% rename from world/LoginServerList.h rename to world/login_server_list.h index 29ee8ed74..200e53b93 100644 --- a/world/LoginServerList.h +++ b/world/login_server_list.h @@ -6,8 +6,8 @@ #include "../common/timer.h" #include "../common/queue.h" #include "../common/eq_packet_structs.h" -#include "../common/Mutex.h" -#include "../common/EmuTCPConnection.h" +#include "../common/mutex.h" +#include "../common/emu_tcp_connection.h" #ifdef _WINDOWS void AutoInitLoginServer(void *tmp); diff --git a/world/net.cpp b/world/net.cpp index 02e4f7bbb..da8936bbd 100644 --- a/world/net.cpp +++ b/world/net.cpp @@ -27,22 +27,21 @@ #include "../common/debug.h" #include "../common/queue.h" #include "../common/timer.h" -#include "../common/EQStreamFactory.h" -#include "../common/EQPacket.h" -#include "client.h" -#include "worlddb.h" +#include "../common/eq_stream_factory.h" +#include "../common/eq_packet.h" #include "../common/seperator.h" #include "../common/version.h" #include "../common/eqtime.h" #include "../common/timeoutmgr.h" -#include "../common/EQEMuError.h" +#include "../common/eqemu_error.h" #include "../common/opcodemgr.h" #include "../common/guilds.h" -#include "../common/EQStreamIdent.h" -//#include "../common/patches/Client62.h" +#include "../common/eq_stream_ident.h" #include "../common/rulesys.h" #include "../common/platform.h" #include "../common/crash.h" +#include "client.h" +#include "worlddb.h" #ifdef _WINDOWS #include #define snprintf _snprintf @@ -68,22 +67,21 @@ #endif +#include "../common/emu_tcp_server.h" +#include "../common/patches/patches.h" #include "zoneserver.h" #include "console.h" -#include "LoginServer.h" -#include "LoginServerList.h" -#include "EQWHTTPHandler.h" -#include "../common/dbasync.h" -#include "../common/EmuTCPServer.h" -#include "WorldConfig.h" -#include "../common/patches/patches.h" +#include "login_server.h" +#include "login_server_list.h" +#include "eqw_http_handler.h" +#include "world_config.h" #include "zoneserver.h" #include "zonelist.h" #include "clientlist.h" -#include "LauncherList.h" +#include "launcher_list.h" #include "wguild_mgr.h" #include "lfplist.h" -#include "AdventureManager.h" +#include "adventure_manager.h" #include "ucs.h" #include "queryserv.h" #include "web_interface.h" @@ -102,7 +100,6 @@ QueryServConnection QSLink; WebInterfaceConnection WILink; LauncherList launcher_list; AdventureManager adventure_manager; -DBAsync *dbasync = nullptr; volatile bool RunLoops = true; uint32 numclients = 0; uint32 numzones = 0; @@ -117,6 +114,15 @@ int main(int argc, char** argv) { set_exception_handler(); register_remote_call_handlers(); + /* Database Version Check */ + uint32 Database_Version = CURRENT_BINARY_DATABASE_VERSION; + if (argc >= 2) { + if (strcasecmp(argv[1], "db_version") == 0) { + std::cout << "Binary Database Version: " << Database_Version << std::endl; + return 0; + } + } + // Load server configuration _log(WORLD__INIT, "Loading server configuration.."); if (!WorldConfig::LoadConfig()) { @@ -179,7 +185,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) { @@ -226,9 +231,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; } @@ -280,6 +284,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.."); @@ -289,10 +295,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: @@ -330,9 +339,6 @@ int main(int argc, char** argv) { _log(WORLD__INIT, "Reboot zone modes %s",holdzones ? "ON" : "OFF"); _log(WORLD__INIT, "Deleted %i stale player corpses from database", database.DeleteStalePlayerCorpses()); - if (RuleB(World, DeleteStaleCorpeBackups) == true) { - _log(WORLD__INIT, "Deleted %i stale player backups from database", database.DeleteStalePlayerBackups()); - } _log(WORLD__INIT, "Loading adventures..."); if(!adventure_manager.LoadAdventureTemplates()) diff --git a/world/perl_EQLConfig.cpp b/world/perl_eql_config.cpp similarity index 99% rename from world/perl_EQLConfig.cpp rename to world/perl_eql_config.cpp index 5179839a6..19ea7c9f6 100644 --- a/world/perl_EQLConfig.cpp +++ b/world/perl_eql_config.cpp @@ -29,8 +29,8 @@ typedef const char Const_char; #ifdef EMBPERL #include "../common/debug.h" -#include "EQWParser.h" -#include "EQLConfig.h" +#include "eqw_parser.h" +#include "eql_config.h" #ifdef seed #undef seed diff --git a/world/perl_EQW.cpp b/world/perl_eqw.cpp similarity index 99% rename from world/perl_EQW.cpp rename to world/perl_eqw.cpp index e6896a08c..0a564164d 100644 --- a/world/perl_EQW.cpp +++ b/world/perl_eqw.cpp @@ -29,8 +29,8 @@ typedef const char Const_char; #ifdef EMBPERL #include "../common/debug.h" -#include "EQWParser.h" -#include "EQW.h" +#include "eqw_parser.h" +#include "eqw.h" #ifdef seed #undef seed diff --git a/world/perl_HTTPRequest.cpp b/world/perl_http_request.cpp similarity index 99% rename from world/perl_HTTPRequest.cpp rename to world/perl_http_request.cpp index 54fa48780..12d70002c 100644 --- a/world/perl_HTTPRequest.cpp +++ b/world/perl_http_request.cpp @@ -29,8 +29,8 @@ typedef const char Const_char; #ifdef EMBPERL #include "../common/debug.h" -#include "EQWParser.h" -#include "HTTPRequest.h" +#include "eqw_parser.h" +#include "http_request.h" #ifdef seed #undef seed diff --git a/world/queryserv.cpp b/world/queryserv.cpp index eaa428332..2539fff63 100644 --- a/world/queryserv.cpp +++ b/world/queryserv.cpp @@ -1,12 +1,12 @@ #include "../common/debug.h" #include "queryserv.h" -#include "WorldConfig.h" +#include "world_config.h" #include "clientlist.h" #include "zonelist.h" #include "../common/logsys.h" #include "../common/logtypes.h" #include "../common/md5.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/packet_dump.h" extern ClientList client_list; diff --git a/world/queryserv.h b/world/queryserv.h index df6dde7f8..5053d7c99 100644 --- a/world/queryserv.h +++ b/world/queryserv.h @@ -2,7 +2,7 @@ #define QueryServ_H #include "../common/types.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/servertalk.h" class QueryServConnection diff --git a/world/remote_call.cpp b/world/remote_call.cpp index ea0a5a6fd..7e09fd468 100644 --- a/world/remote_call.cpp +++ b/world/remote_call.cpp @@ -4,9 +4,9 @@ #include "../common/logsys.h" #include "../common/logtypes.h" #include "../common/md5.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/packet_dump.h" -#include "WorldConfig.h" +#include "world_config.h" #include "clientlist.h" #include "zonelist.h" #include "web_interface.h" diff --git a/world/SoFCharCreateData.h b/world/sof_char_create_data.h similarity index 100% rename from world/SoFCharCreateData.h rename to world/sof_char_create_data.h diff --git a/world/ucs.cpp b/world/ucs.cpp index 262e8c187..f89874ae2 100644 --- a/world/ucs.cpp +++ b/world/ucs.cpp @@ -1,10 +1,10 @@ #include "../common/debug.h" #include "ucs.h" -#include "WorldConfig.h" +#include "world_config.h" #include "../common/logsys.h" #include "../common/logtypes.h" #include "../common/md5.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/packet_dump.h" UCSConnection::UCSConnection() diff --git a/world/ucs.h b/world/ucs.h index a65d82e3d..a6e8ae032 100644 --- a/world/ucs.h +++ b/world/ucs.h @@ -2,7 +2,7 @@ #define UCS_H #include "../common/types.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/servertalk.h" class UCSConnection diff --git a/world/web_interface.cpp b/world/web_interface.cpp index d3349f9b3..da6a5c955 100644 --- a/world/web_interface.cpp +++ b/world/web_interface.cpp @@ -1,6 +1,6 @@ #include "../common/debug.h" #include "web_interface.h" -#include "WorldConfig.h" +#include "world_config.h" #include "clientlist.h" #include "zonelist.h" #include "zoneserver.h" @@ -8,7 +8,7 @@ #include "../common/logsys.h" #include "../common/logtypes.h" #include "../common/md5.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/packet_dump.h" extern ClientList client_list; diff --git a/world/web_interface.h b/world/web_interface.h index 6db1900e2..3806d6620 100644 --- a/world/web_interface.h +++ b/world/web_interface.h @@ -2,7 +2,7 @@ #define WORLD_WEB_INTERFACE_H #include "../common/types.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/servertalk.h" class WebInterfaceConnection diff --git a/world/WorldConfig.cpp b/world/world_config.cpp similarity index 97% rename from world/WorldConfig.cpp rename to world/world_config.cpp index 96313ded3..2f9464e13 100644 --- a/world/WorldConfig.cpp +++ b/world/world_config.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "WorldConfig.h" +#include "world_config.h" WorldConfig *WorldConfig::_world_config = nullptr; diff --git a/world/WorldConfig.h b/world/world_config.h similarity index 98% rename from world/WorldConfig.h rename to world/world_config.h index 316e6cfc4..4689aa08f 100644 --- a/world/WorldConfig.h +++ b/world/world_config.h @@ -18,7 +18,7 @@ #ifndef __WorldConfig_H #define __WorldConfig_H -#include "../common/EQEmuConfig.h" +#include "../common/eqemu_config.h" class WorldConfig : public EQEmuConfig { public: diff --git a/world/world_logsys.cpp b/world/world_logsys.cpp index d7f798b6c..351af9767 100644 --- a/world/world_logsys.cpp +++ b/world/world_logsys.cpp @@ -1,7 +1,7 @@ #include "../common/debug.h" #include "../common/logsys.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "zoneserver.h" #include "client.h" diff --git a/world/WorldTCPConnection.h b/world/world_tcp_connection.h similarity index 100% rename from world/WorldTCPConnection.h rename to world/world_tcp_connection.h diff --git a/world/worlddb.cpp b/world/worlddb.cpp index ab8f6099a..458025349 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -17,16 +17,15 @@ */ #include "worlddb.h" -//#include "../common/Item.h" -#include "../common/StringUtil.h" +//#include "../common/item.h" +#include "../common/string_util.h" #include "../common/eq_packet_structs.h" -#include "../common/Item.h" -#include "../common/dbasync.h" +#include "../common/item.h" #include "../common/rulesys.h" #include #include #include -#include "SoFCharCreateData.h" +#include "sof_char_create_data.h" WorldDatabase database; extern std::vector character_create_allocations; @@ -34,295 +33,255 @@ 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) { + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); + uint32 idfile; + if (item->GetOrnamentationAug(ornamentationAugtype)) { + idfile = atoi(&item->GetOrnamentationAug(ornamentationAugtype)->GetItem()->IDFile[2]); + } + else if (item->GetOrnamentationIcon() && item->GetOrnamentationIDFile()) { + idfile = item->GetOrnamentationIDFile(); + } + else { + 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 an invalid bind point is specified, use the primary bind */ if (bindnum > 4) + { bindnum = 0; - - 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); } - 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; + std::string query = StringFormat("SELECT zone_id, instance_id, x, y, z FROM character_bind WHERE id = %u AND is_home = %u LIMIT 1", CharID, bindnum == 4 ? 1 : 0); + auto results = database.QueryDatabase(query); + if(!results.Success() || results.RowCount() == 0) { + return 0; } - safe_delete_array(query); - return pp.binds[bindnum].zoneId; + int zone_id, instance_id; + double x, y, z, heading; + for (auto row = results.begin(); row != results.end(); ++row) { + zone_id = atoi(row[0]); + instance_id = atoi(row[1]); + x = atof(row[2]); + y = atof(row[3]); + z = atof(row[4]); + heading = atof(row[5]); + } + + query = StringFormat("UPDATE character_data SET zone_id = '%d', zone_instance = '%d', x = '%f', y = '%f', z = '%f', heading = '%f' WHERE id = %u", + zone_id, instance_id, x, y, z, heading, CharID); + + results = database.QueryDatabase(query); + if(!results.Success()) { + return 0; + } + + return zone_id; } 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; + in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = in_pp->binds[0].instance_id = 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: @@ -333,83 +292,93 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* } case 1: { - in_pp->zone_id =2; // qeynos2 + in_pp->zone_id = 2; // qeynos2 in_pp->binds[0].zoneId = 2; // qeynos2 break; } case 2: { - in_pp->zone_id =29; // halas + in_pp->zone_id = 29; // halas in_pp->binds[0].zoneId = 30; // everfrost break; } case 3: { - in_pp->zone_id =19; // rivervale + in_pp->zone_id = 19; // rivervale in_pp->binds[0].zoneId = 20; // kithicor break; } case 4: { - in_pp->zone_id =9; // freportw + in_pp->zone_id = 9; // freportw in_pp->binds[0].zoneId = 9; // freportw break; } case 5: { - in_pp->zone_id =40; // neriaka + in_pp->zone_id = 40; // neriaka in_pp->binds[0].zoneId = 25; // nektulos break; } case 6: { - in_pp->zone_id =52; // gukta + in_pp->zone_id = 52; // gukta in_pp->binds[0].zoneId = 46; // innothule break; } case 7: { - in_pp->zone_id =49; // oggok + in_pp->zone_id = 49; // oggok in_pp->binds[0].zoneId = 47; // feerrott break; } case 8: { - in_pp->zone_id =60; // kaladima + in_pp->zone_id = 60; // kaladima in_pp->binds[0].zoneId = 68; // butcher break; } case 9: { - in_pp->zone_id =54; // gfaydark + in_pp->zone_id = 54; // gfaydark in_pp->binds[0].zoneId = 54; // gfaydark break; } case 10: { - in_pp->zone_id =61; // felwithea + in_pp->zone_id = 61; // felwithea in_pp->binds[0].zoneId = 54; // gfaydark break; } case 11: { - in_pp->zone_id =55; // akanon + in_pp->zone_id = 55; // akanon in_pp->binds[0].zoneId = 56; // steamfont break; } case 12: { - in_pp->zone_id =82; // cabwest + in_pp->zone_id = 82; // cabwest in_pp->binds[0].zoneId = 78; // fieldofbone break; } case 13: { - in_pp->zone_id =155; // sharvahl + in_pp->zone_id = 155; // sharvahl in_pp->binds[0].zoneId = 155; // sharvahl 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 +386,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 +399,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; + in_pp->binds[0].x = in_pp->binds[0].y = in_pp->binds[0].z = in_pp->binds[0].zoneId = in_pp->binds[0].instance_id = 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 +427,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 +444,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 +472,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 +533,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 0dcb6ef44..ecb39ef61 100644 --- a/world/worlddb.h +++ b/world/worlddb.h @@ -19,7 +19,7 @@ #define WORLDDB_H_ #include "../common/shareddb.h" -#include "../common/ZoneNumbers.h" +#include "../common/zone_numbers.h" struct PlayerProfile_Struct; struct CharCreate_Struct; @@ -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 db4843926..c97010aa8 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -18,12 +18,12 @@ #include "../common/debug.h" #include "zonelist.h" #include "zoneserver.h" -#include "WorldTCPConnection.h" +#include "world_tcp_connection.h" #include "worlddb.h" #include "console.h" -#include "WorldConfig.h" +#include "world_config.h" #include "../common/servertalk.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" extern uint32 numzones; extern bool holdzones; @@ -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 abe548751..1ca6d81e7 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -18,22 +18,22 @@ #include "../common/debug.h" #include "zoneserver.h" #include "clientlist.h" -#include "LoginServer.h" -#include "LoginServerList.h" +#include "login_server.h" +#include "login_server_list.h" #include "zonelist.h" #include "worlddb.h" #include "console.h" #include "client.h" #include "../common/md5.h" -#include "WorldConfig.h" +#include "world_config.h" #include "../common/guilds.h" #include "../common/packet_dump.h" #include "../common/misc.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "cliententry.h" #include "wguild_mgr.h" #include "lfplist.h" -#include "AdventureManager.h" +#include "adventure_manager.h" #include "ucs.h" #include "queryserv.h" #include "web_interface.h" @@ -411,6 +411,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; @@ -428,6 +436,8 @@ bool ZoneServer::Process() { break; } case ServerOP_ChannelMessage: { + if (pack->size < sizeof(ServerChannelMessage_Struct)) + break; ServerChannelMessage_Struct* scm = (ServerChannelMessage_Struct*) pack->pBuffer; if(scm->chan_num == 20) { @@ -439,42 +449,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 (cle == 0 || cle->Online() < CLE_Status_Zoning || + (cle->TellsOff() && ((cle->Anon() == 1 && scm->fromadmin < cle->Admin()) || scm->fromadmin < 80))) { if (!scm->noreply) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - //MYSQL_ROW row; Trumpcard - commenting. Currently unused. - time_t rawtime; - struct tm * timeinfo; - time ( &rawtime ); - timeinfo = localtime ( &rawtime ); - char *telldate=asctime(timeinfo); - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT name from character_ where name='%s'",scm->deliverto), errbuf, &result)) { - safe_delete(query); - if (result!=0) { - if (database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO tellque (Date,Receiver,Sender,Message) values('%s','%s','%s','%s')",telldate,scm->deliverto,scm->from,scm->message), errbuf, &result)) - 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); - safe_delete(query); - } - else - zoneserver_list.SendEmoteMessage(scm->from, 0, 0, 0, "You told %s, '%s is not online at this time'", scm->to, scm->to); - mysql_free_result(result); - } - else - safe_delete(query); + ClientListEntry* sender = client_list.FindCharacter(scm->from); + if (!sender || !sender->Server()) + 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 || !sender->Server()) + 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 || !sender->Server()) + 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) @@ -651,17 +667,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)) @@ -674,40 +690,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); @@ -715,8 +722,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; @@ -724,27 +730,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); } } @@ -782,21 +785,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; @@ -1267,39 +1267,20 @@ bool ZoneServer::Process() { UCSLink.SendPacket(pack); break; } - + case ServerOP_QSSendQuery: 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: + case ServerOP_QSPlayerLogMerchantTransactions: { QSLink.SendPacket(pack); break; } - case ServerOP_QSMerchantLogTransactions: - { - QSLink.SendPacket(pack); - break; - } + case ServerOP_WIRemoteCallResponse: case ServerOP_WIClientSession: case ServerOP_WIRemoteCallToClient: @@ -1308,7 +1289,9 @@ bool ZoneServer::Process() { break; } case ServerOP_CZSignalClientByName: - case ServerOP_CZMessagePlayer: + case ServerOP_CZMessagePlayer: + case ServerOP_CZSignalNPC: + case ServerOP_CZSetEntityVariableByNPCTypeID: case ServerOP_CZSignalClient: case ServerOP_DepopAllPlayersCorpses: case ServerOP_DepopPlayerCorpse: @@ -1321,6 +1304,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/world/zoneserver.h b/world/zoneserver.h index 955c0b376..661e03019 100644 --- a/world/zoneserver.h +++ b/world/zoneserver.h @@ -18,8 +18,8 @@ #ifndef ZONESERVER_H #define ZONESERVER_H -#include "WorldTCPConnection.h" -#include "../common/EmuTCPConnection.h" +#include "world_tcp_connection.h" +#include "../common/emu_tcp_connection.h" #include #include diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 7afe9ddd4..d7d0d0a50 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -1,7 +1,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 2.8) SET(zone_sources - AA.cpp + aa.cpp aggro.cpp attack.cpp beacon.cpp @@ -16,7 +16,7 @@ SET(zone_sources command.cpp corpse.cpp doors.cpp - effects.cpp + effects.cpp embparser.cpp embparser_api.cpp embperl.cpp @@ -69,12 +69,12 @@ SET(zone_sources map.cpp merc.cpp mob.cpp - MobAI.cpp + mob_ai.cpp mod_functions.cpp net.cpp npc.cpp - NpcAI.cpp - Object.cpp + npc_ai.cpp + object.cpp oriented_bounding_box.cpp pathing.cpp perl_client.cpp @@ -86,17 +86,18 @@ SET(zone_sources perl_npc.cpp perl_object.cpp perl_perlpacket.cpp - perl_PlayerCorpse.cpp + perl_player_corpse.cpp perl_questitem.cpp perl_raids.cpp perlpacket.cpp petitions.cpp pets.cpp - QGlobals.cpp + qglobals.cpp + queryserv.cpp questmgr.cpp - QuestParserCollection.cpp + quest_parser_collection.cpp raids.cpp - RaycastMesh.cpp + raycast_mesh.cpp remote_call.cpp remote_call_subscribe.cpp spawn2.cpp @@ -118,18 +119,17 @@ SET(zone_sources worldserver.cpp zone.cpp zone_logsys.cpp - ZoneConfig.cpp + zone_config.cpp zonedb.cpp - zonedbasync.cpp zoning.cpp ) SET(zone_headers - AA.h + aa.h basic_functions.h beacon.h bot.h - botStructs.h + bot_structs.h client.h client_logs.h client_packet.h @@ -177,27 +177,29 @@ SET(zone_headers mob.h net.h npc.h - NpcAI.h + npc_ai.h object.h oriented_bounding_box.h pathing.h perlpacket.h petitions.h pets.h - QGlobals.h - QuestInterface.h + qglobals.h + quest_interface.h + queryserv.h + quest_interface.h questmgr.h - QuestParserCollection.h + quest_parser_collection.h raid.h raids.h - RaycastMesh.h + raycast_mesh.h remote_call.h remote_call_subscribe.h skills.h spawn2.cpp spawn2.h spawngroup.h - StringIDs.h + string_ids.h tasks.h titles.h trap.h @@ -206,9 +208,8 @@ SET(zone_headers water_map_v2.h worldserver.h zone.h - ZoneConfig.h + zone_config.h zonedb.h - zonedbasync.h zonedump.h ) diff --git a/zone/AA.cpp b/zone/aa.cpp similarity index 73% rename from zone/AA.cpp rename to zone/aa.cpp index de3704fcd..a8c6ff3d0 100644 --- a/zone/AA.cpp +++ b/zone/aa.cpp @@ -16,79 +16,34 @@ Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// Test 1 - -#include "../common/debug.h" -#include "AA.h" -#include "mob.h" -#include "client.h" -#include "groups.h" -#include "raids.h" -#include "../common/spdat.h" -#include "object.h" -#include "doors.h" -#include "beacon.h" -#include "corpse.h" -#include "titles.h" -#include "../common/races.h" #include "../common/classes.h" +#include "../common/debug.h" #include "../common/eq_packet_structs.h" #include "../common/packet_dump.h" -#include "../common/StringUtil.h" -#include "../common/logsys.h" -#include "zonedb.h" -#include "StringIDs.h" +#include "../common/races.h" +#include "../common/spdat.h" +#include "../common/string_util.h" + +#include "aa.h" +#include "client.h" +#include "corpse.h" +#include "groups.h" +#include "mob.h" +#include "queryserv.h" +#include "raids.h" +#include "string_ids.h" +#include "titles.h" +#include "zonedb.h" + +extern QueryServ* QServ; + -//static data arrays, really not big enough to warrant shared mem. AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank] std::mapaas_send; std::map > aa_effects; //stores the effects from the aa_effects table in memory std::map AARequiredLevelAndCost; -/* - -Schema: - -spell_id is spell to cast, SPELL_UNKNOWN == no spell -nonspell_action is action to preform on activation which is not a spell, 0=none -nonspell_mana is mana that the nonspell action consumes -nonspell_duration is a duration which may be used by the nonspell action -redux_aa is the aa which reduces the reuse timer of the skill -redux_rate is the multiplier of redux_aa, as a percentage of total rate (10 == 10% faster) - -CREATE TABLE aa_actions ( - aaid mediumint unsigned not null, - rank tinyint unsigned not null, - reuse_time mediumint unsigned not null, - spell_id mediumint unsigned not null, - target tinyint unsigned not null, - nonspell_action tinyint unsigned not null, - nonspell_mana mediumint unsigned not null, - nonspell_duration mediumint unsigned not null, - redux_aa mediumint unsigned not null, - redux_rate tinyint not null, - - PRIMARY KEY(aaid, rank) -); - -CREATE TABLE aa_swarmpets ( - spell_id mediumint unsigned not null, - count tinyint unsigned not null, - npc_id int not null, - duration mediumint unsigned not null, - PRIMARY KEY(spell_id) -); -*/ - -/* - -Credits for this function: - -FatherNitwit: Structure and mechanism - -Wiz: Initial set of AAs, original function contents - -Branks: Much updated info and a bunch of higher-numbered AAs - -*/ int Client::GetAATimerID(aaID activate) { SendAA_Struct* aa2 = zone->FindAA(activate); @@ -297,7 +252,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); @@ -313,13 +268,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(); } @@ -458,7 +414,7 @@ void Client::HandleAAAction(aaID activate) { Escape(); break; - // Don't think this code is used any longer for Bestial Alignment as the AA has a spell_id and no nonspell_action. + // Don't think this code is used any longer for Bestial Alignment as the aa.has a spell_id and no nonspell_action. case aaActionBeastialAlignment: switch(GetBaseRace()) { case BARBARIAN: @@ -522,7 +478,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); @@ -536,10 +492,7 @@ void Client::HandleAAAction(aaID activate) { } } - -//Originally written by Branks -//functionality rewritten by Father Nitwit -void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override) { +void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg) { //It might not be a bad idea to put these into the database, eventually.. @@ -557,7 +510,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u pet.count = 1; pet.duration = 1; - for(int x = 0; x < 12; x++) + for(int x = 0; x < MAX_SWARM_PETS; x++) { if(spells[spell_id].effectid[x] == SE_TemporaryPets) { @@ -601,8 +554,6 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, 10, 10, -10, -10, 8, 8, -8, -8 }; - TempPets(true); - while(summon_count > 0) { int pet_duration = pet.duration; if(duration_override > 0) @@ -622,7 +573,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], GetZ(), GetHeading(), FlyMode3); - if((spell_id == 6882) || (spell_id == 6884)) + if (followme) npca->SetFollowID(GetID()); if(!npca->GetSwarmInfo()){ @@ -640,7 +591,10 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u //give the pets somebody to "love" if(targ != nullptr){ npca->AddToHateList(targ, 1000, 1000); - npca->GetSwarmInfo()->target = targ->GetID(); + if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) + npca->GetSwarmInfo()->target = targ->GetID(); + else + npca->GetSwarmInfo()->target = 0; } //we allocated a new NPC type object, give the NPC ownership of that memory @@ -656,7 +610,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u targ->AddToHateList(this, 1, 0); } -void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme) { +void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme, bool sticktarg) { AA_SwarmPet pet; pet.count = 1; @@ -694,7 +648,6 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, 10, 10, -10, -10, 8, 8, -8, -8 }; - TempPets(true); while(summon_count > 0) { int pet_duration = pet.duration; @@ -715,6 +668,9 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], GetZ(), GetHeading(), FlyMode3); + if (followme) + npca->SetFollowID(GetID()); + if(!npca->GetSwarmInfo()){ AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; npca->SetSwarmInfo(nSI); @@ -730,7 +686,11 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid //give the pets somebody to "love" if(targ != nullptr){ npca->AddToHateList(targ, 1000, 1000); - npca->GetSwarmInfo()->target = targ->GetID(); + + if (RuleB(Spells, SwarmPetTargetLock) || sticktarg) + npca->GetSwarmInfo()->target = targ->GetID(); + else + npca->GetSwarmInfo()->target = 0; } //we allocated a new NPC type object, give the NPC ownership of that memory @@ -889,8 +849,6 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration) make_npc->d_meele_texture1 = 0; make_npc->d_meele_texture2 = 0; - TempPets(true); - NPC* npca = new NPC(make_npc, 0, GetX(), GetY(), GetZ(), GetHeading(), FlyMode3); if(!npca->GetSwarmInfo()){ @@ -1032,23 +990,26 @@ 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 real_cost = aa2->cost + (aa2->cost_inc * cur_level); - if(m_pp.aapoints >= real_cost && cur_level < aa2->max_level) { - SetAA(aa2->id, cur_level+1); + if (m_pp.aapoints >= real_cost && cur_level < aa2->max_level) { + SetAA(aa2->id, cur_level + 1); - mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level+1); + mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level + 1); 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)){ + && ((aa2->max_level == (cur_level + 1)) && aa2->sof_next_id)){ SendAA(aa2->id); SendAA(aa2->sof_next_id); } @@ -1057,13 +1018,31 @@ 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 - if(cur_level<1) - Message(15,"You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1)?"points":"point"); - else - Message(15,"You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level+1, real_cost, (real_cost>1)?"points":"point"); + /* + 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){ + Message(15, "You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1) ? "points" : "point"); + + /* QS: Player_Log_AA_Purchases */ + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Initial AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); + } + } + /* Ranked purchase of an AA ability */ + else{ + Message(15, "You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level + 1, real_cost, (real_cost > 1) ? "points" : "point"); + + /* QS: Player_Log_AA_Purchases */ + if (RuleB(QueryServ, PlayerLogAAPurchases)){ + std::string event_desc = StringFormat("Ranked AA Purchase :: aa_name:%s aa_id:%i at cost:%i in zoneid:%i instid:%i", aa2->name, aa2->id, real_cost, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_AA_Purchases, this->CharacterID(), event_desc); + } + } SendAAStats(); @@ -1237,7 +1216,7 @@ void Client::SendAA(uint32 id, int seq) { Note: There were many ways to achieve this effect - The method used proved to be the most straight forward and consistent. Stacking does not currently work ideally for AA's that use hotkeys, therefore they will be excluded at this time. - TODO: Problem with AA hotkeys - When you reach max rank of an AA tier (ie 5/5), it automatically displays the next AA in + TODO: Problem with aa.hotkeys - When you reach max rank of an AA tier (ie 5/5), it automatically displays the next AA in the series and you can not transfer the hotkey to the next AA series. To the best of the my ability and through many different variations of coding I could not find an ideal solution to this issue. @@ -1466,40 +1445,42 @@ void Zone::LoadAAs() { bool ZoneDatabase::LoadAAEffects2() { aa_effects.clear(); //start fresh - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT aaid, slot, effectid, base1, base2 FROM aa_effects ORDER BY aaid ASC, slot ASC"), errbuf, &result)) { - int count = 0; - while((row = mysql_fetch_row(result))!= nullptr) { - int aaid = atoi(row[0]); - int slot = atoi(row[1]); - int effectid = atoi(row[2]); - int base1 = atoi(row[3]); - int base2 = atoi(row[4]); - aa_effects[aaid][slot].skill_id = effectid; - aa_effects[aaid][slot].base1 = base1; - aa_effects[aaid][slot].base2 = base2; - aa_effects[aaid][slot].slot = slot; //not really needed, but we'll populate it just in case - count++; - } - mysql_free_result(result); - if (count < 1) //no results - LogFile->write(EQEMuLog::Error, "Error loading AA Effects, none found in the database."); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAEffects2 query: '%s': %s", query, errbuf); + const std::string query = "SELECT aaid, slot, effectid, base1, base2 FROM aa_effects ORDER BY aaid ASC, slot ASC"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAEffects2 query: '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); + + if (!results.RowCount()) { //no results + LogFile->write(EQEMuLog::Error, "Error loading AA Effects, none found in the database."); + return false; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + int aaid = atoi(row[0]); + int slot = atoi(row[1]); + int effectid = atoi(row[2]); + int base1 = atoi(row[3]); + int base2 = atoi(row[4]); + aa_effects[aaid][slot].skill_id = effectid; + aa_effects[aaid][slot].base1 = base1; + aa_effects[aaid][slot].base2 = base2; + aa_effects[aaid][slot].slot = slot; //not really needed, but we'll populate it just in case + } + 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; @@ -1511,10 +1492,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)) @@ -1537,6 +1559,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)) @@ -1559,6 +1616,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)) @@ -1581,6 +1673,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)) @@ -1606,100 +1745,86 @@ 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() bool ZoneDatabase::LoadAAEffects() { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - memset(AA_Actions, 0, sizeof(AA_Actions)); //I hope the compiler is smart about this size... - const char *query = "SELECT aaid,rank,reuse_time,spell_id,target,nonspell_action,nonspell_mana,nonspell_duration," - "redux_aa,redux_rate,redux_aa2,redux_rate2 FROM aa_actions"; - - if(RunQuery(query, static_cast(strlen(query)), errbuf, &result)) { - //safe_delete_array(query); - int r; - while ((row = mysql_fetch_row(result))) { - r = 0; - int aaid = atoi(row[r++]); - int rank = atoi(row[r++]); - if(aaid < 0 || aaid >= aaHighestID || rank < 0 || rank >= MAX_AA_ACTION_RANKS) - continue; - AA_DBAction *caction = &AA_Actions[aaid][rank]; - - caction->reuse_time = atoi(row[r++]); - caction->spell_id = atoi(row[r++]); - caction->target = (aaTargetType) atoi(row[r++]); - caction->action = (aaNonspellAction) atoi(row[r++]); - caction->mana_cost = atoi(row[r++]); - caction->duration = atoi(row[r++]); - caction->redux_aa = (aaID) atoi(row[r++]); - caction->redux_rate = atoi(row[r++]); - caction->redux_aa2 = (aaID) atoi(row[r++]); - caction->redux_rate2 = atoi(row[r++]); - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadAAEffects query '%s': %s", query, errbuf);; - //safe_delete_array(query); + const std::string query = "SELECT aaid, rank, reuse_time, spell_id, target, " + "nonspell_action, nonspell_mana, nonspell_duration, " + "redux_aa, redux_rate, redux_aa2, redux_rate2 FROM aa_actions"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadAAEffects query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + + int aaid = atoi(row[0]); + int rank = atoi(row[1]); + if(aaid < 0 || aaid >= aaHighestID || rank < 0 || rank >= MAX_AA_ACTION_RANKS) + continue; + AA_DBAction *caction = &AA_Actions[aaid][rank]; + + caction->reuse_time = atoi(row[2]); + caction->spell_id = atoi(row[3]); + caction->target = (aaTargetType) atoi(row[4]); + caction->action = (aaNonspellAction) atoi(row[5]); + caction->mana_cost = atoi(row[6]); + caction->duration = atoi(row[7]); + caction->redux_aa = (aaID) atoi(row[8]); + caction->redux_rate = atoi(row[9]); + caction->redux_aa2 = (aaID) atoi(row[10]); + caction->redux_rate2 = atoi(row[11]); + + } return true; } -//Returns the number effects an AA has when we send them to the client +//Returns the number effects an aa.has when we send them to the client //For the purposes of sizing a packet because every skill does not //have the same number effects, they can range from none to a few depending on AA. //counts the # of effects by counting the different slots of an AAID in the DB. //AndMetal: this may now be obsolete since we have Zone::GetTotalAALevels() uint8 ZoneDatabase::GetTotalAALevels(uint32 skill_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int total=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(slot) from aa_effects where aaid=%i", skill_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - total=atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetTotalAALevels '%s: %s", query, errbuf); - safe_delete_array(query); - } - return total; + + std::string query = StringFormat("SELECT count(slot) FROM aa_effects WHERE aaid = %i", skill_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetTotalAALevels '%s: %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } //this will allow us to count the number of effects for an AA by pulling the info from memory instead of the database. hopefully this will same some CPU cycles @@ -1727,59 +1852,50 @@ void ZoneDatabase::FillAAEffects(SendAA_Struct* aa_struct){ if(!aa_struct) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id), errbuf, &result)) { - int ndx=0; - while((row = mysql_fetch_row(result))!=nullptr) { - aa_struct->abilities[ndx].skill_id=atoi(row[0]); - aa_struct->abilities[ndx].base1=atoi(row[1]); - aa_struct->abilities[ndx].base2=atoi(row[2]); - aa_struct->abilities[ndx].slot=atoi(row[3]); - ndx++; + auto it = aa_effects.find(aa_struct->id); + if (it != aa_effects.end()) { + for (uint32 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; } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query, errbuf); } - safe_delete_array(query); } uint32 ZoneDatabase::CountAAs(){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(title_sid) from altadv_vars"), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr) - count = atoi(row[0]); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAAs query '%s': %s", query, errbuf); + + const std::string query = "SELECT count(title_sid) FROM altadv_vars"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAAs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; } - safe_delete_array(query); - return count; + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]);; } -uint32 ZoneDatabase::CountAAEffects(){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(id) from aa_effects"), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr){ - count = atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAALevels query '%s': %s", query, errbuf); +uint32 ZoneDatabase::CountAAEffects() { + + const std::string query = "SELECT count(id) FROM aa_effects"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAALevels query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; } - safe_delete_array(query); - return count; + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } uint32 ZoneDatabase::GetSizeAA(){ @@ -1792,164 +1908,127 @@ uint32 ZoneDatabase::GetSizeAA(){ void ZoneDatabase::LoadAAs(SendAA_Struct **load){ if(!load) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id from altadv_vars order by skill_id"), errbuf, &result)) { - int skill=0,ndx=0; - while((row = mysql_fetch_row(result))!=nullptr) { - skill=atoi(row[0]); - load[ndx] = GetAASkillVars(skill); - load[ndx]->seq = ndx+1; - ndx++; + + std::string query = "SELECT skill_id FROM altadv_vars ORDER BY skill_id"; + auto results = QueryDatabase(query); + if (results.Success()) { + int skill = 0, index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + skill = atoi(row[0]); + load[index] = GetAASkillVars(skill); + load[index]->seq = index+1; } - mysql_free_result(result); } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf); + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); } - safe_delete_array(query); AARequiredLevelAndCost.clear(); + query = "SELECT skill_id, level, cost from aa_required_level_cost order by skill_id"; + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id, level, cost from aa_required_level_cost order by skill_id"), errbuf, &result)) - { - AALevelCost_Struct aalcs; - while((row = mysql_fetch_row(result))!=nullptr) - { - aalcs.Level = atoi(row[1]); - aalcs.Cost = atoi(row[2]); - AARequiredLevelAndCost[atoi(row[0])] = aalcs; - } - mysql_free_result(result); - } - else - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf); + AALevelCost_Struct aalcs; + for (auto row = results.begin(); row != results.end(); ++row) { + aalcs.Level = atoi(row[1]); + aalcs.Cost = atoi(row[2]); + AARequiredLevelAndCost[atoi(row[0])] = aalcs; + } - safe_delete_array(query); } SendAA_Struct* ZoneDatabase::GetAASkillVars(uint32 skill_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - SendAA_Struct* sendaa = nullptr; - uchar* buffer; - if (RunQuery(query, MakeAnyLenString(&query, "SET @row = 0"), errbuf)) { //initialize "row" variable in database for next query - safe_delete_array(query); - MYSQL_RES *result; //we don't really need these unless we get to this point, so why bother? - MYSQL_ROW row; + std::string query = "SET @row = 0"; //initialize "row" variable in database for next query + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return nullptr; + } - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT " - "a.cost, " - "a.max_level, " - "a.hotkey_sid, " - "a.hotkey_sid2, " - "a.title_sid, " - "a.desc_sid, " - "a.type, " - "COALESCE(" //so we can return 0 if it's null - "(" //this is our derived table that has the row # that we can SELECT from, because the client is stupid - "SELECT " - "p.prereq_index_num " - "FROM " - "(" - "SELECT " - "a2.skill_id, " - "@row := @row + 1 AS prereq_index_num " - "FROM " - "altadv_vars a2" - ") AS p " - "WHERE " - "p.skill_id = a.prereq_skill" - "), " - "0) AS prereq_skill_index, " - "a.prereq_minpoints, " - "a.spell_type, " - "a.spell_refresh, " - "a.classes, " - "a.berserker, " - "a.spellid, " - "a.class_type, " - "a.name, " - "a.cost_inc, " - "a.aa_expansion, " - "a.special_category, " - "a.sof_type, " - "a.sof_cost_inc, " - "a.sof_max_level, " - "a.sof_next_skill, " - "a.clientver, " // Client Version 0 = None, 1 = All, 2 = Titanium/6.2, 4 = SoF 5 = SOD 6 = UF - "a.account_time_required, " - "a.sof_current_level," - "a.sof_next_id, " - "a.level_inc " - " FROM altadv_vars a WHERE skill_id=%i", skill_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - int total_abilities = GetTotalAALevels(skill_id); //eventually we'll want to use zone->GetTotalAALevels(skill_id) since it should save queries to the DB - int totalsize = total_abilities * sizeof(AA_Ability) + sizeof(SendAA_Struct); + query = StringFormat("SELECT a.cost, a.max_level, a.hotkey_sid, a.hotkey_sid2, a.title_sid, a.desc_sid, a.type, " + "COALESCE(" //So we can return 0 if it's null. + "(" // this is our derived table that has the row # + // that we can SELECT from, because the client is stupid. + "SELECT p.prereq_index_num " + "FROM (SELECT a2.skill_id, @row := @row + 1 AS prereq_index_num " + "FROM altadv_vars a2) AS p " + "WHERE p.skill_id = a.prereq_skill), 0) " + "AS prereq_skill_index, a.prereq_minpoints, a.spell_type, a.spell_refresh, a.classes, " + "a.berserker, a.spellid, a.class_type, a.name, a.cost_inc, a.aa_expansion, a.special_category, " + "a.sof_type, a.sof_cost_inc, a.sof_max_level, a.sof_next_skill, " + "a.clientver, " // Client Version 0 = None, 1 = All, 2 = Titanium/6.2, 4 = SoF 5 = SOD 6 = UF + "a.account_time_required, a.sof_current_level, a.sof_next_id, a.level_inc " + "FROM altadv_vars a WHERE skill_id=%i", skill_id); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return nullptr; + } - buffer = new uchar[totalsize]; - memset(buffer,0,totalsize); - sendaa = (SendAA_Struct*)buffer; + if (results.RowCount() != 1) + return nullptr; - row = mysql_fetch_row(result); + int total_abilities = GetTotalAALevels(skill_id); //eventually we'll want to use zone->GetTotalAALevels(skill_id) since it should save queries to the DB + int totalsize = total_abilities * sizeof(AA_Ability) + sizeof(SendAA_Struct); - //ATOI IS NOT UNSIGNED LONG-SAFE!!! + SendAA_Struct* sendaa = nullptr; + uchar* buffer; - sendaa->cost = atoul(row[0]); - sendaa->cost2 = sendaa->cost; - sendaa->max_level = atoul(row[1]); - sendaa->hotkey_sid = atoul(row[2]); - sendaa->id = skill_id; - sendaa->hotkey_sid2 = atoul(row[3]); - sendaa->title_sid = atoul(row[4]); - sendaa->desc_sid = atoul(row[5]); - sendaa->type = atoul(row[6]); - sendaa->prereq_skill = atoul(row[7]); - sendaa->prereq_minpoints = atoul(row[8]); - sendaa->spell_type = atoul(row[9]); - sendaa->spell_refresh = atoul(row[10]); - sendaa->classes = static_cast(atoul(row[11])); - sendaa->berserker = static_cast(atoul(row[12])); - sendaa->last_id = 0xFFFFFFFF; - sendaa->current_level=1; - sendaa->spellid = atoul(row[13]); - sendaa->class_type = atoul(row[14]); - strcpy(sendaa->name,row[15]); + buffer = new uchar[totalsize]; + memset(buffer,0,totalsize); + sendaa = (SendAA_Struct*)buffer; - sendaa->total_abilities=total_abilities; - if(sendaa->max_level > 1) - sendaa->next_id=skill_id+1; - else - sendaa->next_id=0xFFFFFFFF; + auto row = results.begin(); + + //ATOI IS NOT UNSIGNED LONG-SAFE!!! + + sendaa->cost = atoul(row[0]); + sendaa->cost2 = sendaa->cost; + sendaa->max_level = atoul(row[1]); + sendaa->hotkey_sid = atoul(row[2]); + sendaa->id = skill_id; + sendaa->hotkey_sid2 = atoul(row[3]); + sendaa->title_sid = atoul(row[4]); + sendaa->desc_sid = atoul(row[5]); + sendaa->type = atoul(row[6]); + sendaa->prereq_skill = atoul(row[7]); + sendaa->prereq_minpoints = atoul(row[8]); + sendaa->spell_type = atoul(row[9]); + sendaa->spell_refresh = atoul(row[10]); + sendaa->classes = static_cast(atoul(row[11])); + sendaa->berserker = static_cast(atoul(row[12])); + sendaa->last_id = 0xFFFFFFFF; + sendaa->current_level=1; + sendaa->spellid = atoul(row[13]); + sendaa->class_type = atoul(row[14]); + strcpy(sendaa->name,row[15]); + + sendaa->total_abilities=total_abilities; + if(sendaa->max_level > 1) + sendaa->next_id=skill_id+1; + else + sendaa->next_id=0xFFFFFFFF; + + sendaa->cost_inc = atoi(row[16]); + + // Begin SoF Specific/Adjusted AA Fields + sendaa->aa_expansion = atoul(row[17]); + sendaa->special_category = atoul(row[18]); + sendaa->sof_type = atoul(row[19]); + sendaa->sof_cost_inc = atoi(row[20]); + sendaa->sof_max_level = atoul(row[21]); + sendaa->sof_next_skill = atoul(row[22]); + sendaa->clientver = atoul(row[23]); + sendaa->account_time_required = atoul(row[24]); + + //Internal use only - not sent to client + sendaa->sof_current_level = atoul(row[25]); + sendaa->sof_next_id = atoul(row[26]); + sendaa->level_inc = static_cast(atoul(row[27])); - sendaa->cost_inc = atoi(row[16]); - // Begin SoF Specific/Adjusted AA Fields - sendaa->aa_expansion = atoul(row[17]); - sendaa->special_category = atoul(row[18]); - sendaa->sof_type = atoul(row[19]); - sendaa->sof_cost_inc = atoi(row[20]); - sendaa->sof_max_level = atoul(row[21]); - sendaa->sof_next_skill = atoul(row[22]); - sendaa->clientver = atoul(row[23]); - sendaa->account_time_required = atoul(row[24]); - //Internal use only - not sent to client - sendaa->sof_current_level = atoul(row[25]); - sendaa->sof_next_id = atoul(row[26]); - sendaa->level_inc = static_cast(atoul(row[27])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf); - safe_delete_array(query); - } - } else { - LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf); - safe_delete_array(query); - } return sendaa; } diff --git a/zone/AA.h b/zone/aa.h similarity index 99% rename from zone/AA.h rename to zone/aa.h index 3adc887e3..00f74e8c6 100644 --- a/zone/AA.h +++ b/zone/aa.h @@ -2,7 +2,8 @@ #ifndef AA_H #define AA_H -#include "../common/eq_packet_structs.h" +struct AA_Ability; +struct SendAA_Struct; #define MANA_BURN 664 @@ -2134,7 +2135,7 @@ struct AALevelCost_Struct uint32 Cost; }; -//assumes that no activatable AA has more than 5 ranks +//assumes that no activatable aa.has more than 5 ranks #define MAX_AA_ACTION_RANKS 20 extern AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank] extern std::map AA_SwarmPets; //key=spell_id diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 3c6c306ac..8f5da2eaa 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -23,9 +23,9 @@ #include "map.h" #include "../common/spdat.h" #include "../common/skills.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "../common/rulesys.h" -#include "StringIDs.h" +#include "string_ids.h" #include extern Zone* zone; @@ -873,7 +873,34 @@ bool Mob::CombatRange(Mob* other) if (size_mod > 10000) size_mod = size_mod / 7; - if (DistNoRoot(*other) <= size_mod) + float _DistNoRoot = DistNoRoot(*other); + + if (GetSpecialAbility(NPC_CHASE_DISTANCE)){ + + bool DoLoSCheck = true; + float max_dist = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0)); + float min_dist = static_cast(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1)); + + if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2)) + DoLoSCheck = false; //Ignore line of sight check + + if (max_dist == 1) + max_dist = 250.0f; //Default it to 250 if you forget to put a value + + max_dist = max_dist * max_dist; + + if (!min_dist) + min_dist = size_mod; //Default to melee range + else + min_dist = min_dist * min_dist; + + if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist)) + SetPseudoRoot(true); + else + SetPseudoRoot(false); + } + + if (_DistNoRoot <= size_mod) { return true; } @@ -887,6 +914,8 @@ bool Mob::CheckLosFN(Mob* other) { if(other) Result = CheckLosFN(other->GetX(), other->GetY(), other->GetZ(), other->GetSize()); + SetLastLosState(Result); + return Result; } diff --git a/zone/attack.cpp b/zone/attack.cpp index 034a18fc9..01513fa0d 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -21,28 +21,25 @@ #endif #include "../common/debug.h" -#include -#include -#include -#include -#include -#include - -#include "masterentity.h" -#include "NpcAI.h" -#include "../common/packet_dump.h" -#include "../common/eq_packet_structs.h" #include "../common/eq_constants.h" +#include "../common/eq_packet_structs.h" +#include "../common/rulesys.h" #include "../common/skills.h" #include "../common/spdat.h" -#include "zone.h" -#include "StringIDs.h" -#include "../common/StringUtil.h" -#include "../common/rulesys.h" -#include "QuestParserCollection.h" +#include "../common/string_util.h" +#include "queryserv.h" +#include "quest_parser_collection.h" +#include "string_ids.h" #include "water_map.h" #include "worldserver.h" #include "remote_call_subscribe.h" +#include "zone.h" + +#include +#include +#include + +extern QueryServ* QServ; extern WorldServer worldserver; #ifdef _WINDOWS @@ -449,19 +446,16 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { float bonusShieldBlock = 0.0f; - bonusShieldBlock = aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock; + bonusShieldBlock = static_cast(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock); RollTable[1] += bonusShieldBlock; } - if(damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) + if(IsClient() && damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - bool equiped2 = CastToClient()->m_inv.GetItem(MainPrimary); - if(equiped2) { - uint8 TwoHandBlunt = CastToClient()->m_inv.GetItem(MainPrimary)->GetItem()->ItemType; + if(CastToClient()->m_inv.GetItem(MainPrimary)) { float bonusStaffBlock = 0.0f; - if(TwoHandBlunt == ItemType2HBlunt) { - - bonusStaffBlock = aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock; + if (CastToClient()->m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt){ + bonusStaffBlock = static_cast(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock); RollTable[1] += bonusStaffBlock; } } @@ -666,7 +660,7 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac //////////////////////////////////////////////////////// // Scorpious2k: Include AC in the calculation // use serverop variables to set values - int myac = GetAC(); + int32 myac = GetAC(); if(opts) { myac *= (1.0f - opts->armor_pen_percent); myac -= opts->armor_pen_flat; @@ -697,7 +691,7 @@ void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttac } if (acreduction>0) { - damage -= (int) (GetAC() * acreduction/100.0f); + damage -= (int32) (GetAC() * acreduction/100.0f); } if (acrandom>0) { damage -= (myac * MakeRandomInt(0, acrandom) / 10000); @@ -1286,15 +1280,18 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b mlog(COMBAT__DAMAGE, "Damage calculated to %d (min %d, max %d, str %d, skill %d, DMG %d, lv %d)", damage, min_hit, max_hit, GetSTR(), GetSkill(skillinuse), weapon_damage, mylevel); + int hit_chance_bonus = 0; + if(opts) { damage *= opts->damage_percent; damage += opts->damage_flat; hate *= opts->hate_percent; hate += opts->hate_flat; + hit_chance_bonus += opts->hit_chance; } //check to see if we hit.. - if(!other->CheckHitChance(this, skillinuse, Hand)) { + if(!other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) { mlog(COMBAT__ATTACKS, "Attack missed. Damage set to 0."); damage = 0; } else { //we hit, try to avoid it @@ -1314,7 +1311,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b if (Hand == MainSecondary) {// Do we even have it & was attack with mainhand? If not, don't bother with other calculations //Live AA - SlipperyAttacks //This spell effect most likely directly modifies the actual riposte chance when using offhand attack. - int16 OffhandRiposteFail = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail; + int32 OffhandRiposteFail = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail; OffhandRiposteFail *= -1; //Live uses a negative value for this. if (OffhandRiposteFail && @@ -1332,7 +1329,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b } if (((damage < 0) || slippery_attack) && !bRiposte && !IsStrikethrough) { // Hack to still allow Strikethrough chance w/ Slippery Attacks AA - int16 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; + int32 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; if(bonusStrikeThrough && (MakeRandomInt(0, 100) < bonusStrikeThrough)) { Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! @@ -1360,11 +1357,8 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b MeleeLifeTap(damage); - if (damage > 0){ - CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess); - if (HasSkillProcSuccess() && other && other->GetHP() > 0) - TrySkillProc(other, skillinuse, 0, true, Hand); - } + if (damage > 0 && HasSkillProcSuccess() && other && other->GetHP() > 0) + TrySkillProc(other, skillinuse, 0, true, Hand); CommonBreakInvisible(); @@ -1403,7 +1397,7 @@ void Client::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes att } // cut all PVP spell damage to 2/3 -solar - // EverHood - Blasting ourselfs is considered PvP + // Blasting ourselfs is considered PvP //Don't do PvP mitigation if the caster is damaging himself if(other && other->IsClient() && (other != this) && damage > 0) { int PvPMitigation = 100; @@ -1456,14 +1450,14 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att int exploss = 0; mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); - // - // #1: Send death packet to everyone - // + /* + #1: Send death packet to everyone + */ uint8 killed_level = GetLevel(); SendLogoutPackets(); - //make our become corpse packet, and queue to ourself before OP_Death. + /* Make self become corpse packet */ EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; bc->spawn_id = GetID(); @@ -1472,7 +1466,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att bc->z = GetZ(); QueuePacket(&app2); - // make death packet + /* Make Death Packet */ EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); Death_Struct* d = (Death_Struct*)app.pBuffer; d->spawn_id = GetID(); @@ -1485,9 +1479,9 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att app.priority = 6; entity_list.QueueClients(this, &app); - // - // #2: figure out things that affect the player dying and mark them dead - // + /* + #2: figure out things that affect the player dying and mark them dead + */ InterruptSpell(); SetPet(0); @@ -1542,9 +1536,9 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att //remove ourself from all proximities ClearAllProximities(); - // - // #3: exp loss and corpse generation - // + /* + #3: exp loss and corpse generation + */ // figure out if they should lose exp if(RuleB(Character, UseDeathExpLossMult)){ @@ -1629,14 +1623,14 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att database.GetVariable("PvPitem", tmp2, 9); int pvpitem = atoi(tmp2); if(pvpitem>0 && pvpitem<200000) - new_corpse->SetPKItem(pvpitem); + new_corpse->SetPlayerKillItemID(pvpitem); } else if(reward==2) - new_corpse->SetPKItem(-1); + new_corpse->SetPlayerKillItemID(-1); else if(reward==1) - new_corpse->SetPKItem(1); + new_corpse->SetPlayerKillItemID(1); else - new_corpse->SetPKItem(0); + new_corpse->SetPlayerKillItemID(0); if(killerMob->CastToClient()->isgrouped) { Group* group = entity_list.GetGroupByClient(killerMob->CastToClient()); if(group != 0) @@ -1645,7 +1639,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att { if(group->members[i] != nullptr) { - new_corpse->AllowMobLoot(group->members[i],i); + new_corpse->AllowPlayerLoot(group->members[i],i); } } } @@ -1660,27 +1654,21 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att LeftCorpse = true; } - -// if(!IsLD())//Todo: make it so an LDed client leaves corpse if its enabled -// MakeCorpse(exploss); } else { BuffFadeDetrimental(); } - // - // Finally, send em home - // + /* + Finally, send em home - // we change the mob variables, not pp directly, because Save() will copy - // from these and overwrite what we set in pp anyway - // + We change the mob variables, not pp directly, because Save() will copy + from these and overwrite what we set in pp anyway + */ if(LeftCorpse && (GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) { - ClearDraggedCorpses(); - - RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); - + ClearDraggedCorpses(); + RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); SendRespawnBinds(); } else @@ -1697,17 +1685,22 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att if(r) r->MemberZoned(this); - dead_timer.Start(5000, true); - + dead_timer.Start(5000, true); m_pp.zone_id = m_pp.binds[0].zoneId; - m_pp.zoneInstance = 0; - database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id)); - - Save(); - + m_pp.zoneInstance = m_pp.binds[0].instance_id; + database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id)); + Save(); GoToDeath(); } + /* QS: PlayerLogDeaths */ + if (RuleB(QueryServ, PlayerLogDeaths)){ + const char * killer_name = ""; + if (killerMob && killerMob->GetCleanName()){ killer_name = killerMob->GetCleanName(); } + std::string event_desc = StringFormat("Died in zoneid:%i instid:%i by '%s', spellid:%i, damage:%i", this->GetZoneID(), this->GetInstanceID(), killer_name, spell, damage); + QServ->PlayerLogEvent(Player_Log_Deaths, this->CharacterID(), event_desc); + } + parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0); return true; } @@ -1820,7 +1813,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool //ele and bane dmg too //NPCs add this differently than PCs //if NPCs can't inheriently hit the target we don't add bane/magic dmg which isn't exactly the same as PCs - uint16 eleBane = 0; + uint32 eleBane = 0; if(weapon){ if(weapon->BaneDmgBody == other->GetBodyType()){ eleBane += weapon->BaneDmgAmt; @@ -1889,14 +1882,18 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool other->AddToHateList(this, hate); } else { + + int hit_chance_bonus = 0; + if(opts) { damage *= opts->damage_percent; damage += opts->damage_flat; hate *= opts->hate_percent; hate += opts->hate_flat; + hit_chance_bonus += opts->hit_chance; } - if(!other->CheckHitChance(this, skillinuse, Hand)) { + if(!other->CheckHitChance(this, skillinuse, Hand, hit_chance_bonus)) { damage = 0; //miss } else { //hit, check for damage avoidance other->AvoidDamage(this, damage); @@ -2058,6 +2055,13 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack } SetHP(0); SetPet(0); + + if (GetSwarmOwner()){ + Mob* owner = entity_list.GetMobID(GetSwarmOwner()); + if (owner) + owner->SetTempPetCount(owner->GetTempPetCount() - 1); + } + Mob* killer = GetHateDamageTop(this); entity_list.RemoveFromTargets(this, p_depop); @@ -2120,7 +2124,7 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack give_exp_client = give_exp->CastToClient(); bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); - if (give_exp_client && !IsCorpse() && MerchantType == 0) + if (give_exp_client && !IsCorpse()) { Group *kg = entity_list.GetGroupByClient(give_exp_client); Raid *kr = entity_list.GetRaidByClient(give_exp_client); @@ -2130,15 +2134,19 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack if(kr) { - if(!IsLdonTreasure) { + if(!IsLdonTreasure && MerchantType == 0) { kr->SplitExp((finalxp), this); if(killerMob && (kr->IsRaidMember(killerMob->GetName()) || kr->IsRaidMember(killerMob->GetUltimateOwner()->GetName()))) killerMob->TrySpellOnKill(killed_level,spell); } /* Send the EVENT_KILLED_MERIT event for all raid members */ for (int i = 0; i < MAX_RAID_MEMBERS; i++) { - if (kr->members[i].member != nullptr) { // If Group Member is Client - parse->EventNPC(EVENT_KILLED_MERIT, this, kr->members[i].member, "killed", 0); + if (kr->members[i].member != nullptr && kr->members[i].member->IsClient()) { // If Group Member is Client + Client *c = kr->members[i].member; + parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); + + if(RuleB(NPC, EnableMeritBasedFaction)) + c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity()); mod_npc_killed_merit(kr->members[i].member); @@ -2157,7 +2165,7 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack QS->s1.ZoneID = this->GetZoneID(); QS->s1.Type = 2; // Raid Fight for (int i = 0; i < MAX_RAID_MEMBERS; i++) { - if (kr->members[i].member != nullptr) { // If Group Member is Client + if (kr->members[i].member != nullptr && kr->members[i].member->IsClient()) { // If Group Member is Client Client *c = kr->members[i].member; QS->Chars[PlayerCount].char_id = c->CharacterID(); PlayerCount++; @@ -2171,7 +2179,7 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack } else if (give_exp_client->IsGrouped() && kg != nullptr) { - if(!IsLdonTreasure) { + if(!IsLdonTreasure && MerchantType == 0) { kg->SplitExp((finalxp), this); if(killerMob && (kg->IsGroupMember(killerMob->GetName()) || kg->IsGroupMember(killerMob->GetUltimateOwner()->GetName()))) killerMob->TrySpellOnKill(killed_level,spell); @@ -2183,6 +2191,9 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack Client *c = kg->members[i]->CastToClient(); parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); + if(RuleB(NPC, EnableMeritBasedFaction)) + c->SetFactionLevel(c->CharacterID(), GetNPCFactionID(), c->GetBaseClass(), c->GetBaseRace(), c->GetDeity()); + mod_npc_killed_merit(c); if(RuleB(TaskSystem, EnableTaskSystem)) @@ -2214,14 +2225,13 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack } else { - if(!IsLdonTreasure) { + if(!IsLdonTreasure && MerchantType == 0) { int conlevel = give_exp->GetLevelCon(GetLevel()); if (conlevel != CON_GREEN) { - if(GetOwner() && GetOwner()->IsClient()){ - } - else { - give_exp_client->AddEXP((finalxp), conlevel); // Pyro: Comment this if NPC death crashes zone + if(!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) + { + give_exp_client->AddEXP((finalxp), conlevel); if(killerMob && (killerMob->GetID() == give_exp_client->GetID() || killerMob->GetUltimateOwner()->GetID() == give_exp_client->GetID())) killerMob->TrySpellOnKill(killed_level,spell); } @@ -2230,6 +2240,10 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack /* Send the EVENT_KILLED_MERIT event */ parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0); + if(RuleB(NPC, EnableMeritBasedFaction)) + give_exp_client->SetFactionLevel(give_exp_client->CharacterID(), GetNPCFactionID(), give_exp_client->GetBaseClass(), + give_exp_client->GetBaseRace(), give_exp_client->GetDeity()); + mod_npc_killed_merit(give_exp_client); if(RuleB(TaskSystem, EnableTaskSystem)) @@ -2253,7 +2267,7 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack } //do faction hits even if we are a merchant, so long as a player killed us - if(give_exp_client) + if(give_exp_client && !RuleB(NPC, EnableMeritBasedFaction)) hate_list.DoFactionHits(GetNPCFactionID()); if (!HasOwner() && !IsMerc() && class_ != MERCHANT && class_ != ADVENTUREMERCHANT && !GetSwarmInfo() @@ -2281,13 +2295,13 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack if(killer != 0 && emoteid != 0) corpse->CastToNPC()->DoNPCEmote(AFTERDEATH, emoteid); if(killer != 0 && killer->IsClient()) { - corpse->AllowMobLoot(killer, 0); + corpse->AllowPlayerLoot(killer, 0); if(killer->IsGrouped()) { Group* group = entity_list.GetGroupByClient(killer->CastToClient()); if(group != 0) { for(int i=0;i<6;i++) { // Doesnt work right, needs work if(group->members[i] != nullptr) { - corpse->AllowMobLoot(group->members[i],i); + corpse->AllowPlayerLoot(group->members[i],i); } } } @@ -2303,30 +2317,30 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack case 0: case 1: if(r->members[x].member && r->members[x].IsRaidLeader){ - corpse->AllowMobLoot(r->members[x].member, i); + corpse->AllowPlayerLoot(r->members[x].member, i); i++; } break; case 2: if(r->members[x].member && r->members[x].IsRaidLeader){ - corpse->AllowMobLoot(r->members[x].member, i); + corpse->AllowPlayerLoot(r->members[x].member, i); i++; } else if(r->members[x].member && r->members[x].IsGroupLeader){ - corpse->AllowMobLoot(r->members[x].member, i); + corpse->AllowPlayerLoot(r->members[x].member, i); i++; } break; case 3: if(r->members[x].member && r->members[x].IsLooter){ - corpse->AllowMobLoot(r->members[x].member, i); + corpse->AllowPlayerLoot(r->members[x].member, i); i++; } break; case 4: if(r->members[x].member) { - corpse->AllowMobLoot(r->members[x].member, i); + corpse->AllowPlayerLoot(r->members[x].member, i); i++; } break; @@ -2420,7 +2434,7 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, AddRampage(other); int hatemod = 100 + other->spellbonuses.hatemod + other->itembonuses.hatemod + other->aabonuses.hatemod; - int16 shieldhatemod = other->spellbonuses.ShieldEquipHateMod + other->itembonuses.ShieldEquipHateMod + other->aabonuses.ShieldEquipHateMod; + int32 shieldhatemod = other->spellbonuses.ShieldEquipHateMod + other->itembonuses.ShieldEquipHateMod + other->aabonuses.ShieldEquipHateMod; if (shieldhatemod && other->HasShieldEquiped()) hatemod += shieldhatemod; @@ -2542,6 +2556,10 @@ void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, if (myowner->IsAIControlled() && !myowner->GetSpecialAbility(IMMUNE_AGGRO)) myowner->hate_list.Add(other, 0, 0, bFrenzy); } + + if (other->GetTempPetCount()) + entity_list.AddTempPetsToHateList(other, this, bFrenzy); + if (!wasengaged) { if(IsNPC() && other->IsClient() && other->CastToClient()) parse->EventNPC(EVENT_AGGRO, this->CastToNPC(), other, "", 0); @@ -2596,7 +2614,7 @@ void Mob::DamageShield(Mob* attacker, bool spell_ds) { //Spell data for damage shield mitigation shows a negative value for spells for clients and positive //value for spells that effect pets. Unclear as to why. For now will convert all positive to be consistent. if (attacker->IsOffHandAtk()){ - int16 mitigation = attacker->itembonuses.DSMitigationOffHand + + int32 mitigation = attacker->itembonuses.DSMitigationOffHand + attacker->spellbonuses.DSMitigationOffHand + attacker->aabonuses.DSMitigationOffHand; DS -= DS*mitigation/100; @@ -3326,7 +3344,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); @@ -3342,10 +3363,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); @@ -3399,7 +3423,7 @@ bool Mob::HasRangedProcs() const bool Client::CheckDoubleAttack(bool tripleAttack) { //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) - uint16 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; + uint32 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; if(!HasSkill(SkillDoubleAttack) && !bonusGiveDA) return false; @@ -3408,7 +3432,7 @@ bool Client::CheckDoubleAttack(bool tripleAttack) { uint16 skill = GetSkill(SkillDoubleAttack); - int16 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; + int32 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. if (skill) @@ -3422,7 +3446,7 @@ bool Client::CheckDoubleAttack(bool tripleAttack) { //Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect. if(tripleAttack) { // Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp] - int16 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; + int32 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; chance *= 0.2f; //Baseline chance is 20% of your double attack chance. chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers. } @@ -3435,7 +3459,7 @@ bool Client::CheckDoubleAttack(bool tripleAttack) { bool Client::CheckDoubleRangedAttack() { - int16 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; + int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; if(chance && (MakeRandomInt(0, 100) < chance)) return true; @@ -3527,7 +3551,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons if(spell_id == SPELL_UNKNOWN) { damage = ReduceDamage(damage); mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage); - ReduceAllDamage(damage); + damage = ReduceAllDamage(damage); TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker); } else { int32 origdmg = damage; @@ -3540,7 +3564,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons //Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes. Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName()); } - ReduceAllDamage(damage); + damage = ReduceAllDamage(damage); TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker); } @@ -3582,12 +3606,12 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons TryTriggerOnValueAmount(true); //fade mez if we are mezzed - if (IsMezzed()) { + if (IsMezzed() && attacker) { mlog(COMBAT__HITS, "Breaking mez due to attack."); entity_list.MessageClose_StringID(this, true, 100, MT_WornOff, HAS_BEEN_AWAKENED, GetCleanName(), attacker->GetCleanName()); BuffFadeByEffect(SE_Mez); - } + } //check stun chances if bashing if (damage > 0 && ((skill_used == SkillBash || skill_used == SkillKick) && attacker)) { @@ -3775,7 +3799,7 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons safe_delete(outapp); } else { //else, it is a buff tic... - // Everhood - So we can see our dot dmg like live shows it. + // So we can see our dot dmg like live shows it. if(spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && attacker->IsClient()) { //might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it attacker->FilteredMessage_StringID(attacker, MT_DoTDamage, FilterDOT, @@ -3867,7 +3891,7 @@ float Mob::GetProcChances(float ProcBonus, uint16 hand) int mydex = GetDEX(); float ProcChance = 0.0f; - uint16 weapon_speed = GetWeaponSpeedbyHand(hand); + uint32 weapon_speed = GetWeaponSpeedbyHand(hand); if (RuleB(Combat, AdjustProcPerMinute)) { ProcChance = (static_cast(weapon_speed) * @@ -3893,7 +3917,7 @@ float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 h ProcBonus = 0; ProcChance = 0; - uint16 weapon_speed = GetWeaponSpeedbyHand(hand); + uint32 weapon_speed = GetWeaponSpeedbyHand(hand); ProcChance = (static_cast(weapon_speed) * RuleR(Combat, AvgDefProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms ProcBonus += static_cast(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f; @@ -4148,7 +4172,7 @@ void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) Mob *owner = nullptr; float critChance = 0.0f; critChance += RuleI(Combat, MeleeBaseCritChance); - uint16 critMod = 163; + uint32 critMod = 163; if (damage < 1) //We can't critical hit if we don't hit. return; @@ -4163,8 +4187,8 @@ void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) if (!owner) return; - int16 CritPetChance = owner->aabonuses.PetCriticalHit + owner->itembonuses.PetCriticalHit + owner->spellbonuses.PetCriticalHit; - int16 CritChanceBonus = GetCriticalChanceBonus(skill); + int32 CritPetChance = owner->aabonuses.PetCriticalHit + owner->itembonuses.PetCriticalHit + owner->spellbonuses.PetCriticalHit; + int32 CritChanceBonus = GetCriticalChanceBonus(skill); if (CritPetChance || critChance) { @@ -4208,24 +4232,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){ - - int16 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0]; - + if (defender && (defender->GetBodyType() == BT_Undead || + defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire)) { + int32 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){ - 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); + float slayChance = static_cast(SlayRateBonus) / 10000.0f; + if (MakeRandomFloat(0, 1) < slayChance) { + int32 SlayDmgBonus = aabonuses.SlayUndead[1] + itembonuses.SlayUndead[1] + spellbonuses.SlayUndead[1]; + 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; } } @@ -4288,9 +4314,9 @@ void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttack if(MakeRandomFloat(0, 1) < critChance) { - uint16 critMod = 200; + uint32 critMod = 200; bool crip_success = false; - int16 CripplingBlowChance = GetCrippBlowChance(); + int32 CripplingBlowChance = GetCrippBlowChance(); //Crippling Blow Chance: The percent value of the effect is applied //to the your Chance to Critical. (ie You have 10% chance to critical and you @@ -4346,7 +4372,7 @@ bool Mob::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse) uint32 FB_Dmg = aabonuses.FinishingBlow[1] + spellbonuses.FinishingBlow[1] + itembonuses.FinishingBlow[1]; - uint16 FB_Level = 0; + uint32 FB_Level = 0; FB_Level = aabonuses.FinishingBlowLvl[0]; if (FB_Level < spellbonuses.FinishingBlowLvl[0]) FB_Level = spellbonuses.FinishingBlowLvl[0]; @@ -4374,7 +4400,7 @@ void Mob::DoRiposte(Mob* defender) { defender->Attack(this, MainPrimary, true); if (HasDied()) return; - int16 DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] + + int32 DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] + defender->spellbonuses.GiveDoubleRiposte[0] + defender->itembonuses.GiveDoubleRiposte[0]; @@ -4422,7 +4448,7 @@ void Mob::ApplyMeleeDamageBonus(uint16 skill, int32 &damage){ bool Mob::HasDied() { bool Result = false; - int16 hp_below = 0; + int32 hp_below = 0; hp_below = (GetDelayDeath() * -1); @@ -4436,7 +4462,7 @@ uint16 Mob::GetDamageTable(SkillUseTypes skillinuse) { if(GetLevel() <= 51) { - uint16 ret_table = 0; + uint32 ret_table = 0; int str_over_75 = 0; if(GetSTR() > 75) str_over_75 = GetSTR() - 75; @@ -4459,7 +4485,7 @@ uint16 Mob::GetDamageTable(SkillUseTypes skillinuse) } else { - uint16 dmg_table[] = { + uint32 dmg_table[] = { 275, 275, 275, 275, 275, 280, 280, 280, 280, 285, 285, 285, 290, 290, 295, @@ -4636,7 +4662,7 @@ void Mob::TrySkillProc(Mob *on, uint16 skill, uint16 ReuseTime, bool Success, ui float Mob::GetSkillProcChances(uint16 ReuseTime, uint16 hand) { - uint16 weapon_speed; + uint32 weapon_speed; float ProcChance = 0; if (!ReuseTime && hand) { @@ -4713,7 +4739,7 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) if (type == SE_Rune){ for(uint32 slot = 0; slot < buff_max; slot++) { if(slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)){ - uint32 melee_rune_left = buffs[slot].melee_rune; + int melee_rune_left = buffs[slot].melee_rune; if(melee_rune_left > damage) { @@ -4738,7 +4764,7 @@ int32 Mob::RuneAbsorb(int32 damage, uint16 type) else{ for(uint32 slot = 0; slot < buff_max; slot++) { if(slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)){ - uint32 magic_rune_left = buffs[slot].magic_rune; + int magic_rune_left = buffs[slot].magic_rune; if(magic_rune_left > damage) { magic_rune_left -= damage; @@ -4814,3 +4840,171 @@ void Mob::CommonBreakInvisible() hidden = false; 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); +} + +void Client::SetAttackTimer() +{ + 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; + const Item_Struct *PrimaryWeapon = nullptr; + + for (int i = MainRange; i <= MainSecondary; i++) { + //pick a timer + if (i == MainPrimary) + TimerToUse = &attack_timer; + else if (i == MainRange) + TimerToUse = &ranged_timer; + else if (i == MainSecondary) + TimerToUse = &attack_dw_timer; + else //invalid slot (hands will always hit this) + continue; + + const Item_Struct *ItemToUse = nullptr; + + //find our item + ItemInst *ci = GetInv().GetItem(i); + if (ci) + ItemToUse = ci->GetItem(); + + //special offhand stuff + if (i == MainSecondary) { + //if we have a 2H weapon in our main hand, no dual + if (PrimaryWeapon != nullptr) { + if (PrimaryWeapon->ItemClass == ItemClassCommon + && (PrimaryWeapon->ItemType == ItemType2HSlash + || PrimaryWeapon->ItemType == ItemType2HBlunt + || PrimaryWeapon->ItemType == ItemType2HPiercing)) { + attack_dw_timer.Disable(); + continue; + } + } + + //if we cant dual wield, skip it + if (!CanThisClassDualWield()) { + attack_dw_timer.Disable(); + continue; + } + } + + //see if we have a valid weapon + if (ItemToUse != nullptr) { + //check type and damage/delay + if (ItemToUse->ItemClass != ItemClassCommon + || ItemToUse->Damage == 0 + || ItemToUse->Delay == 0) { + //no weapon + ItemToUse = nullptr; + } + // Check to see if skill is valid + else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) && + (ItemToUse->ItemType != ItemTypeMartial) && + (ItemToUse->ItemType != ItemType2HPiercing)) { + //no weapon + ItemToUse = nullptr; + } + } + + 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) + delay = GetMonkHandToHandDelay(); + } else { + //we have a weapon, use its delay + 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) + PrimaryWeapon = ItemToUse; + } +} + +void NPC::SetAttackTimer() +{ + 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 + if (i == MainPrimary) + TimerToUse = &attack_timer; + else if (i == MainRange) + TimerToUse = &ranged_timer; + else if (i == MainSecondary) + TimerToUse = &attack_dw_timer; + else //invalid slot (hands will always hit this) + continue; + + //special offhand stuff + if (i == MainSecondary) { + //NPCs get it for free at 13 + if(GetLevel() < 13) { + attack_dw_timer.Disable(); + continue; + } + } + + TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true); + } +} diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 8ccc8d64e..7944c8ba6 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -20,13 +20,13 @@ #include "masterentity.h" #include "../common/packet_dump.h" #include "../common/moremath.h" -#include "../common/Item.h" +#include "../common/item.h" #include "worldserver.h" #include "../common/skills.h" #include "../common/bodytypes.h" #include "../common/classes.h" #include "../common/rulesys.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include #include #include @@ -35,7 +35,7 @@ #include "../common/unix.h" #endif -#include "StringIDs.h" +#include "string_ids.h" void Mob::CalcBonuses() { @@ -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) { @@ -306,7 +306,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu } //FatherNitwit: New style haste, shields, and regens - if(newbon->haste < (int16)item->Haste) { + if(newbon->haste < (int32)item->Haste) { newbon->haste = item->Haste; } if(item->Regen > 0) @@ -1392,7 +1392,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon) newbon->AggroRange = -1; newbon->AssistRange = -1; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid != SPELL_UNKNOWN){ ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false, buffs[i].ticsremaining,i); @@ -1477,19 +1477,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne // redundant to have level check here if(newbon->AggroRange == -1 || effect_value < newbon->AggroRange) { - newbon->AggroRange = effect_value; + newbon->AggroRange = static_cast(effect_value); } break; } case SE_Harmony: { - // neotokyo: Harmony effect as buff - kinda tricky + // Harmony effect as buff - kinda tricky // harmony could stack with a lull spell, which has better aggro range // take the one with less range in any case if(newbon->AssistRange == -1 || effect_value < newbon->AssistRange) { - newbon->AssistRange = effect_value; + newbon->AssistRange = static_cast(effect_value); } break; } @@ -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; @@ -2799,7 +2808,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne || ((effect_value < 0) && (newbon->AlterNPCLevel > effect_value)) || ((effect_value > 0) && (newbon->AlterNPCLevel < effect_value))) { - int16 tmp_lv = GetOrigLevel() + effect_value; + int tmp_lv = GetOrigLevel() + effect_value; if (tmp_lv < 1) tmp_lv = 1; else if (tmp_lv > 255) @@ -3083,7 +3092,7 @@ void Client::CalcItemScale() { bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y) { // behavior change: 'slot_y' is now [RANGE]_END and not [RANGE]_END + 1 bool changed = false; - int i; + uint32 i; for (i = slot_x; i <= slot_y; i++) { if (i == MainAmmo) // moved here from calling procedure to facilitate future range changes where MainAmmo may not be the last slot continue; @@ -3177,7 +3186,7 @@ void Client::DoItemEnterZone() { bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) { // behavior change: 'slot_y' is now [RANGE]_END and not [RANGE]_END + 1 bool changed = false; - for(int i = slot_x; i <= slot_y; i++) { + for(uint32 i = slot_x; i <= slot_y; i++) { if (i == MainAmmo) // moved here from calling procedure to facilitate future range changes where MainAmmo may not be the last slot continue; @@ -3371,15 +3380,15 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_ChangeFrenzyRad: - spellbonuses.AggroRange = effect_value; - aabonuses.AggroRange = effect_value; - itembonuses.AggroRange = effect_value; + spellbonuses.AggroRange = static_cast(effect_value); + aabonuses.AggroRange = static_cast(effect_value); + itembonuses.AggroRange = static_cast(effect_value); break; case SE_Harmony: - spellbonuses.AssistRange = effect_value; - aabonuses.AssistRange = effect_value; - itembonuses.AssistRange = effect_value; + spellbonuses.AssistRange = static_cast(effect_value); + aabonuses.AssistRange = static_cast(effect_value); + itembonuses.AssistRange = static_cast(effect_value); break; case SE_AttackSpeed: @@ -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; @@ -4334,11 +4347,11 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_TriggerMeleeThreshold: - spellbonuses.TriggerMeleeThreshold = effect_value; + spellbonuses.TriggerMeleeThreshold = false; break; case SE_TriggerSpellThreshold: - spellbonuses.TriggerSpellThreshold = effect_value; + spellbonuses.TriggerSpellThreshold = false; break; case SE_DivineAura: @@ -4364,7 +4377,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_DistanceRemoval: - spellbonuses.DistanceRemoval = effect_value; + spellbonuses.DistanceRemoval = false; break; case SE_ImprovedTaunt: @@ -4467,7 +4480,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_Sanctuary: - spellbonuses.Sanctuary = effect_value; + spellbonuses.Sanctuary = false; break; case SE_FactionModPct: @@ -4483,10 +4496,28 @@ void Mob::NegateSpellsBonuses(uint16 spell_id) break; case SE_IllusionPersistence: - spellbonuses.IllusionPersistence = effect_value; - itembonuses.IllusionPersistence = effect_value; - aabonuses.IllusionPersistence = effect_value; + spellbonuses.IllusionPersistence = false; + itembonuses.IllusionPersistence = false; + aabonuses.IllusionPersistence = false; break; + + case SE_SkillProcSuccess:{ + for(int e = 0; e < MAX_SKILL_PROCS; e++) + { + spellbonuses.SkillProcSuccess[e] = effect_value; + itembonuses.SkillProcSuccess[e] = effect_value; + aabonuses.SkillProcSuccess[e] = effect_value; + } + } + + case SE_SkillProc:{ + for(int e = 0; e < MAX_SKILL_PROCS; e++) + { + spellbonuses.SkillProc[e] = effect_value; + itembonuses.SkillProc[e] = effect_value; + aabonuses.SkillProc[e] = effect_value; + } + } } } } diff --git a/zone/bot.cpp b/zone/bot.cpp index aa5ab33f0..496ddc7ae 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3,8 +3,8 @@ #include "bot.h" #include "object.h" #include "doors.h" -#include "QuestParserCollection.h" -#include "../common/StringUtil.h" +#include "quest_parser_collection.h" +#include "../common/string_util.h" extern volatile bool ZoneLoaded; @@ -338,7 +338,7 @@ bool Bot::IsStanding() { return result; } -NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int16 mr, int16 cr, int16 dr, int16 fr, int16 pr, int16 corrup, int16 ac, uint16 str, uint16 sta, uint16 dex, uint16 agi, uint16 _int, uint16 wis, uint16 cha, uint16 attack) { +NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack) { NPCType BotNPCType; int CopyLength = 0; @@ -459,20 +459,20 @@ void Bot::GenerateBaseStats() { int BotSpellID = 0; // base stats - uint16 Strength = _baseSTR; - uint16 Stamina = _baseSTA; - uint16 Dexterity = _baseDEX; - uint16 Agility = _baseAGI; - uint16 Wisdom = _baseWIS; - uint16 Intelligence = _baseINT; - uint16 Charisma = _baseCHA; - uint16 Attack = _baseATK; - int16 MagicResist = _baseMR; - int16 FireResist = _baseFR; - int16 DiseaseResist = _baseDR; - int16 PoisonResist = _basePR; - int16 ColdResist = _baseCR; - int16 CorruptionResist = _baseCorrup; + uint32 Strength = _baseSTR; + uint32 Stamina = _baseSTA; + uint32 Dexterity = _baseDEX; + uint32 Agility = _baseAGI; + uint32 Wisdom = _baseWIS; + uint32 Intelligence = _baseINT; + uint32 Charisma = _baseCHA; + uint32 Attack = _baseATK; + int32 MagicResist = _baseMR; + int32 FireResist = _baseFR; + int32 DiseaseResist = _baseDR; + int32 PoisonResist = _basePR; + int32 ColdResist = _baseCR; + int32 CorruptionResist = _baseCorrup; switch(this->GetClass()) { case 1: // Warrior (why not just use 'case WARRIOR:'?) @@ -845,7 +845,7 @@ void Bot::GenerateAppearance() { } -int16 Bot::acmod() +int32 Bot::acmod() { int agility = GetAGI(); int level = GetLevel(); @@ -1352,10 +1352,10 @@ uint16 Bot::MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const { return(database.GetSkillCap(class_, skillid, level)); } -uint16 Bot::GetTotalATK() +uint32 Bot::GetTotalATK() { - uint16 AttackRating = 0; - uint16 WornCap = itembonuses.ATK; + uint32 AttackRating = 0; + uint32 WornCap = itembonuses.ATK; if(IsBot()) { AttackRating = ((WornCap * 1.342) + (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69)); @@ -1372,9 +1372,9 @@ uint16 Bot::GetTotalATK() return AttackRating; } -uint16 Bot::GetATKRating() +uint32 Bot::GetATKRating() { - uint16 AttackRating = 0; + uint32 AttackRating = 0; if(IsBot()) { AttackRating = (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69); @@ -1388,9 +1388,9 @@ int32 Bot::GenerateBaseHitPoints() { // Calc Base Hit Points int new_base_hp = 0; - uint16 lm = GetClassLevelFactor(); - uint16 Post255; - uint16 NormalSTA = GetSTA(); + uint32 lm = GetClassLevelFactor(); + uint32 Post255; + uint32 NormalSTA = GetSTA(); if(GetOwner() && GetOwner()->CastToClient() && GetOwner()->CastToClient()->GetClientVersion() >= EQClientSoD && RuleB(Character, SoDClientUseSoDHPManaEnd)) { @@ -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() { @@ -2618,8 +2566,8 @@ void Bot::LoadPet() { if(PetSaveId > 0 && !GetPet() && PetSaveId <= SPDAT_RECORDS) { std::string petName; - uint16 petMana = 0; - uint16 petHitPoints = 0; + uint32 petMana = 0; + uint32 petHitPoints = 0; uint32 botPetId = 0; LoadPetStats(&petName, &petMana, &petHitPoints, &botPetId, PetSaveId); @@ -2645,136 +2593,81 @@ 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; +void Bot::LoadPetStats(std::string* petName, uint32* petMana, uint32* petHitPoints, uint32* botPetId, uint32 botPetSaveId) { + 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() { @@ -2813,247 +2706,166 @@ void Bot::SavePet() { } } -uint32 Bot::SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoints, uint32 botPetId) { - uint32 Result = 0; +uint32 Bot::SavePetStats(std::string petName, uint32 petMana, uint32 petHitPoints, uint32 botPetId) { - 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() @@ -3256,7 +3068,7 @@ void Bot::BotRangedAttack(Mob* other) { bool Bot::CheckBotDoubleAttack(bool tripleAttack) { //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) - uint16 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; + uint32 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; // If you don't have the double attack skill, return if(!GetSkill(SkillDoubleAttack) && !(GetClass() == BARD || GetClass() == BEASTLORD)) @@ -3267,7 +3079,7 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) { uint16 skill = GetSkill(SkillDoubleAttack); - int16 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; + int32 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. if (skill) @@ -3281,7 +3093,7 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) { //Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect. if(tripleAttack) { // Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp] - int16 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; + int32 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; chance *= 0.2f; //Baseline chance is 20% of your double attack chance. chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers. } @@ -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); } @@ -3800,7 +3612,7 @@ void Bot::AI_Process() { } //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; + int32 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; if (GetTarget() && flurrychance) { @@ -3812,7 +3624,7 @@ void Bot::AI_Process() { } } - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; + int32 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; if (GetTarget() && ExtraAttackChanceBonus) { ItemInst *wpn = GetBotItem(MainPrimary); @@ -3861,9 +3673,9 @@ void Bot::AI_Process() { if(bIsFist || ((weapontype != ItemType2HSlash) && (weapontype != ItemType2HPiercing) && (weapontype != ItemType2HBlunt))) { float DualWieldProbability = 0.0f; - int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; + int32 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max - int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; + int32 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; float random = MakeRandomFloat(0, 1); @@ -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) { @@ -6590,7 +6254,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b if (Hand == MainSecondary) {// Do we even have it & was attack with mainhand? If not, don't bother with other calculations //Live AA - SlipperyAttacks //This spell effect most likely directly modifies the actual riposte chance when using offhand attack. - int16 OffhandRiposteFail = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail; + int32 OffhandRiposteFail = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail; OffhandRiposteFail *= -1; //Live uses a negative value for this. if (OffhandRiposteFail && @@ -6608,7 +6272,7 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b } if (((damage < 0) || slippery_attack) && !FromRiposte && !IsStrikethrough) { // Hack to still allow Strikethrough chance w/ Slippery Attacks AA - int16 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; + int32 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; if(bonusStrikeThrough && (MakeRandomInt(0, 100) < bonusStrikeThrough)) { Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! @@ -6683,11 +6347,11 @@ bool Bot::Attack(Mob* other, int Hand, bool FromRiposte, bool IsStrikethrough, b return false; } -int16 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) +int32 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) { const SPDat_Spell_Struct &spell = spells[spell_id]; - int16 value = 0; + int32 value = 0; int lvlModifier = 100; int spell_level = 0; int lvldiff = 0; @@ -7101,13 +6765,13 @@ int16 Bot::CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id) return(value*lvlModifier/100); } -int16 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { +int32 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { if (IsBardSong(spell_id) && bottype != BotfocusFcBaseEffects) return 0; - int16 realTotal = 0; - int16 realTotal2 = 0; - int16 realTotal3 = 0; + int32 realTotal = 0; + int32 realTotal2 = 0; + int32 realTotal3 = 0; bool rand_effectiveness = false; //Improved Healing, Damage & Mana Reduction are handled differently in that some are random percentages @@ -7125,9 +6789,9 @@ int16 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { const Item_Struct* UsedItem = 0; const ItemInst* TempInst = 0; uint16 UsedFocusID = 0; - int16 Total = 0; - int16 focus_max = 0; - int16 focus_max_real = 0; + int32 Total = 0; + int32 focus_max = 0; + int32 focus_max_real = 0; //item focus for(int x = EmuConstants::EQUIPMENT_BEGIN; x <= EmuConstants::EQUIPMENT_END; x++) @@ -7209,14 +6873,14 @@ int16 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { if (spellbonuses.FocusEffects[bottype]){ //Spell Focus - int16 Total2 = 0; - int16 focus_max2 = 0; - int16 focus_max_real2 = 0; + int32 Total2 = 0; + int32 focus_max2 = 0; + int32 focus_max_real2 = 0; int buff_tracker = -1; int buff_slot = 0; - uint16 focusspellid = 0; - uint16 focusspell_tracker = 0; + uint32 focusspellid = 0; + uint32 focusspell_tracker = 0; uint32 buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { focusspellid = buffs[buff_slot].spellid; @@ -7262,7 +6926,7 @@ int16 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { if (aabonuses.FocusEffects[bottype]){ int totalAAs = database.CountAAs(); - int16 Total3 = 0; + int32 Total3 = 0; uint32 slots = 0; uint32 aa_AA = 0; uint32 aa_value = 0; @@ -7298,14 +6962,14 @@ int16 Bot::GetBotFocusEffect(BotfocusType bottype, uint16 spell_id) { return realTotal + realTotal2; } -int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { +int32 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus) { if(!IsValidSpell(focus_id) || !IsValidSpell(spell_id)) return 0; const SPDat_Spell_Struct &focus_spell = spells[focus_id]; const SPDat_Spell_Struct &spell = spells[spell_id]; - int16 value = 0; + int32 value = 0; int lvlModifier = 100; int spell_level = 0; int lvldiff = 0; @@ -7361,7 +7025,7 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel break; case SE_LimitCastTimeMin: - if (spells[spell_id].cast_time < (uint16)focus_spell.base[i]) + if (spells[spell_id].cast_time < (uint32)focus_spell.base[i]) return(0); break; @@ -7739,7 +7403,7 @@ int16 Bot::CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spel float Bot::GetProcChances(float ProcBonus, uint16 hand) { int mydex = GetDEX(); float ProcChance = 0.0f; - uint16 weapon_speed = 0; + uint32 weapon_speed = 0; switch (hand) { case MainPrimary: weapon_speed = attack_timer.GetDuration(); @@ -8022,7 +7686,7 @@ void Bot::DoRiposte(Mob* defender) { defender->Attack(this, MainPrimary, true); //double riposte - int16 DoubleRipChance = defender->GetAABonuses().GiveDoubleRiposte[0] + + int32 DoubleRipChance = defender->GetAABonuses().GiveDoubleRiposte[0] + defender->GetSpellBonuses().GiveDoubleRiposte[0] + defender->GetItemBonuses().GiveDoubleRiposte[0]; @@ -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,13 +8013,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if(!ca_time) return; - float HasteModifier = 0; - if(GetHaste() >= 0){ - HasteModifier = 10000 / (100 + GetHaste()); - } - else { - HasteModifier = (100 - GetHaste()); - } + float HasteModifier = GetHaste() * 0.01f; int32 dmg = 0; uint16 skill_to_use = -1; @@ -8556,7 +8214,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { MonkSpecialAttack(target, skill_to_use); //Live AA - Technique of Master Wu - uint16 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + uint32 bDoubleSpecialAttack = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; if( bDoubleSpecialAttack && (bDoubleSpecialAttack >= 100 || bDoubleSpecialAttack > MakeRandomInt(0,100))) { int MonkSPA [5] = { SkillFlyingKick, SkillDragonPunch, SkillEagleStrike, SkillTigerClaw, SkillRoundKick }; @@ -8587,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) { @@ -8893,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() { @@ -8980,13 +8619,7 @@ int32 Bot::CalcMaxMana() { } void Bot::SetAttackTimer() { - float PermaHaste; - if(GetHaste() > 0) - PermaHaste = 1 / (1 + (float)GetHaste()/100); - else if(GetHaste() < 0) - PermaHaste = 1 * (1 - (float)GetHaste()/100); - else - PermaHaste = 1.0f; + float haste_mod = GetHaste() * 0.01f; //default value for attack timer in case they have //an invalid weapon equipped: @@ -8995,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; @@ -9113,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; @@ -9125,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; @@ -9153,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; @@ -9181,66 +8774,66 @@ 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; int32 value_BaseEffect = 0; - int16 chance = 0; + int32 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); @@ -9250,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); } @@ -9357,7 +8950,7 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { // Formula = Unknown exact, based off a random percent chance up to mana cost(after focuses) of the cast spell if(this->itembonuses.Clairvoyance && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) { - int16 mana_back = this->itembonuses.Clairvoyance * MakeRandomInt(1, 100) / 100; + int32 mana_back = this->itembonuses.Clairvoyance * MakeRandomInt(1, 100) / 100; // Doesnt generate mana, so best case is a free spell if(mana_back > cost) mana_back = cost; @@ -9418,7 +9011,7 @@ int32 Bot::GetActSpellCost(uint16 spell_id, int32 cost) { } } - int16 focus_redux = GetBotFocusEffect(BotfocusManaCost, spell_id); + int32 focus_redux = GetBotFocusEffect(BotfocusManaCost, spell_id); if(focus_redux > 0) { @@ -10066,9 +9659,9 @@ int32 Bot::CalcManaRegenCap(){ } // Return max stat value for level -int16 Bot::GetMaxStat() { +int32 Bot::GetMaxStat() { int level = GetLevel(); - int16 base = 0; + int32 base = 0; if (level < 61) { base = 255; @@ -10086,10 +9679,10 @@ int16 Bot::GetMaxStat() { return(base); } -int16 Bot::GetMaxResist() { +int32 Bot::GetMaxResist() { int level = GetLevel(); - int16 base = 500; + int32 base = 500; if(level > 60) base += ((level - 60) * 5); @@ -10097,89 +9690,89 @@ int16 Bot::GetMaxResist() { return base; } -int16 Bot::GetMaxSTR() { +int32 Bot::GetMaxSTR() { return GetMaxStat() + itembonuses.STRCapMod + spellbonuses.STRCapMod + aabonuses.STRCapMod; } -int16 Bot::GetMaxSTA() { +int32 Bot::GetMaxSTA() { return GetMaxStat() + itembonuses.STACapMod + spellbonuses.STACapMod + aabonuses.STACapMod; } -int16 Bot::GetMaxDEX() { +int32 Bot::GetMaxDEX() { return GetMaxStat() + itembonuses.DEXCapMod + spellbonuses.DEXCapMod + aabonuses.DEXCapMod; } -int16 Bot::GetMaxAGI() { +int32 Bot::GetMaxAGI() { return GetMaxStat() + itembonuses.AGICapMod + spellbonuses.AGICapMod + aabonuses.AGICapMod; } -int16 Bot::GetMaxINT() { +int32 Bot::GetMaxINT() { return GetMaxStat() + itembonuses.INTCapMod + spellbonuses.INTCapMod + aabonuses.INTCapMod; } -int16 Bot::GetMaxWIS() { +int32 Bot::GetMaxWIS() { return GetMaxStat() + itembonuses.WISCapMod + spellbonuses.WISCapMod + aabonuses.WISCapMod; } -int16 Bot::GetMaxCHA() { +int32 Bot::GetMaxCHA() { return GetMaxStat() + itembonuses.CHACapMod + spellbonuses.CHACapMod + aabonuses.CHACapMod; } -int16 Bot::GetMaxMR() { +int32 Bot::GetMaxMR() { return GetMaxResist() + itembonuses.MRCapMod + spellbonuses.MRCapMod + aabonuses.MRCapMod; } -int16 Bot::GetMaxPR() { +int32 Bot::GetMaxPR() { return GetMaxResist() + itembonuses.PRCapMod + spellbonuses.PRCapMod + aabonuses.PRCapMod; } -int16 Bot::GetMaxDR() { +int32 Bot::GetMaxDR() { return GetMaxResist() + itembonuses.DRCapMod + spellbonuses.DRCapMod + aabonuses.DRCapMod; } -int16 Bot::GetMaxCR() { +int32 Bot::GetMaxCR() { return GetMaxResist() + itembonuses.CRCapMod + spellbonuses.CRCapMod + aabonuses.CRCapMod; } -int16 Bot::GetMaxFR() { +int32 Bot::GetMaxFR() { return GetMaxResist() + itembonuses.FRCapMod + spellbonuses.FRCapMod + aabonuses.FRCapMod; } -int16 Bot::GetMaxCorrup() { +int32 Bot::GetMaxCorrup() { return GetMaxResist() + itembonuses.CorrupCapMod + spellbonuses.CorrupCapMod + aabonuses.CorrupCapMod; } -int16 Bot::CalcSTR() { - int16 val = STR + itembonuses.STR + spellbonuses.STR; +int32 Bot::CalcSTR() { + int32 val = STR + itembonuses.STR + spellbonuses.STR; - int16 mod = aabonuses.STR; + int32 mod = aabonuses.STR; if(val>255 && GetLevel() <= 60) val = 255; @@ -10195,10 +9788,10 @@ int16 Bot::CalcSTR() { return(STR); } -int16 Bot::CalcSTA() { - int16 val = STA + itembonuses.STA + spellbonuses.STA; +int32 Bot::CalcSTA() { + int32 val = STA + itembonuses.STA + spellbonuses.STA; - int16 mod = aabonuses.STA; + int32 mod = aabonuses.STA; if(val>255 && GetLevel() <= 60) val = 255; @@ -10214,9 +9807,9 @@ int16 Bot::CalcSTA() { return(STA); } -int16 Bot::CalcAGI() { - int16 val = AGI + itembonuses.AGI + spellbonuses.AGI; - int16 mod = aabonuses.AGI; +int32 Bot::CalcAGI() { + int32 val = AGI + itembonuses.AGI + spellbonuses.AGI; + int32 mod = aabonuses.AGI; if(val>255 && GetLevel() <= 60) val = 255; @@ -10233,10 +9826,10 @@ int16 Bot::CalcAGI() { return(AGI); } -int16 Bot::CalcDEX() { - int16 val = DEX + itembonuses.DEX + spellbonuses.DEX; +int32 Bot::CalcDEX() { + int32 val = DEX + itembonuses.DEX + spellbonuses.DEX; - int16 mod = aabonuses.DEX; + int32 mod = aabonuses.DEX; if(val>255 && GetLevel() <= 60) val = 255; @@ -10252,10 +9845,10 @@ int16 Bot::CalcDEX() { return(DEX); } -int16 Bot::CalcINT() { - int16 val = INT + itembonuses.INT + spellbonuses.INT; +int32 Bot::CalcINT() { + int32 val = INT + itembonuses.INT + spellbonuses.INT; - int16 mod = aabonuses.INT; + int32 mod = aabonuses.INT; if(val>255 && GetLevel() <= 60) val = 255; @@ -10270,10 +9863,10 @@ int16 Bot::CalcINT() { return(INT); } -int16 Bot::CalcWIS() { - int16 val = WIS + itembonuses.WIS + spellbonuses.WIS; +int32 Bot::CalcWIS() { + int32 val = WIS + itembonuses.WIS + spellbonuses.WIS; - int16 mod = aabonuses.WIS; + int32 mod = aabonuses.WIS; if(val>255 && GetLevel() <= 60) val = 255; @@ -10289,10 +9882,10 @@ int16 Bot::CalcWIS() { return(WIS); } -int16 Bot::CalcCHA() { - int16 val = CHA + itembonuses.CHA + spellbonuses.CHA; +int32 Bot::CalcCHA() { + int32 val = CHA + itembonuses.CHA + spellbonuses.CHA; - int16 mod = aabonuses.CHA; + int32 mod = aabonuses.CHA; if(val>255 && GetLevel() <= 60) val = 255; @@ -10311,7 +9904,7 @@ int16 Bot::CalcCHA() { //The AA multipliers are set to be 5, but were 2 on WR //The resistant discipline which I think should be here is implemented //in Mob::ResistSpell -int16 Bot::CalcMR() +int32 Bot::CalcMR() { MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR; @@ -10327,7 +9920,7 @@ int16 Bot::CalcMR() return(MR); } -int16 Bot::CalcFR() +int32 Bot::CalcFR() { int c = GetClass(); if(c == RANGER) { @@ -10349,7 +9942,7 @@ int16 Bot::CalcFR() return(FR); } -int16 Bot::CalcDR() +int32 Bot::CalcDR() { int c = GetClass(); if(c == PALADIN) { @@ -10378,7 +9971,7 @@ int16 Bot::CalcDR() return(DR); } -int16 Bot::CalcPR() +int32 Bot::CalcPR() { int c = GetClass(); if(c == ROGUE) { @@ -10407,7 +10000,7 @@ int16 Bot::CalcPR() return(PR); } -int16 Bot::CalcCR() +int32 Bot::CalcCR() { int c = GetClass(); if(c == RANGER) { @@ -10429,7 +10022,7 @@ int16 Bot::CalcCR() return(CR); } -int16 Bot::CalcCorrup() +int32 Bot::CalcCorrup() { Corrup = Corrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; @@ -10439,7 +10032,7 @@ int16 Bot::CalcCorrup() return(Corrup); } -int16 Bot::CalcATK() { +int32 Bot::CalcATK() { ATK = itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); return(ATK); } @@ -11704,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."); @@ -11723,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)"); @@ -11741,8 +11335,6 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { if(!strcasecmp(sep->arg[1], "augmentitem")) { AugmentItem_Struct* in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)]; in_augment->container_slot = 1000; // - in_augment->unknown02[0] = 0; - in_augment->unknown02[1] = 0; in_augment->augment_slot = -1; Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject()); return; @@ -12272,7 +11864,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) { } if(!strcasecmp(sep->arg[1], "inventory") && !strcasecmp(sep->arg[2], "remove")) { - if((c->GetTarget() == nullptr) || (sep->arg[3] == '\0') || !c->GetTarget()->IsBot()) + if((c->GetTarget() == nullptr) || (sep->arg[3][0] == '\0') || !c->GetTarget()->IsBot()) { c->Message(15, "Usage: #bot inventory remove [slotid] (You must have a bot targetted) "); return; @@ -16479,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 eba10a54e..6e7b452af 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -3,15 +3,15 @@ #ifdef BOTS -#include "botStructs.h" +#include "bot_structs.h" #include "mob.h" #include "client.h" #include "pets.h" #include "groups.h" #include "corpse.h" #include "zonedb.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" +#include "string_ids.h" +#include "../common/misc_functions.h" #include "../common/debug.h" #include "guild_mgr.h" #include "worldserver.h" @@ -172,10 +172,10 @@ public: virtual int GetMonkHandToHandDamage(void); virtual bool TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse); virtual void DoRiposte(Mob* defender); - inline virtual int16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(SkillOffense)) * 9 / 10); } - inline virtual int16 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } - uint16 GetTotalATK(); - uint16 GetATKRating(); + inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(SkillOffense)) * 9 / 10); } + inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } + uint32 GetTotalATK(); + uint32 GetATKRating(); uint16 GetPrimarySkillValue(); uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } @@ -238,35 +238,35 @@ public: void ClearHealRotationLeader() { _healRotationLeader = 0; } void ClearHealRotationMembers(); void ClearHealRotationTargets(); - inline virtual int16 GetMaxStat(); - inline virtual int16 GetMaxResist(); - inline virtual int16 GetMaxSTR(); - inline virtual int16 GetMaxSTA(); - inline virtual int16 GetMaxDEX(); - inline virtual int16 GetMaxAGI(); - inline virtual int16 GetMaxINT(); - inline virtual int16 GetMaxWIS(); - inline virtual int16 GetMaxCHA(); - inline virtual int16 GetMaxMR(); - inline virtual int16 GetMaxPR(); - inline virtual int16 GetMaxDR(); - inline virtual int16 GetMaxCR(); - inline virtual int16 GetMaxFR(); - inline virtual int16 GetMaxCorrup(); - int16 CalcATK(); - int16 CalcSTR(); - int16 CalcSTA(); - int16 CalcDEX(); - int16 CalcAGI(); - int16 CalcINT(); - int16 CalcWIS(); - int16 CalcCHA(); - int16 CalcMR(); - int16 CalcFR(); - int16 CalcDR(); - int16 CalcPR(); - int16 CalcCR(); - int16 CalcCorrup(); + inline virtual int32 GetMaxStat(); + inline virtual int32 GetMaxResist(); + inline virtual int32 GetMaxSTR(); + inline virtual int32 GetMaxSTA(); + inline virtual int32 GetMaxDEX(); + inline virtual int32 GetMaxAGI(); + inline virtual int32 GetMaxINT(); + inline virtual int32 GetMaxWIS(); + inline virtual int32 GetMaxCHA(); + inline virtual int32 GetMaxMR(); + inline virtual int32 GetMaxPR(); + inline virtual int32 GetMaxDR(); + inline virtual int32 GetMaxCR(); + inline virtual int32 GetMaxFR(); + inline virtual int32 GetMaxCorrup(); + int32 CalcATK(); + int32 CalcSTR(); + int32 CalcSTA(); + int32 CalcDEX(); + int32 CalcAGI(); + int32 CalcINT(); + int32 CalcWIS(); + int32 CalcCHA(); + int32 CalcMR(); + int32 CalcFR(); + int32 CalcDR(); + int32 CalcPR(); + int32 CalcCR(); + int32 CalcCorrup(); int32 CalcHPRegenCap(); int32 CalcManaRegenCap(); int32 LevelRegen(); @@ -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); @@ -465,57 +465,57 @@ public: uint32 GetHealRotationNextHealTime() { return _healRotationNextHeal; } uint32 GetHealRotationTimer () { return _healRotationTimer; } bool GetBardUseOutOfCombatSongs() { return _bardUseOutOfCombatSongs;} - inline virtual int16 GetAC() const { return AC; } - inline virtual int16 GetSTR() const { return STR; } - inline virtual int16 GetSTA() const { return STA; } - inline virtual int16 GetDEX() const { return DEX; } - inline virtual int16 GetAGI() const { return AGI; } - inline virtual int16 GetINT() const { return INT; } - inline virtual int16 GetWIS() const { return WIS; } - inline virtual int16 GetCHA() const { return CHA; } - inline virtual int16 GetMR() const { return MR; } - inline virtual int16 GetFR() const { return FR; } - inline virtual int16 GetDR() const { return DR; } - inline virtual int16 GetPR() const { return PR; } - inline virtual int16 GetCR() const { return CR; } - inline virtual int16 GetCorrup() const { return Corrup; } + inline virtual int32 GetAC() const { return AC; } + inline virtual int32 GetSTR() const { return STR; } + inline virtual int32 GetSTA() const { return STA; } + inline virtual int32 GetDEX() const { return DEX; } + inline virtual int32 GetAGI() const { return AGI; } + inline virtual int32 GetINT() const { return INT; } + inline virtual int32 GetWIS() const { return WIS; } + inline virtual int32 GetCHA() const { return CHA; } + inline virtual int32 GetMR() const { return MR; } + inline virtual int32 GetFR() const { return FR; } + inline virtual int32 GetDR() const { return DR; } + inline virtual int32 GetPR() const { return PR; } + inline virtual int32 GetCR() const { return CR; } + inline virtual int32 GetCorrup() const { return Corrup; } //Heroic - inline virtual int16 GetHeroicSTR() const { return itembonuses.HeroicSTR; } - inline virtual int16 GetHeroicSTA() const { return itembonuses.HeroicSTA; } - inline virtual int16 GetHeroicDEX() const { return itembonuses.HeroicDEX; } - inline virtual int16 GetHeroicAGI() const { return itembonuses.HeroicAGI; } - inline virtual int16 GetHeroicINT() const { return itembonuses.HeroicINT; } - inline virtual int16 GetHeroicWIS() const { return itembonuses.HeroicWIS; } - inline virtual int16 GetHeroicCHA() const { return itembonuses.HeroicCHA; } - inline virtual int16 GetHeroicMR() const { return itembonuses.HeroicMR; } - inline virtual int16 GetHeroicFR() const { return itembonuses.HeroicFR; } - inline virtual int16 GetHeroicDR() const { return itembonuses.HeroicDR; } - inline virtual int16 GetHeroicPR() const { return itembonuses.HeroicPR; } - inline virtual int16 GetHeroicCR() const { return itembonuses.HeroicCR; } - inline virtual int16 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } + inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } + inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } + inline virtual int32 GetHeroicDEX() const { return itembonuses.HeroicDEX; } + inline virtual int32 GetHeroicAGI() const { return itembonuses.HeroicAGI; } + inline virtual int32 GetHeroicINT() const { return itembonuses.HeroicINT; } + inline virtual int32 GetHeroicWIS() const { return itembonuses.HeroicWIS; } + inline virtual int32 GetHeroicCHA() const { return itembonuses.HeroicCHA; } + inline virtual int32 GetHeroicMR() const { return itembonuses.HeroicMR; } + inline virtual int32 GetHeroicFR() const { return itembonuses.HeroicFR; } + inline virtual int32 GetHeroicDR() const { return itembonuses.HeroicDR; } + inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } + inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } + inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } // Mod2 - inline virtual int16 GetShielding() const { return itembonuses.MeleeMitigation; } - inline virtual int16 GetSpellShield() const { return itembonuses.SpellShield; } - inline virtual int16 GetDoTShield() const { return itembonuses.DoTShielding; } - inline virtual int16 GetStunResist() const { return itembonuses.StunResist; } - inline virtual int16 GetStrikeThrough() const { return itembonuses.StrikeThrough; } - inline virtual int16 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } - inline virtual int16 GetAccuracy() const { return itembonuses.HitChance; } - inline virtual int16 GetCombatEffects() const { return itembonuses.ProcChance; } - inline virtual int16 GetDS() const { return itembonuses.DamageShield; } + inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } + inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } + inline virtual int32 GetDoTShield() const { return itembonuses.DoTShielding; } + inline virtual int32 GetStunResist() const { return itembonuses.StunResist; } + inline virtual int32 GetStrikeThrough() const { return itembonuses.StrikeThrough; } + inline virtual int32 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } + inline virtual int32 GetAccuracy() const { return itembonuses.HitChance; } + inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } + inline virtual int32 GetDS() const { return itembonuses.DamageShield; } // Mod3 - inline virtual int16 GetHealAmt() const { return itembonuses.HealAmt; } - inline virtual int16 GetSpellDmg() const { return itembonuses.SpellDmg; } - inline virtual int16 GetClair() const { return itembonuses.Clairvoyance; } - inline virtual int16 GetDSMit() const { return itembonuses.DSMitigation; } + inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } + inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } + inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } + inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } - inline virtual int16 GetSingMod() const { return itembonuses.singingMod; } - inline virtual int16 GetBrassMod() const { return itembonuses.brassMod; } - inline virtual int16 GetPercMod() const { return itembonuses.percussionMod; } - inline virtual int16 GetStringMod() const { return itembonuses.stringedMod; } - inline virtual int16 GetWindMod() const { return itembonuses.windMod; } + inline virtual int32 GetSingMod() const { return itembonuses.singingMod; } + inline virtual int32 GetBrassMod() const { return itembonuses.brassMod; } + inline virtual int32 GetPercMod() const { return itembonuses.percussionMod; } + inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } + inline virtual int32 GetWindMod() const { return itembonuses.windMod; } - inline virtual int16 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath; } + inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath; } inline InspectMessage_Struct& GetInspectMessage() { return _botInspectMessage; } inline const InspectMessage_Struct& GetInspectMessage() const { return _botInspectMessage; } @@ -556,13 +556,13 @@ public: protected: virtual void PetAIProcess(); - static NPCType FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int16 mr, int16 cr, int16 dr, int16 fr, int16 pr, int16 corrup, int16 ac, uint16 str, uint16 sta, uint16 dex, uint16 agi, uint16 _int, uint16 wis, uint16 cha, uint16 attack); + static NPCType FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack); virtual void BotMeditate(bool isSitting); virtual void BotRangedAttack(Mob* other); virtual bool CheckBotDoubleAttack(bool Triple = false); - virtual int16 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id); - virtual int16 CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false); - virtual int16 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id); + virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id); + virtual int32 CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false); + virtual int32 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint16 spell_id); virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0); virtual float GetMaxMeleeRangeToTarget(Mob* target); @@ -601,7 +601,7 @@ private: int32 base_end; int32 cur_end; int32 max_end; - int16 end_regen; + int32 end_regen; uint32 timers[MaxTimer]; bool _hasBeenSummoned; float _preSummonX; @@ -626,26 +626,26 @@ private: bool _bardUseOutOfCombatSongs; // Private "base stats" Members - int16 _baseMR; - int16 _baseCR; - int16 _baseDR; - int16 _baseFR; - int16 _basePR; - int16 _baseCorrup; - int _baseAC; - int16 _baseSTR; - int16 _baseSTA; - int16 _baseDEX; - int16 _baseAGI; - int16 _baseINT; - int16 _baseWIS; - int16 _baseCHA; - int16 _baseATK; + int32 _baseMR; + int32 _baseCR; + int32 _baseDR; + int32 _baseFR; + int32 _basePR; + int32 _baseCorrup; + int32 _baseAC; + int32 _baseSTR; + int32 _baseSTA; + int32 _baseDEX; + int32 _baseAGI; + int32 _baseINT; + int32 _baseWIS; + int32 _baseCHA; + int32 _baseATK; uint16 _baseRace; // Necessary to preserve the race otherwise bots get their race updated in the db when they get an illusion. uint8 _baseGender; // Bots gender. Necessary to preserve the original value otherwise it can be changed by illusions. // Class Methods - int16 acmod(); + int32 acmod(); void GenerateBaseStats(); void GenerateAppearance(); void GenerateArmorClass(); @@ -670,8 +670,8 @@ private: void SavePetBuffs(SpellBuff_Struct* petBuffs, uint32 botPetSaveId); void LoadPetItems(uint32* petItems, uint32 botPetSaveId); void SavePetItems(uint32* petItems, uint32 botPetSaveId); - void LoadPetStats(std::string* petName, uint16* petMana, uint16* petHitPoints, uint32* botPetId, uint32 botPetSaveId); - uint32 SavePetStats(std::string petName, uint16 petMana, uint16 petHitPoints, uint32 botPetId); + void LoadPetStats(std::string* petName, uint32* petMana, uint32* petHitPoints, uint32* botPetId, uint32 botPetSaveId); + uint32 SavePetStats(std::string petName, uint32 petMana, uint32 petHitPoints, uint32 botPetId); void LoadPet(); // Load and spawn bot pet if there is one void SavePet(); // Save and depop bot pet if there is one uint32 GetPetSaveId(); diff --git a/zone/botStructs.h b/zone/bot_structs.h similarity index 100% rename from zone/botStructs.h rename to zone/bot_structs.h diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 14f243d19..4917ed953 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1,7 +1,7 @@ #ifdef BOTS #include "bot.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { diff --git a/zone/client.cpp b/zone/client.cpp index aa93478ba..e4f2c823a 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -17,13 +17,9 @@ */ #include "../common/debug.h" #include -#include -#include #include #include #include -#include -#include // for windows compile #ifdef _WINDOWS @@ -39,31 +35,23 @@ 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/ZoneNumbers.h" -#include "../common/moremath.h" #include "../common/guilds.h" -#include "../common/breakdowns.h" #include "../common/rulesys.h" -#include "../common/StringUtil.h" -#include "forage.h" +#include "../common/string_util.h" +#include "../common/data_verification.h" +#include "net.h" +#include "worldserver.h" +#include "zonedb.h" +#include "petitions.h" #include "command.h" -#include "StringIDs.h" -#include "NpcAI.h" +#include "string_ids.h" #include "client_logs.h" #include "guild_mgr.h" -#include "QuestParserCollection.h" - +#include "quest_parser_collection.h" +#include "queryserv.h" +extern QueryServ* QServ; extern EntityList entity_list; extern Zone* zone; extern volatile bool ZoneLoaded; @@ -72,8 +60,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 @@ -324,9 +310,11 @@ Client::Client(EQStreamInterface* ieqs) initial_respawn_selection = 0; alternate_currency_loaded = false; - + EngagedRaidTarget = false; SavedRaidRestTimer = 0; + + interrogateinv_flag = false; } Client::~Client() { @@ -396,7 +384,7 @@ Client::~Client() { if(IsHoveringForRespawn()) { m_pp.zone_id = m_pp.binds[0].zoneId; - m_pp.zoneInstance = 0; + m_pp.zoneInstance = m_pp.binds[0].instance_id; x_pos = m_pp.binds[0].x; y_pos = m_pp.binds[0].y; z_pos = m_pp.binds[0].z; @@ -478,60 +466,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 @@ -540,42 +491,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(CharacterID(), &m_pp); + + /* Save Current Bind Points */ + database.SaveCharacterBindPoint(CharacterID(), m_pp.binds[0].zoneId, m_pp.binds[0].instance_id, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0, 0); /* Regular bind */ + database.SaveCharacterBindPoint(CharacterID(), m_pp.binds[4].zoneId, m_pp.binds[4].instance_id, 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(); @@ -592,53 +573,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; - } + 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() @@ -727,14 +679,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 { @@ -805,8 +753,8 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } } - - if(RuleB(QueryServ, PlayerChatLogging)) { + /* Logs Player Chat */ + if (RuleB(QueryServ, PlayerLogChat)) { ServerPacket* pack = new ServerPacket(ServerOP_Speech, sizeof(Server_Speech_Struct) + strlen(message) + 1); Server_Speech_Struct* sem = (Server_Speech_Struct*) pack->pBuffer; @@ -841,7 +789,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s switch(chan_num) { - case 0: { // GuildChat + case 0: { /* Guild Chat */ if (!IsInAGuild()) Message_StringID(MT_DefaultText, GUILD_NOT_MEMBER2); //You are not a member of any guild. else if (!guild_mgr.CheckPermission(GuildID(), GuildRank(), GUILD_SPEAK)) @@ -850,7 +798,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s Message(0, "Error: World server disconnected"); break; } - case 2: { // GroupChat + case 2: { /* Group Chat */ Raid* raid = entity_list.GetRaidByClient(this); if(raid) { raid->RaidGroupSay((const char*) message, this); @@ -863,14 +811,14 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 15: { //raid say + case 15: { /* Raid Say */ Raid* raid = entity_list.GetRaidByClient(this); if(raid){ raid->RaidSay((const char*) message, this); } break; } - case 3: { // Shout + case 3: { /* Shout */ Mob *sender = this; if (GetPet() && GetPet()->FindType(SE_VoiceGraft)) sender = GetPet(); @@ -878,7 +826,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s entity_list.ChannelMessage(sender, chan_num, language, lang_skill, message); break; } - case 4: { // Auction + case 4: { /* Auction */ if(RuleB(Chat, ServerWideAuction)) { if(!global_channel_timer.Check()) @@ -917,7 +865,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 5: { // OOC + case 5: { /* OOC */ if(RuleB(Chat, ServerWideOOC)) { if(!global_channel_timer.Check()) @@ -964,15 +912,15 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s } break; } - case 6: // Broadcast - case 11: { // GMSay + case 6: /* Broadcast */ + case 11: { /* GM Say */ if (!(admin >= 80)) Message(0, "Error: Only GMs can use this channel"); else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, message)) Message(0, "Error: World server disconnected"); break; } - case 7: { // Tell + case 7: { /* Tell */ if(!global_channel_timer.Check()) { if(strlen(targetname) == 0) @@ -1020,7 +968,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s Message(0, "Error: World server disconnected"); break; } - case 8: { // /say + case 8: { /* Say */ if(message[0] == COMMAND_CHAR) { if(command_dispatch(this, message) == -2) { if(parse->PlayerHasQuestSub(EVENT_COMMAND)) { @@ -1029,7 +977,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); } } @@ -1327,7 +1275,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; @@ -1384,6 +1332,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; @@ -1405,6 +1354,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; @@ -1423,6 +1374,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; @@ -1915,15 +1868,36 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.equipment[MaterialFeet] = item->Material; ns->spawn.colors[MaterialFeet].color = GetEquipmentColor(MaterialFeet); } + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); if ((inst = m_inv[MainPrimary]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - if (strlen(item->IDFile) > 2) - ns->spawn.equipment[MaterialPrimary] = atoi(&item->IDFile[2]); + if (inst->GetOrnamentationAug(ornamentationAugtype)) { + item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + if (strlen(item->IDFile) > 2) + ns->spawn.equipment[MaterialPrimary] = atoi(&item->IDFile[2]); + } + else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { + ns->spawn.equipment[MaterialPrimary] = inst->GetOrnamentationIDFile(); + } + else { + item = inst->GetItem(); + if (strlen(item->IDFile) > 2) + ns->spawn.equipment[MaterialPrimary] = atoi(&item->IDFile[2]); + } } if ((inst = m_inv[MainSecondary]) && inst->IsType(ItemClassCommon)) { - item = inst->GetItem(); - if (strlen(item->IDFile) > 2) - ns->spawn.equipment[MaterialSecondary] = atoi(&item->IDFile[2]); + if (inst->GetOrnamentationAug(ornamentationAugtype)) { + item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + if (strlen(item->IDFile) > 2) + ns->spawn.equipment[MaterialSecondary] = atoi(&item->IDFile[2]); + } + else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { + ns->spawn.equipment[MaterialSecondary] = inst->GetOrnamentationIDFile(); + } + else { + item = inst->GetItem(); + if (strlen(item->IDFile) > 2) + ns->spawn.equipment[MaterialSecondary] = atoi(&item->IDFile[2]); + } } //these two may be related to ns->spawn.texture @@ -2111,7 +2085,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper = copperpp; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } silver -= copper; @@ -2126,7 +2100,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper += (silver-(m_pp.silver*10)); if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } @@ -2146,7 +2120,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { m_pp.copper += coppertest; if(updateclient) SendMoneyUpdate(); - Save(); + SaveCurrency(); return true; } @@ -2164,7 +2138,7 @@ bool Client::TakeMoneyFromPP(uint64 copper, bool updateclient) { if(updateclient) SendMoneyUpdate(); RecalcWeight(); - Save(); + SaveCurrency(); return true; } } @@ -2174,32 +2148,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) { @@ -2230,12 +2199,24 @@ 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::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 + */ + 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); +} + 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) @@ -2257,7 +2238,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", @@ -2338,13 +2319,15 @@ bool Client::CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int cha if (skillval < maxskill) { // 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 + int32 Chance = 10 + chancemodi + ((252 - skillval) / 20); + 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); @@ -2370,7 +2353,7 @@ void Client::CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill) { int LangSkill = m_pp.languages[langid]; // get current language skill if (LangSkill < 100) { // if the language isn't already maxed - int16 Chance = 5 + ((TeacherSkill - LangSkill)/10); // greater chance to learn if teacher's skill is much higher than yours + int32 Chance = 5 + ((TeacherSkill - LangSkill)/10); // greater chance to learn if teacher's skill is much higher than yours Chance = (Chance * RuleI(Character, SkillUpModifier)/100); if(MakeRandomFloat(0,100) < Chance) { // if they make the roll @@ -2591,38 +2574,6 @@ void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 } } -void Client::LogLoot(Client* player, Corpse* corpse, const Item_Struct* item){ - char* logtext; - char itemid[100]; - char itemname[100]; - char coinloot[100]; - if (item!=0){ - memset(itemid,0,sizeof(itemid)); - memset(itemname,0,sizeof(itemid)); - itoa(item->ID,itemid,10); - sprintf(itemname,"%s",item->Name); - logtext=itemname; - - strcat(logtext,"("); - strcat(logtext,itemid); - strcat(logtext,") Looted"); - database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),corpse->orgname,"Looting Item",logtext,4); - } - else{ - if ((corpse->GetPlatinum() + corpse->GetGold() + corpse->GetSilver() + corpse->GetCopper())>0) { - memset(coinloot,0,sizeof(coinloot)); - sprintf(coinloot,"%i PP %i GP %i SP %i CP",corpse->GetPlatinum(),corpse->GetGold(),corpse->GetSilver(),corpse->GetCopper()); - logtext=coinloot; - strcat(logtext," Looted"); - if (corpse->GetPlatinum()>10000) - database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),corpse->orgname,"Excessive Loot!",logtext,9); - else - database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),corpse->orgname,"Looting Money",logtext,5); - } - } -} - - bool Client::BindWound(Mob* bindmob, bool start, bool fail){ EQApplicationPacket* outapp = 0; if(!fail) { @@ -2785,6 +2736,7 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){ void Client::SetMaterial(int16 in_slot, uint32 item_id) { const Item_Struct* item = database.GetItem(item_id); + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); if (item && (item->ItemClass==ItemClassCommon)) { if (in_slot==MainHead) m_pp.item_material[MaterialHead] = item->Material; @@ -2794,21 +2746,38 @@ 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) m_pp.item_material[MaterialLegs] = item->Material; else if (in_slot==MainFeet) m_pp.item_material[MaterialFeet] = item->Material; - else if (in_slot==MainPrimary) - m_pp.item_material[MaterialPrimary] = atoi(item->IDFile+2); - else if (in_slot==MainSecondary) - m_pp.item_material[MaterialSecondary] = atoi(item->IDFile+2); + else if (in_slot == MainPrimary) { + const ItemInst* inst = m_inv[MainPrimary]; + if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { + item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + m_pp.item_material[MaterialPrimary] = atoi(item->IDFile + 2); + } + else if (inst && inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { + m_pp.item_material[MaterialPrimary] = inst->GetOrnamentationIDFile(); + } + else { + m_pp.item_material[MaterialPrimary] = atoi(item->IDFile + 2); + } + } + else if (in_slot == MainSecondary) { + const ItemInst* inst = m_inv[MainSecondary]; + if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { + item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + m_pp.item_material[MaterialSecondary] = atoi(item->IDFile + 2); + } + else if (inst && inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { + m_pp.item_material[MaterialSecondary] = inst->GetOrnamentationIDFile(); + } + else { + m_pp.item_material[MaterialSecondary] = atoi(item->IDFile + 2); + } + } } } @@ -3121,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 @@ -3152,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) @@ -3186,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; @@ -3203,6 +3184,10 @@ void Client::LinkDead() { entity_list.MessageGroup(this,true,15,"%s has gone linkdead.",GetName()); GetGroup()->DelMember(this); + if (GetMerc()) + { + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + } } Raid *raid = entity_list.GetRaidByClient(this); if(raid){ @@ -3689,7 +3674,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) @@ -3829,7 +3818,8 @@ void Client::Sacrifice(Client *caster) void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) { - if(!Caster || PendingTranslocate) return; + if(!Caster || PendingTranslocate) + return; const SPDat_Spell_Struct &Spell = spells[SpellID]; @@ -3837,26 +3827,29 @@ void Client::SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID) { Translocate_Struct *ts = (Translocate_Struct*)outapp->pBuffer; strcpy(ts->Caster, Caster->GetName()); - ts->SpellID = SpellID; + PendingTranslocateData.spell_id = ts->SpellID = SpellID; if((SpellID == 1422) || (SpellID == 1334) || (SpellID == 3243)) { - ts->ZoneID = m_pp.binds[0].zoneId; - ts->x = m_pp.binds[0].x; - ts->y = m_pp.binds[0].y; - ts->z = m_pp.binds[0].z; + PendingTranslocateData.zone_id = ts->ZoneID = m_pp.binds[0].zoneId; + PendingTranslocateData.instance_id = m_pp.binds[0].instance_id; + PendingTranslocateData.x = ts->x = m_pp.binds[0].x; + PendingTranslocateData.y = ts->y = m_pp.binds[0].y; + PendingTranslocateData.z = ts->z = m_pp.binds[0].z; + PendingTranslocateData.heading = m_pp.binds[0].heading; } else { - ts->ZoneID = database.GetZoneID(Spell.teleport_zone); - ts->y = Spell.base[0]; - ts->x = Spell.base[1]; - ts->z = Spell.base[2]; + PendingTranslocateData.zone_id = ts->ZoneID = database.GetZoneID(Spell.teleport_zone); + PendingTranslocateData.instance_id = 0; + PendingTranslocateData.y = ts->y = Spell.base[0]; + PendingTranslocateData.x = ts->x = Spell.base[1]; + PendingTranslocateData.z = ts->z = Spell.base[2]; + PendingTranslocateData.heading = 0.0; } ts->unknown008 = 0; ts->Complete = 0; - PendingTranslocateData = *ts; - PendingTranslocate=true; + PendingTranslocate = true; TranslocateTime = time(nullptr); QueuePacket(outapp); @@ -3989,50 +3982,38 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const void Client::KeyRingLoad() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - - sprintf(query, "SELECT item_id FROM keyring WHERE char_id='%i' ORDER BY item_id",character_id); - if (database.RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - while(0 != (row = mysql_fetch_row(result))){ - keyring.push_back(atoi(row[0])); - } - mysql_free_result(result); - }else { - std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT item_id FROM keyring " + "WHERE char_id = '%i' ORDER BY item_id", character_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in Client::KeyRingLoad query '" << query << "' " << results.ErrorMessage() << std::endl; return; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) + keyring.push_back(atoi(row[0])); + } void Client::KeyRingAdd(uint32 item_id) { - if(0==item_id)return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - query = new char[256]; - bool bFound = KeyRingCheck(item_id); - if(!bFound){ - sprintf(query, "INSERT INTO keyring(char_id,item_id) VALUES(%i,%i)",character_id,item_id); - if(database.RunQuery(query, strlen(query), errbuf, 0, &affected_rows)) - { - Message(4,"Added to keyring."); - safe_delete_array(query); - } - else - { - std::cerr << "Error in Doors::HandleClick query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return; - } - keyring.push_back(item_id); - } + if(0==item_id) + return; + + bool found = KeyRingCheck(item_id); + if (found) + return; + + std::string query = StringFormat("INSERT INTO keyring(char_id, item_id) VALUES(%i, %i)", character_id, item_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in Doors::HandleClick query '" << query << "' " << results.ErrorMessage() << std::endl; + return; + } + + Message(4,"Added to keyring."); + + keyring.push_back(item_id); } bool Client::KeyRingCheck(uint32 item_id) @@ -4063,40 +4044,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); } @@ -4141,6 +4109,184 @@ void Client::UpdateLFP() { worldserver.UpdateLFP(CharacterID(), LFPMembers); } +bool Client::GroupFollow(Client* inviter) { + + if (inviter) + { + isgrouped = true; + Raid* raid = entity_list.GetRaidByClient(inviter); + Raid* iraid = entity_list.GetRaidByClient(this); + + //inviter has a raid don't do group stuff instead do raid stuff! + 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){ + groupToUse = raid->members[x].GroupNumber; + break; + } + } + } + if (iraid == raid) + { + //both in same raid + uint32 ngid = raid->GetGroup(inviter->GetName()); + if (raid->GroupCount(ngid) < 6) + { + raid->MoveMember(GetName(), ngid); + raid->SendGroupDisband(this); + //raid->SendRaidGroupAdd(GetName(), ngid); + //raid->SendGroupUpdate(this); + raid->GroupUpdate(ngid); //break + } + return false; + } + if (raid->RaidCount() < MAX_RAID_MEMBERS) + { + if (raid->GroupCount(groupToUse) < 6) + { + raid->SendRaidCreate(this); + raid->SendMakeLeaderPacketTo(raid->leadername, this); + raid->AddMember(this, groupToUse); + raid->SendBulkRaid(this); + //raid->SendRaidGroupAdd(GetName(), groupToUse); + //raid->SendGroupUpdate(this); + raid->GroupUpdate(groupToUse); //break + if (raid->IsLocked()) + { + raid->SendRaidLockTo(this); + } + return false; + } + else + { + raid->SendRaidCreate(this); + raid->SendMakeLeaderPacketTo(raid->leadername, this); + raid->AddMember(this); + raid->SendBulkRaid(this); + if (raid->IsLocked()) + { + raid->SendRaidLockTo(this); + } + return false; + } + } + } + + Group* group = entity_list.GetGroupByClient(inviter); + + if (!group) + { + //Make new group + group = new Group(inviter); + + if (!group) + { + return false; + } + + entity_list.AddGroup(group); + + 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 false; + } + + //now we have a group id, can set inviter's id + database.SetGroupID(inviter->GetName(), group->GetID(), inviter->CharacterID(), false); + database.SetGroupLeaderName(group->GetID(), inviter->GetName()); + group->UpdateGroupAAs(); + + //Invite the inviter into the group first.....dont ask + if (inviter->GetClientVersion() < EQClientSoD) + { + 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'. + group->GetGroupAAs(&outgj->leader_aas); + inviter->QueuePacket(outapp); + safe_delete(outapp); + } + else + { + // SoD and later + inviter->SendGroupCreatePacket(); + inviter->SendGroupLeaderChangePacket(inviter->GetName()); + inviter->SendGroupJoinAcknowledge(); + } + + } + + if (!group) + { + return false; + } + + // Remove merc from old group before adding client to the new one + if (GetMerc() && GetMerc()->HasGroup()) + { + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + } + + if (!group->AddMember(this)) + { + // If failed to add client to new group, regroup with merc + if (GetMerc()) + { + GetMerc()->MercJoinClientGroup(); + } + else + { + isgrouped = false; + } + return false; + } + + if (GetClientVersion() >= EQClientSoD) + { + SendGroupJoinAcknowledge(); + } + + // Temporary hack for SoD, as things seem to work quite differently + if (inviter->IsClient() && inviter->GetClientVersion() >= EQClientSoD) + { + database.RefreshGroupFromDB(inviter); + } + + // Add the merc back into the new group if possible + if (GetMerc()) + { + GetMerc()->MercJoinClientGroup(); + } + + if (inviter->IsLFP()) + { + // If the player who invited us to a group is LFP, have them update world now that we have joined their group. + inviter->UpdateLFP(); + } + + database.RefreshGroupFromDB(this); + group->SendHPPacketsTo(this); + //send updates to clients out of zone... + group->SendGroupJoinOOZ(this); + return true; + } + return false; +} + uint16 Client::GetPrimarySkillValue() { SkillUseTypes skill = HIGHEST_SKILL; //because nullptr == 0, which is 1H Slashing, & we want it to return 0 from GetSkill @@ -4201,10 +4347,10 @@ uint16 Client::GetPrimarySkillValue() return GetSkill(skill); } -uint16 Client::GetTotalATK() +uint32 Client::GetTotalATK() { - uint16 AttackRating = 0; - uint16 WornCap = itembonuses.ATK; + uint32 AttackRating = 0; + uint32 WornCap = itembonuses.ATK; if(IsClient()) { AttackRating = ((WornCap * 1.342) + (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69)); @@ -4221,9 +4367,9 @@ uint16 Client::GetTotalATK() return AttackRating; } -uint16 Client::GetATKRating() +uint32 Client::GetATKRating() { - uint16 AttackRating = 0; + uint32 AttackRating = 0; if(IsClient()) { AttackRating = (GetSkill(SkillOffense) * 1.345) + ((GetSTR() - 66) * 0.9) + (GetPrimarySkillValue() * 2.69); @@ -4269,7 +4415,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; @@ -4279,40 +4424,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; @@ -4333,15 +4484,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) { @@ -4386,9 +4537,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); @@ -4474,7 +4625,8 @@ void Client::SendRespawnBinds() BindStruct* b = &m_pp.binds[0]; RespawnOption opt; opt.name = "Bind Location"; - opt.zoneid = b->zoneId; + opt.zone_id = b->zoneId; + opt.instance_id = b->instance_id; opt.x = b->x; opt.y = b->y; opt.z = b->z; @@ -4484,7 +4636,8 @@ void Client::SendRespawnBinds() //Rez is always added at the end RespawnOption rez; rez.name = "Resurrect"; - rez.zoneid = zone->GetZoneID(); + rez.zone_id = zone->GetZoneID(); + rez.instance_id = zone->GetInstanceID(); rez.x = GetX(); rez.y = GetY(); rez.z = GetZ(); @@ -4493,7 +4646,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; @@ -4519,7 +4672,7 @@ void Client::SendRespawnBinds() { opt = &(*itr); VARSTRUCT_ENCODE_TYPE(uint32, buffer, count++); //option num (from 0) - VARSTRUCT_ENCODE_TYPE(uint32, buffer, opt->zoneid); + VARSTRUCT_ENCODE_TYPE(uint32, buffer, opt->zone_id); VARSTRUCT_ENCODE_TYPE(float, buffer, opt->x); VARSTRUCT_ENCODE_TYPE(float, buffer, opt->y); VARSTRUCT_ENCODE_TYPE(float, buffer, opt->z); @@ -4782,7 +4935,7 @@ void Client::SummonAndRezzAllCorpses() entity_list.RemoveAllCorpsesByCharID(CharacterID()); - int CorpseCount = database.SummonAllPlayerCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), + int CorpseCount = database.SummonAllCharacterCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), GetX(), GetY(), GetZ(), GetHeading()); if(CorpseCount <= 0) { @@ -4820,7 +4973,7 @@ void Client::SummonAllCorpses(float dest_x, float dest_y, float dest_z, float de entity_list.RemoveAllCorpsesByCharID(CharacterID()); - int CorpseCount = database.SummonAllPlayerCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), + int CorpseCount = database.SummonAllCharacterCorpses(CharacterID(), zone->GetZoneID(), zone->GetInstanceID(), dest_x, dest_y, dest_z, dest_heading); if(CorpseCount <= 0) { @@ -4864,7 +5017,7 @@ void Client::DepopPlayerCorpse(uint32 dbid) void Client::BuryPlayerCorpses() { - database.BuryAllPlayerCorpses(CharacterID()); + database.BuryAllCharacterCorpses(CharacterID()); } void Client::NotifyNewTitlesAvailable() @@ -4892,6 +5045,10 @@ void Client::SetStartZone(uint32 zoneid, float x, float y, float z) return; m_pp.binds[4].zoneId = zoneid; + if(zone->GetInstanceID() != 0 && zone->IsInstancePersistent()) { + m_pp.binds[4].instance_id = zone->GetInstanceID(); + } + if (x == 0 && y == 0 && z ==0) database.GetSafePoints(m_pp.binds[4].zoneId, 0, &m_pp.binds[4].x, &m_pp.binds[4].y, &m_pp.binds[4].z); else { @@ -5279,71 +5436,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 @@ -5353,143 +5493,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; } @@ -5717,7 +5804,7 @@ void Client::AddCrystals(uint32 Radiant, uint32 Ebon) m_pp.currentEbonCrystals += Ebon; m_pp.careerEbonCrystals += Ebon; - Save(); + SaveCurrency(); SendCrystalCounts(); } @@ -5732,15 +5819,26 @@ void Client::ProcessInspectRequest(Client* requestee, Client* requester) { const Item_Struct* item = nullptr; const ItemInst* inst = nullptr; - + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); for(int16 L = 0; L <= 20; L++) { inst = requestee->GetInv().GetItem(L); if(inst) { item = inst->GetItem(); if(item) { - strcpy(insr->itemnames[L], item->Name); - insr->itemicons[L] = item->Icon; + if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { + const Item_Struct *aug_weap = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = aug_weap->Icon; + } + else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = inst->GetOrnamentationIcon(); + } + else { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = item->Icon; + } } else insr->itemicons[L] = 0xFFFFFFFF; @@ -6328,7 +6426,6 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid static const float swarm_pet_x[MAX_SWARM_PETS] = { 5, -5, 5, -5, 10, -10, 10, -10, 8, -8, 8, -8 }; static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, 10, 10, -10, -10, 8, 8, -8, -8 }; - TempPets(true); while(summon_count > 0) { NPCType *npc_dup = nullptr; @@ -6373,6 +6470,11 @@ void Client::AssignToInstance(uint16 instance_id) database.AddClientToInstance(instance_id, CharacterID()); } +void Client::RemoveFromInstance(uint16 instance_id) +{ + database.RemoveClientFromInstance(instance_id, CharacterID()); +} + void Client::SendStatsWindow(Client* client, bool use_window) { // Define the types of page breaks we need @@ -6924,8 +7026,18 @@ void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount) SendAlternateCurrencyValue(currency_id); } -void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount) +void Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method) { + + /* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */ + if (method == 1){ + /* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */ + if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){ + std::string event_desc = StringFormat("Added via Quest :: Cursor to Item :: alt_currency_id:%i amount:%i in zoneid:%i instid:%i", currency_id, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Alternate_Currency_Transactions, this->CharacterID(), event_desc); + } + } + if(amount == 0) { return; } @@ -7346,26 +7458,26 @@ void Client::SendMercPersonalInfo() { uint32 mercTypeCount = 1; uint32 mercCount = 1; //TODO: Un-hardcode this and support multiple mercs like in later clients than SoD. - //uint32 packetSize = 0; - uint32 i=0; + uint32 i = 0; uint32 altCurrentType = 19; //TODO: Implement alternate currency purchases involving mercs! - if (GetClientVersion() >= EQClientRoF) + MercTemplate *mercData = &zone->merc_templates[GetMercInfo().MercTemplateID]; + + int stancecount = 0; + stancecount += zone->merc_stance_list[GetMercInfo().MercTemplateID].size(); + if(stancecount > MAX_MERC_STANCES || mercCount > MAX_MERC || mercTypeCount > MAX_MERC_GRADES) { - MercTemplate *mercData = &zone->merc_templates[GetMercInfo().MercTemplateID]; + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercPersonalInfo Cancelled: (%i) (%i) (%i)", stancecount, mercCount, mercTypeCount); + SendMercMerchantResponsePacket(0); + return; + } - if (mercData) + if(mercData) + { + if (GetClientVersion() >= EQClientRoF) { - int i = 0; - int stancecount = 0; - stancecount += zone->merc_stance_list[GetMercInfo().MercTemplateID].size(); - - if(stancecount > MAX_MERC_STANCES || mercCount > MAX_MERC || mercTypeCount > MAX_MERC_GRADES) - { - SendMercMerchantResponsePacket(0); - return; - } - if (mercCount > 0 && mercCount) + if (mercCount > 0) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataUpdate, sizeof(MercenaryDataUpdate_Struct)); MercenaryDataUpdate_Struct* mdus = (MercenaryDataUpdate_Struct*)outapp->pBuffer; @@ -7403,40 +7515,19 @@ void Client::SendMercPersonalInfo() mdus->MercData[i].MercUnk05 = 1; FastQueuePacket(&outapp); + safe_delete(outapp); return; } } - } - else - { - int stancecount = 0; - stancecount += zone->merc_stance_list[GetMercInfo().MercTemplateID].size(); - - if(mercCount > MAX_MERC || mercTypeCount > MAX_MERC_GRADES) + else { - if (GetClientVersion() == EQClientSoD) + if(mercTypeCount > 0 && mercCount > 0) { - SendMercMerchantResponsePacket(0); - } - return; - } - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, sizeof(MercenaryMerchantList_Struct)); - MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; - MercTemplate *mercData = &zone->merc_templates[GetMercInfo().MercTemplateID]; - - - if(mercData) - { - if(mercTypeCount > 0) - { - mml->MercTypeCount = mercTypeCount; //We only should have one merc entry. + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryDataResponse, sizeof(MercenaryMerchantList_Struct)); + MercenaryMerchantList_Struct* mml = (MercenaryMerchantList_Struct*)outapp->pBuffer; + mml->MercTypeCount = mercTypeCount; //We should only have one merc entry. mml->MercGrades[i] = 1; - } - mml->MercCount = mercCount; - if(mercCount > 0) - { - + mml->MercCount = mercCount; mml->Mercs[i].MercID = mercData->MercTemplateID; mml->Mercs[i].MercType = mercData->MercType; mml->Mercs[i].MercSubType = mercData->MercSubType; @@ -7453,7 +7544,7 @@ void Client::SendMercPersonalInfo() mml->Mercs[i].StanceCount = zone->merc_stance_list[mercData->MercTemplateID].size(); mml->Mercs[i].MercUnk03 = 0; mml->Mercs[i].MercUnk04 = 1; - //mml->Mercs[i].MercName; + strn0cpy(mml->Mercs[i].MercName, GetMercInfo().merc_name , sizeof(mml->Mercs[i].MercName)); int stanceindex = 0; if(mml->Mercs[i].StanceCount != 0) { @@ -7467,31 +7558,21 @@ void Client::SendMercPersonalInfo() } } FastQueuePacket(&outapp); - } - else - { safe_delete(outapp); - if (GetClientVersion() == EQClientSoD) - { - SendMercMerchantResponsePacket(0); - } return; } - if (GetClientVersion() == EQClientSoD) - { - SendMercMerchantResponsePacket(0); - } - } - else - { - safe_delete(outapp); - if (GetClientVersion() == EQClientSoD) - { - SendMercMerchantResponsePacket(0); - } - return; } + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercPersonalInfo Send Successful"); + + SendMercMerchantResponsePacket(0); } + else + { + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercPersonalInfo Send Failed Due to no MercData for %i", GetMercInfo().MercTemplateID); + } + } void Client::SendClearMercInfo() @@ -7553,7 +7634,7 @@ FACTION_VALUE Client::GetReverseFactionCon(Mob* iOther) { } //o-------------------------------------------------------------- -//| Name: GetFactionLevel; rembrant, Dec. 16, 2001 +//| Name: GetFactionLevel; Dec. 16, 2001 //o-------------------------------------------------------------- //| Notes: Gets the characters faction standing with the specified NPC. //| Will return Indifferent on failure. @@ -7566,7 +7647,7 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra int32 tmpFactionValue; FactionMods fmods; - // neotokyo: few optimizations + // few optimizations if (GetFeigned()) return FACTION_INDIFFERENT; if (invisible_undead && tnpc && !tnpc->SeeInvisibleUndead()) @@ -7589,7 +7670,7 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra { //Get the players current faction with pFaction tmpFactionValue = GetCharacterFactionLevel(pFaction); - // Everhood - tack on any bonuses from Alliance type spell effects + //Tack on any bonuses from Alliance type spell effects tmpFactionValue += GetFactionBonus(pFaction); tmpFactionValue += GetItemFactionBonus(pFaction); //Return the faction to the client @@ -7611,97 +7692,77 @@ FACTION_VALUE Client::GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_ra return fac; } -//o-------------------------------------------------------------- -//| Name: SetFactionLevel; rembrant, Dec. 20, 2001 -//o-------------------------------------------------------------- -//| Notes: Sets the characters faction standing with the specified NPC. -//o-------------------------------------------------------------- +//Sets the characters faction standing with the specified NPC. void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity) { - int32 faction_id[MAX_NPC_FACTIONS]={ 0,0,0,0,0,0,0,0,0,0 }; - int32 npc_value[MAX_NPC_FACTIONS]={ 0,0,0,0,0,0,0,0,0,0 }; - uint8 temp[MAX_NPC_FACTIONS]={ 0,0,0,0,0,0,0,0,0,0 }; + int32 faction_id[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + int32 npc_value[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + uint8 temp[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int32 mod; - int32 t; int32 tmpValue; int32 current_value; FactionMods fm; + bool change = false; + bool repair = false; + // Get the npc faction list - if(!database.GetNPCFactionList(npc_id, faction_id, npc_value, temp)) + if (!database.GetNPCFactionList(npc_id, faction_id, npc_value, temp)) return; - for(int i = 0;iitembonuses.HeroicCHA) { + if (this->itembonuses.HeroicCHA) + { int faction_mod = itembonuses.HeroicCHA / 5; // If our result isn't truncated, then just do that - if(npc_value[i] * faction_mod / 100 != 0) + if (npc_value[i] * faction_mod / 100 != 0) npc_value[i] += npc_value[i] * faction_mod / 100; // If our result is truncated, then double a mob's value every once and a while to equal what they would have got - else { - if(MakeRandomInt(0, 100) < faction_mod) + else + { + if (MakeRandomInt(0, 100) < faction_mod) npc_value[i] *= 2; } } - //figure out their modifier - mod = fm.base + fm.class_mod + fm.race_mod + fm.deity_mod; - if(mod > MAX_FACTION) - mod = MAX_FACTION; - else if(mod < MIN_FACTION) - mod = MIN_FACTION; + // Set flag when to update db + if (current_value > MAX_PERSONAL_FACTION) + { + current_value = MAX_PERSONAL_FACTION; + repair = true; + } + else if (current_value < MIN_PERSONAL_FACTION) + { + current_value = MIN_PERSONAL_FACTION; + repair = true; + } + else if ((m_pp.gm != 1) && (npc_value[i] != 0) && ((current_value != MAX_PERSONAL_FACTION) || (current_value != MIN_PERSONAL_FACTION))) + change = true; - // Calculate the faction - if(npc_value[i] != 0) { - tmpValue = current_value + mod + npc_value[i]; + current_value += npc_value[i]; - int16 FactionModPct = spellbonuses.FactionModPct + itembonuses.FactionModPct + aabonuses.FactionModPct; - tmpValue += (tmpValue * FactionModPct) / 100; + if (current_value > MAX_PERSONAL_FACTION) + current_value = MAX_PERSONAL_FACTION; + else if (current_value < MIN_PERSONAL_FACTION) + current_value = MIN_PERSONAL_FACTION; - // Make sure faction hits don't go to GMs... - if (m_pp.gm==1 && (tmpValue < current_value)) { - tmpValue = current_value; - } + if (change || repair) + { + database.SetCharacterFactionLevel(char_id, faction_id[i], current_value, temp[i], factionvalues); - // Make sure we dont go over the min/max faction limits - if(tmpValue >= MAX_FACTION) + if (change) { - t = MAX_FACTION - mod; - if(current_value == t) { - //do nothing, it is already maxed out - } else if(!(database.SetCharacterFactionLevel(char_id, faction_id[i], t, temp[i], factionvalues))) - { - return; - } + mod = fm.base + fm.class_mod + fm.race_mod + fm.deity_mod; + tmpValue = current_value + mod + npc_value[i]; + SendFactionMessage(npc_value[i], faction_id[i], tmpValue, temp[i]); } - else if(tmpValue <= MIN_FACTION) - { - t = MIN_FACTION - mod; - if(current_value == t) { - //do nothing, it is already maxed out - } else if(!(database.SetCharacterFactionLevel(char_id, faction_id[i], t, temp[i], factionvalues))) - { - return; - } - } - else - { - if(!(database.SetCharacterFactionLevel(char_id, faction_id[i], current_value + npc_value[i], temp[i], factionvalues))) - { - return; - } - } - if(tmpValue <= MIN_FACTION) - tmpValue = MIN_FACTION; - - SendFactionMessage(npc_value[i], faction_id[i], tmpValue, temp[i]); } } } @@ -7729,46 +7790,82 @@ int32 Client::GetCharacterFactionLevel(int32 faction_id) return 0; faction_map::iterator res; res = factionvalues.find(faction_id); - if(res == factionvalues.end()) - return(0); - return(res->second); + if (res == factionvalues.end()) + return 0; + return res->second; } // returns the character's faction level, adjusted for racial, class, and deity modifiers int32 Client::GetModCharacterFactionLevel(int32 faction_id) { int32 Modded = GetCharacterFactionLevel(faction_id); FactionMods fm; - if(database.GetFactionData(&fm,GetClass(),GetRace(),GetDeity(),faction_id)) + if (database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), faction_id)) Modded += fm.base + fm.class_mod + fm.race_mod + fm.deity_mod; - if (Modded > MAX_FACTION) - Modded = MAX_FACTION; return Modded; } -bool Client::HatedByClass(uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction) +void Client::MerchantRejectMessage(Mob *merchant, int primaryfaction) { + int messageid = 0; + int32 tmpFactionValue = 0; + int32 lowestvalue = 0; + FactionMods fmod; - bool Result = false; - - int32 tmpFactionValue; - FactionMods fmods; - - //First get the NPC's Primary faction - if(pFaction > 0) - { - //Get the faction data from the database - if(database.GetFactionData(&fmods, p_class, p_race, p_deity, pFaction)) - { - tmpFactionValue = GetCharacterFactionLevel(pFaction); - tmpFactionValue += GetFactionBonus(pFaction); - tmpFactionValue += GetItemFactionBonus(pFaction); - CalculateFaction(&fmods, tmpFactionValue); - if(fmods.class_mod < fmods.race_mod) - Result = true; + // If a faction is involved, get the data. + if (primaryfaction > 0) { + if (database.GetFactionData(&fmod, GetClass(), GetRace(), GetDeity(), primaryfaction)) { + tmpFactionValue = GetCharacterFactionLevel(primaryfaction); + lowestvalue = std::min(tmpFactionValue, std::min(fmod.class_mod, fmod.race_mod)); } } - return Result; + // If no primary faction or biggest influence is your faction hit + if (primaryfaction <= 0 || lowestvalue == tmpFactionValue) { + merchant->Say_StringID(MakeRandomInt(WONT_SELL_DEEDS1, WONT_SELL_DEEDS6)); + } else if (lowestvalue == fmod.race_mod) { // race biggest + // Non-standard race (ex. illusioned to wolf) + if (GetRace() > PLAYER_RACE_COUNT) { + messageid = MakeRandomInt(1, 3); // these aren't sequential StringIDs :( + switch (messageid) { + case 1: + messageid = WONT_SELL_NONSTDRACE1; + break; + case 2: + messageid = WONT_SELL_NONSTDRACE2; + break; + case 3: + messageid = WONT_SELL_NONSTDRACE3; + break; + default: // w/e should never happen + messageid = WONT_SELL_NONSTDRACE1; + break; + } + merchant->Say_StringID(messageid); + } else { // normal player races + messageid = MakeRandomInt(1, 4); + switch (messageid) { + case 1: + messageid = WONT_SELL_RACE1; + break; + case 2: + messageid = WONT_SELL_RACE2; + break; + case 3: + messageid = WONT_SELL_RACE3; + break; + case 4: + messageid = WONT_SELL_RACE4; + break; + default: // w/e should never happen + messageid = WONT_SELL_RACE1; + break; + } + merchant->Say_StringID(messageid, itoa(GetRace())); + } + } else if (lowestvalue == fmod.class_mod) { + merchant->Say_StringID(MakeRandomInt(WONT_SELL_CLASS1, WONT_SELL_CLASS5), itoa(GetClass())); + } + return; } //o-------------------------------------------------------------- @@ -7782,59 +7879,51 @@ void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalval // default to Faction# if we couldn't get the name from the ID if (database.GetFactionName(faction_id, name, sizeof(name)) == false) - snprintf(name, sizeof(name),"Faction%i",faction_id); + snprintf(name, sizeof(name), "Faction%i", faction_id); if (tmpvalue == 0 || temp == 1 || temp == 2) return; - else if (totalvalue >= MAX_FACTION) - Message_StringID(0, FACTION_BEST, name); - else if (tmpvalue > 0 && totalvalue < MAX_FACTION) - Message_StringID(0, FACTION_BETTER, name); - else if (tmpvalue < 0 && totalvalue > MIN_FACTION) - Message_StringID(0, FACTION_WORSE, name); - else if (totalvalue <= MIN_FACTION) - Message_StringID(0, FACTION_WORST, name); + else if (totalvalue >= MAX_PERSONAL_FACTION) + Message_StringID(15, FACTION_BEST, name); + else if (totalvalue <= MIN_PERSONAL_FACTION) + Message_StringID(15, FACTION_WORST, name); + else if (tmpvalue > 0 && totalvalue < MAX_PERSONAL_FACTION && !RuleB(Client, UseLiveFactionMessage)) + Message_StringID(15, FACTION_BETTER, name); + else if (tmpvalue < 0 && totalvalue > MIN_PERSONAL_FACTION && !RuleB(Client, UseLiveFactionMessage)) + Message_StringID(15, FACTION_WORSE, name); + else if (RuleB(Client, UseLiveFactionMessage)) + Message(15, "Your faction standing with %s has been adjusted by %i.", name, tmpvalue); //New Live faction message (14261) return; } 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; } @@ -7913,7 +8002,7 @@ void Client::ItemTimerCheck() TryItemTimer(i); } - for(i = EmuConstants::GENERAL_BAGS_BEGIN; i <= MainCursor; i++) + for(i = EmuConstants::GENERAL_BEGIN; i <= MainCursor; i++) { TryItemTimer(i); } @@ -7939,7 +8028,7 @@ void Client::TryItemTimer(int slot) } ++it_iter; } - + if(slot > EmuConstants::EQUIPMENT_END) { return; } @@ -7987,8 +8076,9 @@ void Client::RefundAA() { } if(refunded) { + SaveAA(); Save(); - Kick(); + // Kick(); } } @@ -8003,7 +8093,7 @@ void Client::IncrementAA(int aa_id) { SetAA(aa_id, GetAA(aa_id) + 1); - Save(); + SaveAA(); SendAA(aa_id); SendAATable(); @@ -8020,7 +8110,7 @@ void Client::SendItemScale(ItemInst *inst) { } } -void Client::AddRespawnOption(std::string option_name, uint32 zoneid, float x, float y, float z, float heading, bool initial_selection, int8 position) +void Client::AddRespawnOption(std::string option_name, uint32 zoneid, uint16 instance_id, float x, float y, float z, float heading, bool initial_selection, int8 position) { //If respawn window is already open, any changes would create an inconsistency with the client if (IsHoveringForRespawn()) { return; } @@ -8031,7 +8121,8 @@ void Client::AddRespawnOption(std::string option_name, uint32 zoneid, float x, f //Create respawn option RespawnOption res_opt; res_opt.name = option_name; - res_opt.zoneid = zoneid; + res_opt.zone_id = zoneid; + res_opt.instance_id = instance_id; res_opt.x = x; res_opt.y = y; res_opt.z = z; @@ -8168,9 +8259,9 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_ { if(!item) { return; } - uint16 cons_mod = 180; + uint32 cons_mod = 180; - int16 metabolism_bonus = spellbonuses.Metabolism + itembonuses.Metabolism + aabonuses.Metabolism; + int32 metabolism_bonus = spellbonuses.Metabolism + itembonuses.Metabolism + aabonuses.Metabolism; if (metabolism_bonus) cons_mod = cons_mod * metabolism_bonus * RuleI(Character, ConsumptionMultiplier) / 10000; @@ -8244,28 +8335,67 @@ 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])); - } +void Client::ShowNumHits() +{ + uint32 buffcount = GetMaxTotalSlots(); + for (uint32 buffslot = 0; buffslot < buffcount; buffslot++) { + const Buffs_Struct &curbuff = buffs[buffslot]; + if (curbuff.spellid != SPELL_UNKNOWN && curbuff.numhits) + Message(0, "You have %d hits left on %s", curbuff.numhits, GetSpellName(curbuff.spellid)); + } + return; +} + +float Client::GetQuiverHaste() +{ + float quiver_haste = 0; + for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) { + const ItemInst *pi = GetInv().GetItem(r); + if (!pi) + continue; + if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) { + float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv)); + quiver_haste = std::max(temp_wr, quiver_haste); + } + } + if (quiver_haste > 0) + quiver_haste = 1.0f / (1.0f + static_cast(quiver_haste) / 100.0f); + return quiver_haste; +} + +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); +} - mysql_free_result(result); - -} \ No newline at end of file diff --git a/zone/client.h b/zone/client.h index 718efbb6e..4c69917fd 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1,4 +1,4 @@ -/* EQEMu: Everquest Server Emulator +/* EQEMu: Everquest Server Emulator Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.org) This program is free software; you can redistribute it and/or modify @@ -24,28 +24,29 @@ class Client; #include "../common/emu_opcodes.h" #include "../common/eq_packet_structs.h" #include "../common/eq_constants.h" -#include "../common/EQStreamIntf.h" -#include "../common/EQPacket.h" +#include "../common/eq_stream_intf.h" +#include "../common/eq_packet.h" #include "../common/linked_list.h" #include "../common/extprofile.h" #include "../common/classes.h" #include "../common/races.h" #include "../common/deity.h" #include "../common/seperator.h" -#include "../common/Item.h" +#include "../common/item.h" #include "../common/guilds.h" #include "../common/item_struct.h" #include "../common/clientversions.h" +#include "common.h" #include "zonedb.h" #include "errno.h" #include "mob.h" #include "npc.h" #include "merc.h" #include "zone.h" -#include "AA.h" +#include "aa.h" #include "questmgr.h" -#include "QGlobals.h" +#include "qglobals.h" #ifdef _WINDOWS // since windows defines these within windef.h (which windows.h include) @@ -60,10 +61,10 @@ class Client; #include -#define CLIENT_TIMEOUT 90000 -#define CLIENT_LD_TIMEOUT 30000 // length of time client stays in zone after LDing -#define TARGETING_RANGE 200 // range for /assist and /target -#define XTARGET_HARDCAP 20 +#define CLIENT_TIMEOUT 90000 +#define CLIENT_LD_TIMEOUT 30000 // length of time client stays in zone after LDing +#define TARGETING_RANGE 200 // range for /assist and /target +#define XTARGET_HARDCAP 20 extern Zone* zone; extern TaskManager *taskmanager; @@ -77,46 +78,41 @@ public: bool ack_req; }; -enum { //Type arguments to the Message* routines. +enum { //Type arguments to the Message* routines. //all not explicitly listed are the same grey color clientMessageWhite0 = 0, - clientMessageLoot = 2, //dark green - clientMessageTradeskill = 4, //light blue - clientMessageTell = 5, //magenta + clientMessageLoot = 2, //dark green + clientMessageTradeskill = 4, //light blue + clientMessageTell = 5, //magenta clientMessageWhite = 7, clientMessageWhite2 = 10, clientMessageLightGrey = 12, - clientMessageError = 13, //red + clientMessageError = 13, //red clientMessageGreen = 14, clientMessageYellow = 15, clientMessageBlue = 16, - clientMessageGroup = 18, //cyan + clientMessageGroup = 18, //cyan clientMessageWhite3 = 20, }; #define SPELLBAR_UNLOCK 0x2bc -enum { //scribing argument to MemorizeSpell +enum { //scribing argument to MemorizeSpell memSpellScribing = 0, memSpellMemorize = 1, memSpellForget = 2, 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 - GMSummon, // Always send ZonePlayerToBind_Struct to client: Only a GM Summon - ZoneToBindPoint, // Always send ZonePlayerToBind_Struct to client: Death Only - ZoneSolicited, // Always send ZonePlayerToBind_Struct to client: Portal, Translocate, Evac spells that have a x y z coord in the spell data + ZoneToSafeCoords, // Always send ZonePlayerToBind_Struct to client: Succor/Evac + GMSummon, // Always send ZonePlayerToBind_Struct to client: Only a GM Summon + ZoneToBindPoint, // Always send ZonePlayerToBind_Struct to client: Death Only + ZoneSolicited, // Always send ZonePlayerToBind_Struct to client: Portal, Translocate, Evac spells that have a x y z coord in the spell data ZoneUnsolicited, - GateToBindPoint, // Always send RequestClientZoneChange_Struct to client: Gate spell or Translocate To Bind Point spell - SummonPC, // In-zone GMMove() always: Call of the Hero spell or some other type of in zone only summons - Rewind, // Summon to /rewind location. + GateToBindPoint, // Always send RequestClientZoneChange_Struct to client: Gate spell or Translocate To Bind Point spell + SummonPC, // In-zone GMMove() always: Call of the Hero spell or some other type of in zone only summons + Rewind, // Summon to /rewind location. EvacToSafeCoords } ZoneMode; @@ -181,7 +177,8 @@ struct XTarget_Struct struct RespawnOption { std::string name; - uint32 zoneid; + uint32 zone_id; + uint16 instance_id; float x; float y; float z; @@ -211,184 +208,190 @@ public: Client(EQStreamInterface * ieqs); ~Client(); - //abstract virtual function implementations requird by base abstract class + //abstract virtual function implementations required by base abstract class virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill); virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, - ExtraAttackOptions *opts = nullptr); + ExtraAttackOptions *opts = nullptr); virtual bool HasRaid() { return (GetRaid() ? true : false); } virtual bool HasGroup() { return (GetGroup() ? true : false); } virtual Raid* GetRaid() { return entity_list.GetRaidByClient(this); } virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); } virtual inline bool IsBerserk() { return berserk; } virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating); + virtual void SetAttackTimer(); + float GetQuiverHaste(); - void AI_Init(); - void AI_Start(uint32 iMoveDelay = 0); - void AI_Stop(); - void AI_Process(); - void AI_SpellCast(); - void Trader_ShowItems(); - void Trader_CustomerBrowsing(Client *Customer); - void Trader_EndTrader(); - void Trader_StartTrader(); - uint8 WithCustomer(uint16 NewCustomer); - void KeyRingLoad(); - void KeyRingAdd(uint32 item_id); - bool KeyRingCheck(uint32 item_id); - void KeyRingList(); + void AI_Init(); + void AI_Start(uint32 iMoveDelay = 0); + void AI_Stop(); + void AI_Process(); + void AI_SpellCast(); + void Trader_ShowItems(); + void Trader_CustomerBrowsing(Client *Customer); + void Trader_EndTrader(); + void Trader_StartTrader(); + uint8 WithCustomer(uint16 NewCustomer); + void KeyRingLoad(); + void KeyRingAdd(uint32 item_id); + 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); - void SendBuyerPacket(Client* Buyer); + void CompleteConnect(); + bool TryStacking(ItemInst* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true); + void SendTraderPacket(Client* trader, uint32 Unknown72 = 51); + void SendBuyerPacket(Client* Buyer); GetItems_Struct* GetTraderItems(); - void SendBazaarWelcome(); - void DyeArmor(DyeStruct* dye); - uint8 SlotConvert(uint8 slot,bool bracer=false); - void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0); - void Message_StringID(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0); - bool FilteredMessageCheck(Mob *sender, eqFilterType filter); - void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id); - void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, - uint32 string_id, const char *message1, const char *message2 = nullptr, - const char *message3 = nullptr, const char *message4 = nullptr, - const char *message5 = nullptr, const char *message6 = nullptr, - const char *message7 = nullptr, const char *message8 = nullptr, - const char *message9 = nullptr); - 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); + void SendBazaarWelcome(); + void DyeArmor(DyeStruct* dye); + uint8 SlotConvert(uint8 slot,bool bracer=false); + void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0); + void Message_StringID(uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0); + bool FilteredMessageCheck(Mob *sender, eqFilterType filter); + void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, uint32 string_id); + void FilteredMessage_StringID(Mob *sender, uint32 type, eqFilterType filter, + uint32 string_id, const char *message1, const char *message2 = nullptr, + const char *message3 = nullptr, const char *message4 = nullptr, + 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); ItemInst* FindTraderItemBySerialNumber(int32 SerialNumber); - void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot); - void NukeTraderItem(uint16 slot,int16 charges,uint16 quantity,Client* customer,uint16 traderslot, int uniqueid); - void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges); - void TradeRequestFailed(const EQApplicationPacket* app); - void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app); - void TraderUpdate(uint16 slot_id,uint32 trader_id); - void FinishTrade(Mob* with, ServerPacket* qspack = nullptr, bool finalizer = false); - void SendZonePoints(); + void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot); + void NukeTraderItem(uint16 slot,int16 charges,uint16 quantity,Client* customer,uint16 traderslot, int uniqueid); + void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges); + void TradeRequestFailed(const EQApplicationPacket* app); + void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app); + void TraderUpdate(uint16 slot_id,uint32 trader_id); + void FinishTrade(Mob* with, bool finalizer = false, void* event_entry = nullptr, std::list* event_details = nullptr); + void SendZonePoints(); - void SendBuyerResults(char *SearchQuery, uint32 SearchID); - void ShowBuyLines(const EQApplicationPacket *app); - void SellToBuyer(const EQApplicationPacket *app); - void ToggleBuyerMode(bool TurnOn); - void UpdateBuyLine(const EQApplicationPacket *app); - void BuyerItemSearch(const EQApplicationPacket *app); - void SetBuyerWelcomeMessage(const char* WelcomeMessage) { BuyerWelcomeMessage = WelcomeMessage; } - const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); } + void SendBuyerResults(char *SearchQuery, uint32 SearchID); + void ShowBuyLines(const EQApplicationPacket *app); + void SellToBuyer(const EQApplicationPacket *app); + void ToggleBuyerMode(bool TurnOn); + void UpdateBuyLine(const EQApplicationPacket *app); + void BuyerItemSearch(const EQApplicationPacket *app); + void SetBuyerWelcomeMessage(const char* WelcomeMessage) { BuyerWelcomeMessage = WelcomeMessage; } + const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); } - void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); virtual bool Process(); - void LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const Item_Struct* item, bool buying); - void SendPacketQueue(bool Block = true); - void QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone); - void FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL); - void ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname=nullptr); - void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...); - void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); - void Message(uint32 type, const char* message, ...); - void QuestJournalledMessage(const char *npcname, const char* message); - void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber); - void SendSound(); - void LearnRecipe(uint32 recipeID); - bool CanIncreaseTradeskill(SkillUseTypes tradeskill); + void LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32 price, const Item_Struct* item, bool buying); + void SendPacketQueue(bool Block = true); + void QueuePacket(const EQApplicationPacket* app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL, eqFilterType filter=FilterNone); + void FastQueuePacket(EQApplicationPacket** app, bool ack_req = true, CLIENT_CONN_STATUS = CLIENT_CONNECTINGALL); + void ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_skill, const char* orig_message, const char* targetname=nullptr); + void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, const char* message, ...); + void ChannelMessageSend(const char* from, const char* to, uint8 chan_num, uint8 language, uint8 lang_skill, const char* message, ...); + void Message(uint32 type, const char* message, ...); + void QuestJournalledMessage(const char *npcname, const char* message); + void VoiceMacroReceived(uint32 Type, char *Target, uint32 MacroNumber); + void SendSound(); + void LearnRecipe(uint32 recipeID); + bool CanIncreaseTradeskill(SkillUseTypes tradeskill); - EQApplicationPacket* ReturnItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); + EQApplicationPacket* ReturnItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); - bool GetRevoked() const { return revoked; } - void SetRevoked(bool rev) { revoked = rev; } - inline uint32 GetIP() const { return ip; } - inline bool GetHideMe() const { return gmhideme; } - void SetHideMe(bool hm); - inline uint16 GetPort() const { return port; } - bool IsDead() const { return(dead); } - bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); } - inline bool IsLFP() { return LFP; } - void UpdateLFP(); + bool GetRevoked() const { return revoked; } + void SetRevoked(bool rev) { revoked = rev; } + inline uint32 GetIP() const { return ip; } + inline bool GetHideMe() const { return gmhideme; } + void SetHideMe(bool hm); + inline uint16 GetPort() const { return port; } + bool IsDead() const { return(dead); } + bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); } + inline bool IsLFP() { return LFP; } + void UpdateLFP(); - virtual bool Save() { return Save(0); } - bool Save(uint8 iCommitNow); // 0 = delayed, 1=async now, 2=sync now - void SaveBackup(); + virtual bool Save() { return Save(0); } + 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); } - inline void Kick() { client_state = CLIENT_KICKED; } - inline void Disconnect() { eqs->Close(); client_state = DISCONNECTED; } - inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); } - void WorldKick(); - inline uint8 GetAnon() const { return m_pp.anon; } - inline PlayerProfile_Struct& GetPP() { return m_pp; } - inline ExtendedProfile_Struct& GetEPP() { return m_epp; } - inline Inventory& GetInv() { return m_inv; } - inline const Inventory& GetInv() const { return m_inv; } - inline PetInfo* GetPetInfo(uint16 pet) { return (pet==1)?&m_suspendedminion:&m_petinfo; } + inline bool Connected() const { return (client_state == CLIENT_CONNECTED); } + inline bool InZone() const { return (client_state == CLIENT_CONNECTED || client_state == CLIENT_LINKDEAD); } + inline void Kick() { client_state = CLIENT_KICKED; } + inline void Disconnect() { eqs->Close(); client_state = DISCONNECTED; } + inline bool IsLD() const { return (bool) (client_state == CLIENT_LINKDEAD); } + void WorldKick(); + inline uint8 GetAnon() const { return m_pp.anon; } + inline PlayerProfile_Struct& GetPP() { return m_pp; } + inline ExtendedProfile_Struct& GetEPP() { return m_epp; } + inline Inventory& GetInv() { return m_inv; } + inline const Inventory& GetInv() const { return m_inv; } + inline PetInfo* GetPetInfo(uint16 pet) { return (pet==1)?&m_suspendedminion:&m_petinfo; } inline InspectMessage_Struct& GetInspectMessage() { return m_inspect_message; } inline const InspectMessage_Struct& GetInspectMessage() const { return m_inspect_message; } - bool CheckAccess(int16 iDBLevel, int16 iDefaultLevel); + bool CheckAccess(int16 iDBLevel, int16 iDefaultLevel); - void CheckQuests(const char* zonename, const char* message, uint32 npc_id, uint32 item_id, Mob* other); - void LogLoot(Client* player,Corpse* corpse,const Item_Struct* item); - bool AutoAttackEnabled() const { return auto_attack; } - bool AutoFireEnabled() const { return auto_fire; } - void MakeCorpse(uint32 exploss); + void CheckQuests(const char* zonename, const char* message, uint32 npc_id, uint32 item_id, Mob* other); + bool AutoAttackEnabled() const { return auto_attack; } + bool AutoFireEnabled() const { return auto_fire; } + void MakeCorpse(uint32 exploss); - bool ChangeFirstName(const char* in_firstname,const char* gmname); + bool ChangeFirstName(const char* in_firstname,const char* gmname); - void Duck(); - void Stand(); + void Duck(); + void Stand(); - virtual void SetMaxHP(); - int32 LevelRegen(); - void HPTick(); - void SetGM(bool toggle); - void SetPVP(bool toggle); + virtual void SetMaxHP(); + int32 LevelRegen(); + void HPTick(); + void SetGM(bool toggle); + void SetPVP(bool toggle); - inline bool GetPVP() const { return zone->GetZoneID() == 77 ? true : (m_pp.pvp != 0); } - inline bool GetGM() const { return m_pp.gm != 0; } + inline bool GetPVP() const { return zone->GetZoneID() == 77 ? true : (m_pp.pvp != 0); } + inline bool GetGM() const { return m_pp.gm != 0; } - inline void SetBaseClass(uint32 i) { m_pp.class_=i; } - inline void SetBaseRace(uint32 i) { m_pp.race=i; } - inline void SetBaseGender(uint32 i) { m_pp.gender=i; } + inline void SetBaseClass(uint32 i) { m_pp.class_=i; } + inline void SetBaseRace(uint32 i) { m_pp.race=i; } + inline void SetBaseGender(uint32 i) { m_pp.gender=i; } inline void SetDeity(uint32 i) {m_pp.deity=i;deity=i;} - inline uint8 GetLevel2() const { return m_pp.level2; } - inline uint16 GetBaseRace() const { return m_pp.race; } - inline uint16 GetBaseClass() const { return m_pp.class_; } - inline uint8 GetBaseGender() const { return m_pp.gender; } - inline uint8 GetBaseFace() const { return m_pp.face; } - inline uint8 GetBaseHairColor() const { return m_pp.haircolor; } - inline uint8 GetBaseBeardColor() const { return m_pp.beardcolor; } - inline uint8 GetBaseEyeColor() const { return m_pp.eyecolor1; } - inline uint8 GetBaseHairStyle() const { return m_pp.hairstyle; } - inline uint8 GetBaseBeard() const { return m_pp.beard; } - inline uint8 GetBaseHeritage() const { return m_pp.drakkin_heritage; } - inline uint8 GetBaseTattoo() const { return m_pp.drakkin_tattoo; } - inline uint8 GetBaseDetails() const { return m_pp.drakkin_details; } - inline const float GetBindX(uint32 index = 0) const { return m_pp.binds[index].x; } - inline const float GetBindY(uint32 index = 0) const { return m_pp.binds[index].y; } - inline const float GetBindZ(uint32 index = 0) const { return m_pp.binds[index].z; } - inline const float GetBindHeading(uint32 index = 0) const { return m_pp.binds[index].heading; } - inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zoneId; } - int32 CalcMaxMana(); - int32 CalcBaseMana(); - const int32& SetMana(int32 amount); - int32 CalcManaRegenCap(); + inline uint8 GetLevel2() const { return m_pp.level2; } + inline uint16 GetBaseRace() const { return m_pp.race; } + inline uint16 GetBaseClass() const { return m_pp.class_; } + inline uint8 GetBaseGender() const { return m_pp.gender; } + inline uint8 GetBaseFace() const { return m_pp.face; } + inline uint8 GetBaseHairColor() const { return m_pp.haircolor; } + inline uint8 GetBaseBeardColor() const { return m_pp.beardcolor; } + inline uint8 GetBaseEyeColor() const { return m_pp.eyecolor1; } + inline uint8 GetBaseHairStyle() const { return m_pp.hairstyle; } + inline uint8 GetBaseBeard() const { return m_pp.beard; } + inline uint8 GetBaseHeritage() const { return m_pp.drakkin_heritage; } + inline uint8 GetBaseTattoo() const { return m_pp.drakkin_tattoo; } + inline uint8 GetBaseDetails() const { return m_pp.drakkin_details; } + inline const float GetBindX(uint32 index = 0) const { return m_pp.binds[index].x; } + inline const float GetBindY(uint32 index = 0) const { return m_pp.binds[index].y; } + inline const float GetBindZ(uint32 index = 0) const { return m_pp.binds[index].z; } + inline const float GetBindHeading(uint32 index = 0) const { return m_pp.binds[index].heading; } + inline uint32 GetBindZoneID(uint32 index = 0) const { return m_pp.binds[index].zoneId; } + inline uint32 GetBindInstanceID(uint32 index = 0) const { return m_pp.binds[index].instance_id; } + int32 CalcMaxMana(); + int32 CalcBaseMana(); + const int32& SetMana(int32 amount); + int32 CalcManaRegenCap(); - void ServerFilter(SetServerFilter_Struct* filter); - void BulkSendTraderInventory(uint32 char_id); - void SendSingleTraderItem(uint32 char_id, int uniqueid); - void BulkSendMerchantInventory(int merchant_id, int npcid); + void ServerFilter(SetServerFilter_Struct* filter); + void BulkSendTraderInventory(uint32 char_id); + void SendSingleTraderItem(uint32 char_id, int uniqueid); + void BulkSendMerchantInventory(int merchant_id, int npcid); - inline uint8 GetLanguageSkill(uint16 n) const { return m_pp.languages[n]; } + inline uint8 GetLanguageSkill(uint16 n) const { return m_pp.languages[n]; } - void SendPickPocketResponse(Mob *from, uint32 amt, int type, const Item_Struct* item = nullptr); + void SendPickPocketResponse(Mob *from, uint32 amt, int type, const Item_Struct* item = nullptr); - inline const char* GetLastName() const { return lastname; } + inline const char* GetLastName() const { return lastname; } inline float ProximityX() const { return(proximity_x); } inline float ProximityY() const { return(proximity_y); } @@ -396,91 +399,91 @@ public: inline void ClearAllProximities() { entity_list.ProcessMove(this, FLT_MAX, FLT_MAX, FLT_MAX); proximity_x = FLT_MAX; proximity_y = FLT_MAX; proximity_z = FLT_MAX; } /* - Begin client modifiers + Begin client modifiers */ virtual void CalcBonuses(); //these are all precalculated now - inline virtual int16 GetAC() const { return AC; } - inline virtual int16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(SkillOffense)) * 9 / 10); } - inline virtual int16 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } - inline virtual int GetHaste() const { return Haste; } + inline virtual int32 GetAC() const { return AC; } + inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(SkillOffense)) * 9 / 10); } + inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } + inline virtual int GetHaste() const { return Haste; } int GetRawACNoShield(int &shield_ac) const; - inline virtual int16 GetSTR() const { return STR; } - inline virtual int16 GetSTA() const { return STA; } - inline virtual int16 GetDEX() const { return DEX; } - inline virtual int16 GetAGI() const { return AGI; } - inline virtual int16 GetINT() const { return INT; } - inline virtual int16 GetWIS() const { return WIS; } - inline virtual int16 GetCHA() const { return CHA; } - inline virtual int16 GetMR() const { return MR; } - inline virtual int16 GetFR() const { return FR; } - inline virtual int16 GetDR() const { return DR; } - inline virtual int16 GetPR() const { return PR; } - inline virtual int16 GetCR() const { return CR; } - inline virtual int16 GetCorrup() const { return Corrup; } + inline virtual int32 GetSTR() const { return STR; } + inline virtual int32 GetSTA() const { return STA; } + inline virtual int32 GetDEX() const { return DEX; } + inline virtual int32 GetAGI() const { return AGI; } + inline virtual int32 GetINT() const { return INT; } + inline virtual int32 GetWIS() const { return WIS; } + inline virtual int32 GetCHA() const { return CHA; } + inline virtual int32 GetMR() const { return MR; } + inline virtual int32 GetFR() const { return FR; } + inline virtual int32 GetDR() const { return DR; } + inline virtual int32 GetPR() const { return PR; } + inline virtual int32 GetCR() const { return CR; } + inline virtual int32 GetCorrup() const { return Corrup; } - int16 GetMaxStat() const; - int16 GetMaxResist() const; - int16 GetMaxSTR() const; - int16 GetMaxSTA() const; - int16 GetMaxDEX() const; - int16 GetMaxAGI() const; - int16 GetMaxINT() const; - int16 GetMaxWIS() const; - int16 GetMaxCHA() const; - int16 GetMaxMR() const; - int16 GetMaxPR() const; - int16 GetMaxDR() const; - int16 GetMaxCR() const; - int16 GetMaxFR() const; - int16 GetMaxCorrup() const; - inline uint8 GetBaseSTR() const { return m_pp.STR; } - inline uint8 GetBaseSTA() const { return m_pp.STA; } - inline uint8 GetBaseCHA() const { return m_pp.CHA; } - inline uint8 GetBaseDEX() const { return m_pp.DEX; } - inline uint8 GetBaseINT() const { return m_pp.INT; } - inline uint8 GetBaseAGI() const { return m_pp.AGI; } - inline uint8 GetBaseWIS() const { return m_pp.WIS; } - inline uint8 GetBaseCorrup() const { return 15; } // Same for all + int32 GetMaxStat() const; + int32 GetMaxResist() const; + int32 GetMaxSTR() const; + int32 GetMaxSTA() const; + int32 GetMaxDEX() const; + int32 GetMaxAGI() const; + int32 GetMaxINT() const; + int32 GetMaxWIS() const; + int32 GetMaxCHA() const; + int32 GetMaxMR() const; + int32 GetMaxPR() const; + int32 GetMaxDR() const; + int32 GetMaxCR() const; + int32 GetMaxFR() const; + int32 GetMaxCorrup() const; + inline uint8 GetBaseSTR() const { return m_pp.STR; } + inline uint8 GetBaseSTA() const { return m_pp.STA; } + inline uint8 GetBaseCHA() const { return m_pp.CHA; } + inline uint8 GetBaseDEX() const { return m_pp.DEX; } + inline uint8 GetBaseINT() const { return m_pp.INT; } + inline uint8 GetBaseAGI() const { return m_pp.AGI; } + inline uint8 GetBaseWIS() const { return m_pp.WIS; } + inline uint8 GetBaseCorrup() const { return 15; } // Same for all - inline virtual int16 GetHeroicSTR() const { return itembonuses.HeroicSTR; } - inline virtual int16 GetHeroicSTA() const { return itembonuses.HeroicSTA; } - inline virtual int16 GetHeroicDEX() const { return itembonuses.HeroicDEX; } - inline virtual int16 GetHeroicAGI() const { return itembonuses.HeroicAGI; } - inline virtual int16 GetHeroicINT() const { return itembonuses.HeroicINT; } - inline virtual int16 GetHeroicWIS() const { return itembonuses.HeroicWIS; } - inline virtual int16 GetHeroicCHA() const { return itembonuses.HeroicCHA; } - inline virtual int16 GetHeroicMR() const { return itembonuses.HeroicMR; } - inline virtual int16 GetHeroicFR() const { return itembonuses.HeroicFR; } - inline virtual int16 GetHeroicDR() const { return itembonuses.HeroicDR; } - inline virtual int16 GetHeroicPR() const { return itembonuses.HeroicPR; } - inline virtual int16 GetHeroicCR() const { return itembonuses.HeroicCR; } - inline virtual int16 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } + inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } + inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } + inline virtual int32 GetHeroicDEX() const { return itembonuses.HeroicDEX; } + inline virtual int32 GetHeroicAGI() const { return itembonuses.HeroicAGI; } + inline virtual int32 GetHeroicINT() const { return itembonuses.HeroicINT; } + inline virtual int32 GetHeroicWIS() const { return itembonuses.HeroicWIS; } + inline virtual int32 GetHeroicCHA() const { return itembonuses.HeroicCHA; } + inline virtual int32 GetHeroicMR() const { return itembonuses.HeroicMR; } + inline virtual int32 GetHeroicFR() const { return itembonuses.HeroicFR; } + inline virtual int32 GetHeroicDR() const { return itembonuses.HeroicDR; } + inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } + inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } + inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } // Mod2 - inline virtual int16 GetShielding() const { return itembonuses.MeleeMitigation; } - inline virtual int16 GetSpellShield() const { return itembonuses.SpellShield; } - inline virtual int16 GetDoTShield() const { return itembonuses.DoTShielding; } - inline virtual int16 GetStunResist() const { return itembonuses.StunResist; } - inline virtual int16 GetStrikeThrough() const { return itembonuses.StrikeThrough; } - inline virtual int16 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } - inline virtual int16 GetAccuracy() const { return itembonuses.HitChance; } - inline virtual int16 GetCombatEffects() const { return itembonuses.ProcChance; } - inline virtual int16 GetDS() const { return itembonuses.DamageShield; } + inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } + inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } + inline virtual int32 GetDoTShield() const { return itembonuses.DoTShielding; } + inline virtual int32 GetStunResist() const { return itembonuses.StunResist; } + inline virtual int32 GetStrikeThrough() const { return itembonuses.StrikeThrough; } + inline virtual int32 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } + inline virtual int32 GetAccuracy() const { return itembonuses.HitChance; } + inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } + inline virtual int32 GetDS() const { return itembonuses.DamageShield; } // Mod3 - inline virtual int16 GetHealAmt() const { return itembonuses.HealAmt; } - inline virtual int16 GetSpellDmg() const { return itembonuses.SpellDmg; } - inline virtual int16 GetClair() const { return itembonuses.Clairvoyance; } - inline virtual int16 GetDSMit() const { return itembonuses.DSMitigation; } + inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } + inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } + inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } + inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } - inline virtual int16 GetSingMod() const { return itembonuses.singingMod; } - inline virtual int16 GetBrassMod() const { return itembonuses.brassMod; } - inline virtual int16 GetPercMod() const { return itembonuses.percussionMod; } - inline virtual int16 GetStringMod() const { return itembonuses.stringedMod; } - inline virtual int16 GetWindMod() const { return itembonuses.windMod; } + inline virtual int32 GetSingMod() const { return itembonuses.singingMod; } + inline virtual int32 GetBrassMod() const { return itembonuses.brassMod; } + inline virtual int32 GetPercMod() const { return itembonuses.percussionMod; } + inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } + inline virtual int32 GetWindMod() const { return itembonuses.windMod; } - inline virtual int16 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } + inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false); int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr); @@ -502,33 +505,33 @@ public: virtual void InitializeBuffSlots(); virtual void UninitializeBuffSlots(); - inline const int32 GetBaseHP() const { return base_hp; } + inline const int32 GetBaseHP() const { return base_hp; } uint32 GetWeight() const { return(weight); } inline void RecalcWeight() { weight = CalcCurrentWeight(); } uint32 CalcCurrentWeight(); - inline uint32 GetCopper() const { return m_pp.copper; } - inline uint32 GetSilver() const { return m_pp.silver; } - inline uint32 GetGold() const { return m_pp.gold; } - inline uint32 GetPlatinum() const { return m_pp.platinum; } + inline uint32 GetCopper() const { return m_pp.copper; } + inline uint32 GetSilver() const { return m_pp.silver; } + inline uint32 GetGold() const { return m_pp.gold; } + inline uint32 GetPlatinum() const { return m_pp.platinum; } /*Endurance and such*/ - void CalcMaxEndurance(); //This calculates the maximum endurance we can have - int32 CalcBaseEndurance(); //Calculates Base End - int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() - int32 GetEndurance() const {return cur_end;} //This gets our current endurance - int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call - int32 CalcEnduranceRegenCap(); - int32 CalcHPRegenCap(); + void CalcMaxEndurance(); //This calculates the maximum endurance we can have + int32 CalcBaseEndurance(); //Calculates Base End + int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() + int32 GetEndurance() const {return cur_end;} //This gets our current endurance + int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call + int32 CalcEnduranceRegenCap(); + int32 CalcHPRegenCap(); inline uint8 GetEndurancePercent() { return (uint8)((float)cur_end / (float)max_end * 100.0f); } - void SetEndurance(int32 newEnd); //This sets the current endurance to the new value - void DoEnduranceRegen(); //This Regenerates endurance - void DoEnduranceUpkeep(); //does the endurance upkeep + void SetEndurance(int32 newEnd); //This sets the current endurance to the new value + void DoEnduranceRegen(); //This Regenerates endurance + void DoEnduranceUpkeep(); //does the endurance upkeep //This calculates total Attack Rating to match very close to what the client should show - uint16 GetTotalATK(); - uint16 GetATKRating(); + uint32 GetTotalATK(); + uint32 GetATKRating(); //This gets the skill value of the item type equiped in the Primary Slot uint16 GetPrimarySkillValue(); @@ -536,160 +539,167 @@ public: bool Rampage(); void DurationRampage(uint32 duration); - inline uint32 GetEXP() const { return m_pp.exp; } + inline uint32 GetEXP() const { return m_pp.exp; } - bool UpdateLDoNPoints(int32 points, uint32 theme); - void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } - uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; } - void AddPVPPoints(uint32 Points); - uint32 GetRadiantCrystals() { return m_pp.currentRadCrystals; } - void SetRadiantCrystals(uint32 Crystals) { m_pp.currentRadCrystals = Crystals; } - uint32 GetEbonCrystals() { return m_pp.currentEbonCrystals; } - void SetEbonCrystals(uint32 Crystals) { m_pp.currentEbonCrystals = Crystals; } - void AddCrystals(uint32 Radiant, uint32 Ebon); - void SendCrystalCounts(); + bool UpdateLDoNPoints(int32 points, uint32 theme); + void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } + uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; } + void AddPVPPoints(uint32 Points); + uint32 GetRadiantCrystals() { return m_pp.currentRadCrystals; } + void SetRadiantCrystals(uint32 Crystals) { m_pp.currentRadCrystals = Crystals; } + uint32 GetEbonCrystals() { return m_pp.currentEbonCrystals; } + void SetEbonCrystals(uint32 Crystals) { m_pp.currentEbonCrystals = Crystals; } + void AddCrystals(uint32 Radiant, uint32 Ebon); + void SendCrystalCounts(); - void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); - void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); - void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0); - void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp); - void AddLeadershipEXP(uint32 group_exp, uint32 raid_exp); - void SendLeadershipEXPUpdate(); - bool IsLeadershipEXPOn(); - inline int GetLeadershipAA(int AAID) { return m_pp.leader_abilities.ranks[AAID]; } - int GroupLeadershipAAHealthEnhancement(); - int GroupLeadershipAAManaEnhancement(); - int GroupLeadershipAAHealthRegeneration(); - int GroupLeadershipAAOffenseEnhancement(); - void InspectBuffs(Client* Inspector, int Rank); - uint32 GetRaidPoints() { return(m_pp.raid_leadership_points); } - uint32 GetGroupPoints() { return(m_pp.group_leadership_points); } - uint32 GetRaidEXP() { return(m_pp.raid_leadership_exp); } - uint32 GetGroupEXP() { return(m_pp.group_leadership_exp); } - uint32 GetTotalSecondsPlayed() { return(TotalSecondsPlayed); } + void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); + void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); + void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0); + void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp); + void AddLeadershipEXP(uint32 group_exp, uint32 raid_exp); + 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(); + int GroupLeadershipAAOffenseEnhancement(); + void InspectBuffs(Client* Inspector, int Rank); + uint32 GetRaidPoints() { return(m_pp.raid_leadership_points); } + uint32 GetGroupPoints() { return(m_pp.group_leadership_points); } + uint32 GetRaidEXP() { return(m_pp.raid_leadership_exp); } + uint32 GetGroupEXP() { return(m_pp.group_leadership_exp); } + uint32 GetTotalSecondsPlayed() { return(TotalSecondsPlayed); } virtual void SetLevel(uint8 set_level, bool command = false); - void GoToBind(uint8 bindnum = 0); - void GoToSafeCoords(uint16 zone_id, uint16 instance_id); - void Gate(); - void SetBindPoint(int to_zone = -1, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f); - void SetStartZone(uint32 zoneid, float x = 0.0f, float y =0.0f, float z = 0.0f); - uint32 GetStartZone(void); - void MovePC(const char* zonename, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void MovePC(float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void MovePC(uint32 zoneID, uint32 instanceID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - void AssignToInstance(uint16 instance_id); - void WhoAll(); - bool CheckLoreConflict(const Item_Struct* item); - void ChangeLastName(const char* in_lastname); - void GetGroupAAs(GroupLeadershipAA_Struct *into) const; - void ClearGroupAAs(); - void UpdateGroupAAs(int32 points, uint32 type); - void SacrificeConfirm(Client* caster); - void Sacrifice(Client* caster); - void GoToDeath(); - inline const int32 GetInstanceID() const { return zone->GetInstanceID(); } - FACTION_VALUE GetReverseFactionCon(Mob* iOther); - FACTION_VALUE GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc); - int32 GetCharacterFactionLevel(int32 faction_id); - int32 GetModCharacterFactionLevel(int32 faction_id); - bool HatedByClass(uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction); - void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalvalue, uint8 temp); + void GoToBind(uint8 bindnum = 0); + void GoToSafeCoords(uint16 zone_id, uint16 instance_id); + void Gate(); + void SetBindPoint(int to_zone = -1, int to_instance = 0, float new_x = 0.0f, float new_y = 0.0f, float new_z = 0.0f); + void SetStartZone(uint32 zoneid, float x = 0.0f, float y =0.0f, float z = 0.0f); + uint32 GetStartZone(void); + void MovePC(const char* zonename, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); + void MovePC(uint32 zoneID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); + void MovePC(float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); + void MovePC(uint32 zoneID, uint32 instanceID, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); + void AssignToInstance(uint16 instance_id); + void RemoveFromInstance(uint16 instance_id); + void WhoAll(); + 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); + void Sacrifice(Client* caster); + void GoToDeath(); + inline const int32 GetInstanceID() const { return zone->GetInstanceID(); } - void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity); - void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp); - int16 GetRawItemAC(); - uint16 GetCombinedAC_TEST(); + FACTION_VALUE GetReverseFactionCon(Mob* iOther); + FACTION_VALUE GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc); + int32 GetCharacterFactionLevel(int32 faction_id); + int32 GetModCharacterFactionLevel(int32 faction_id); + void MerchantRejectMessage(Mob *merchant, int primaryfaction); + void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 totalvalue, uint8 temp); + + void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity); + void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp); + int32 GetRawItemAC(); + uint16 GetCombinedAC_TEST(); + + inline uint32 LSAccountID() const { return lsaccountid; } + inline uint32 GetWID() const { return WID; } + inline void SetWID(uint32 iWID) { WID = iWID; } + inline uint32 AccountID() const { return account_id; } - inline uint32 LSAccountID() const { return lsaccountid; } - inline uint32 GetWID() const { return WID; } - inline void SetWID(uint32 iWID) { WID = iWID; } - inline uint32 AccountID() const { return account_id; } inline const char* AccountName()const { return account_name; } - inline int16 Admin() const { return admin; } - inline uint32 CharacterID() const { return character_id; } - void UpdateAdmin(bool iFromDB = true); - void UpdateWho(uint8 remove = 0); - bool GMHideMe(Client* client = 0); + inline int16 Admin() const { return admin; } + inline uint32 CharacterID() const { return character_id; } + void UpdateAdmin(bool iFromDB = true); + void UpdateWho(uint8 remove = 0); + bool GMHideMe(Client* client = 0); inline bool IsInAGuild() const { return(guild_id != GUILD_NONE && guild_id != 0); } inline bool IsInGuild(uint32 in_gid) const { return(in_gid == guild_id && IsInAGuild()); } - inline uint32 GuildID() const { return guild_id; } - inline uint8 GuildRank() const { return guildrank; } - void SendGuildMOTD(bool GetGuildMOTDReply = false); - void SendGuildURL(); - void SendGuildChannel(); - void SendGuildSpawnAppearance(); - void SendGuildRanks(); - void SendGuildMembers(); - void SendGuildList(); - void SendGuildJoin(GuildJoin_Struct* gj); - void RefreshGuildInfo(); + inline uint32 GuildID() const { return guild_id; } + inline uint8 GuildRank() const { return guildrank; } + void SendGuildMOTD(bool GetGuildMOTDReply = false); + void SendGuildURL(); + void SendGuildChannel(); + void SendGuildSpawnAppearance(); + void SendGuildRanks(); + void SendGuildMembers(); + void SendGuildList(); + void SendGuildJoin(GuildJoin_Struct* gj); + void RefreshGuildInfo(); - void SendManaUpdatePacket(); - void SendManaUpdate(); - void SendEnduranceUpdate(); - uint8 GetFace() const { return m_pp.face; } - void WhoAll(Who_All_Struct* whom); - void FriendsWho(char *FriendsString); + void SendManaUpdatePacket(); + void SendManaUpdate(); + void SendEnduranceUpdate(); + uint8 GetFace() const { return m_pp.face; } + void WhoAll(Who_All_Struct* whom); + void FriendsWho(char *FriendsString); - void Stun(int duration); - void UnStun(); - void ReadBook(BookRequest_Struct *book); - void QuestReadBook(const char* text, uint8 type); - void SendClientMoneyUpdate(uint8 type,uint32 amount); - void SendMoneyUpdate(); - bool TakeMoneyFromPP(uint64 copper, bool updateclient=false); - void AddMoneyToPP(uint64 copper,bool updateclient); - void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold,uint32 platinum,bool updateclient); - bool HasMoney(uint64 copper); - uint64 GetCarriedMoney(); - uint64 GetAllMoney(); + void Stun(int duration); + void UnStun(); + void ReadBook(BookRequest_Struct *book); + void QuestReadBook(const char* text, uint8 type); + void SendClientMoneyUpdate(uint8 type,uint32 amount); + void SendMoneyUpdate(); + bool TakeMoneyFromPP(uint64 copper, bool updateclient=false); + void AddMoneyToPP(uint64 copper,bool updateclient); + void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold,uint32 platinum,bool updateclient); + bool HasMoney(uint64 copper); + uint64 GetCarriedMoney(); + uint64 GetAllMoney(); - bool IsDiscovered(uint32 itemid); - void DiscoverItem(uint32 itemid); + bool IsDiscovered(uint32 itemid); + void DiscoverItem(uint32 itemid); - bool TGB() const { return tgb; } + bool TGB() const { return tgb; } - void OnDisconnect(bool hard_disconnect); + 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; } - 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; - void SetSkill(SkillUseTypes skill_num, uint16 value); - void AddSkill(SkillUseTypes skillid, uint16 value); - void CheckSpecializeIncrease(uint16 spell_id); - void CheckSongSkillIncrease(uint16 spell_id); - bool CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi = 0); - void CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill); - void SetLanguageSkill(int langid, int value); - void SetHoTT(uint32 mobid); - void ShowSkillsWindow(); - void SendStatsWindow(Client* client, bool use_window); + 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; } + 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; + void SetSkill(SkillUseTypes skill_num, uint16 value); + void AddSkill(SkillUseTypes skillid, uint16 value); + void CheckSpecializeIncrease(uint16 spell_id); + void CheckSongSkillIncrease(uint16 spell_id); + bool CheckIncreaseSkill(SkillUseTypes skillid, Mob *against_who, int chancemodi = 0); + void CheckLanguageSkillIncrease(uint8 langid, uint8 TeacherSkill); + void SetLanguageSkill(int langid, int value); + void SetHoTT(uint32 mobid); + void ShowSkillsWindow(); + void SendStatsWindow(Client* client, bool use_window); - uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; - inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } - uint8 SkillTrainLevel(SkillUseTypes skillid, uint16 class_); + uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; + 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); void GMKill(); - inline bool IsMedding() const {return medding;} + inline bool IsMedding() const {return medding;} inline uint16 GetDuelTarget() const { return duel_target; } - inline bool IsDueling() const { return duelaccepted; } - inline void SetDuelTarget(uint16 set_id) { duel_target=set_id; } - inline void SetDueling(bool duel) { duelaccepted = duel; } + inline bool IsDueling() const { return duelaccepted; } + inline void SetDuelTarget(uint16 set_id) { duel_target=set_id; } + inline void SetDueling(bool duel) { duelaccepted = duel; } // use this one instead void MemSpell(uint16 spell_id, int slot, bool update_client = true); void UnmemSpell(int slot, bool update_client = true); @@ -699,14 +709,14 @@ public: void UnscribeSpellAll(bool update_client = true); void UntrainDisc(int slot, bool update_client = true); void UntrainDiscAll(bool update_client = true); - bool SpellGlobalCheck(uint16 Spell_ID, uint16 Char_ID); + bool SpellGlobalCheck(uint16 Spell_ID, uint32 Char_ID); uint32 GetCharMaxLevelFromQGlobal(); - inline bool IsSitting() const {return (playeraction == 1);} - inline bool IsBecomeNPC() const { return npcflag; } - inline uint8 GetBecomeNPCLevel() const { return npclevel; } - inline void SetBecomeNPC(bool flag) { npcflag = flag; } - inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; } + inline bool IsSitting() const {return (playeraction == 1);} + inline bool IsBecomeNPC() const { return npcflag; } + inline uint8 GetBecomeNPCLevel() const { return npclevel; } + inline void SetBecomeNPC(bool flag) { npcflag = flag; } + inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; } void SetFeigned(bool in_feigned); /// this cures timing issues cuz dead animation isn't done but server side feigning is? inline bool GetFeigned() const { return(feigned); } @@ -714,7 +724,7 @@ public: #ifdef PACKET_PROFILER void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); } #endif - uint32 GetEquipment(uint8 material_slot) const; // returns item id + uint32 GetEquipment(uint8 material_slot) const; // returns item id uint32 GetEquipmentColor(uint8 material_slot) const; inline bool AutoSplitEnabled() { return m_pp.autosplit != 0; } @@ -728,17 +738,17 @@ public: bool BindWound(Mob* bindmob, bool start, bool fail = false); void SetTradeskillObject(Object* object) { m_tradeskill_object = object; } Object* GetTradeskillObject() { return m_tradeskill_object; } - void SendTributes(); - void SendGuildTributes(); - void DoTributeUpdate(); - void SendTributeDetails(uint32 client_id, uint32 tribute_id); - int32 TributeItem(uint32 slot, uint32 quantity); - int32 TributeMoney(uint32 platinum); - void AddTributePoints(int32 ammount); - void ChangeTributeSettings(TributeInfo_Struct *t); - void SendTributeTimer(); - void ToggleTribute(bool enabled); - void SendPathPacket(std::vector &path); + void SendTributes(); + void SendGuildTributes(); + void DoTributeUpdate(); + void SendTributeDetails(uint32 client_id, uint32 tribute_id); + int32 TributeItem(uint32 slot, uint32 quantity); + int32 TributeMoney(uint32 platinum); + void AddTributePoints(int32 ammount); + void ChangeTributeSettings(TributeInfo_Struct *t); + void SendTributeTimer(); + void ToggleTribute(bool enabled); + void SendPathPacket(std::vector &path); inline PTimerList &GetPTimers() { return(p_timers); } @@ -750,9 +760,9 @@ public: void BuyAA(AA_Action* action); //this function is used by some AA stuff void MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing); - void SetAATitle(const char *Title); - void SetTitleSuffix(const char *txt); - inline uint32 GetMaxAAXP(void) const { return max_AAXP; } + void SetAATitle(const char *Title); + void SetTitleSuffix(const char *txt); + inline uint32 GetMaxAAXP(void) const { return max_AAXP; } inline uint32 GetAAXP() const { return m_pp.expAA; } void SendAAStats(); void SendAATable(); @@ -780,96 +790,98 @@ public: int32 GetAAEffectid(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, true, false,false); } int32 GetAABase1(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, true,false); } int32 GetAABase2(uint32 aa_ID, uint32 slot_id) { return GetAAEffectDataBySlot(aa_ID, slot_id, false, false,true); } - int16 acmod(); + int32 acmod(); // Item methods - uint32 NukeItem(uint32 itemnum, uint8 where_to_check = - (invWhereWorn | invWherePersonal | invWhereBank | invWhereSharedBank | invWhereTrading | invWhereCursor)); - void SetTint(int16 slot_id, uint32 color); - void SetTint(int16 slot_id, Color_Struct& color); - void SetMaterial(int16 slot_id, uint32 item_id); - void Undye(); - int32 GetItemIDAt(int16 slot_id); - int32 GetAugmentIDAt(int16 slot_id, uint8 augslot); - bool PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update = false); - bool PushItemOnCursor(const ItemInst& inst, bool client_update = false); - void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true); - bool SwapItem(MoveItem_Struct* move_in); - void SwapItemResync(MoveItem_Struct* move_slots); - void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); - void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0); - bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0); - bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, bool attuned = false, uint16 to_slot = MainCursor); - void SetStats(uint8 type,int16 set_val); - void IncStats(uint8 type,int16 increase_val); - void DropItem(int16 slot_id); - bool MakeItemLink(char* &ret_link, const ItemInst* inst); - int GetItemLinkHash(const ItemInst* inst); - void SendItemLink(const ItemInst* inst, bool sendtoall=false); - void SendLootItemInPacket(const ItemInst* inst, int16 slot_id); - void SendItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); - bool IsValidSlot(uint32 slot); - bool IsBankSlot(uint32 slot); + 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); + void SetTint(int16 slot_id, Color_Struct& color); + void SetMaterial(int16 slot_id, uint32 item_id); + void Undye(); + int32 GetItemIDAt(int16 slot_id); + int32 GetAugmentIDAt(int16 slot_id, uint8 augslot); + bool PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update = false); + bool PushItemOnCursor(const ItemInst& inst, bool client_update = false); + void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true); + bool SwapItem(MoveItem_Struct* move_in); + void SwapItemResync(MoveItem_Struct* move_slots); + void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false); + void PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootItem_Struct** bag_item_data = 0); + bool AutoPutLootInInventory(ItemInst& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0); + bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, bool attuned = false, uint16 to_slot = MainCursor, uint32 ornament_icon = 0, uint32 ornament_idfile = 0); + void SetStats(uint8 type,int16 set_val); + void IncStats(uint8 type,int16 increase_val); + void DropItem(int16 slot_id); + bool MakeItemLink(char* &ret_link, const ItemInst* inst); + int GetItemLinkHash(const ItemInst* inst); + void SendItemLink(const ItemInst* inst, bool sendtoall=false); + void SendLootItemInPacket(const ItemInst* inst, int16 slot_id); + void SendItemPacket(int16 slot_id, const ItemInst* inst, ItemPacketType packet_type); + bool IsValidSlot(uint32 slot); + bool IsBankSlot(uint32 slot); - inline bool IsTrader() const { return(Trader); } - inline bool IsBuyer() const { return(Buyer); } - eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; } - void SetFilter(eqFilterType filter_id, eqFilterMode value) { ClientFilters[filter_id]=value; } + inline bool IsTrader() const { return(Trader); } + inline bool IsBuyer() const { return(Buyer); } + eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; } + void SetFilter(eqFilterType filter_id, eqFilterMode value) { ClientFilters[filter_id]=value; } - void BreakInvis(); - void LeaveGroup(); + void BreakInvis(); + void LeaveGroup(); - bool Hungry() const {if (GetGM()) return false; return m_pp.hunger_level <= 3000;} - bool Thirsty() const {if (GetGM()) return false; return m_pp.thirst_level <= 3000;} - int32 GetHunger() const { return m_pp.hunger_level; } - int32 GetThirst() const { return m_pp.thirst_level; } - void SetHunger(int32 in_hunger); - void SetThirst(int32 in_thirst); - void SetConsumption(int32 in_hunger, int32 in_thirst); + bool Hungry() const {if (GetGM()) return false; return m_pp.hunger_level <= 3000;} + bool Thirsty() const {if (GetGM()) return false; return m_pp.thirst_level <= 3000;} +int32 GetHunger() const { return m_pp.hunger_level; } +int32 GetThirst() const { return m_pp.thirst_level; } +void SetHunger(int32 in_hunger); +void SetThirst(int32 in_thirst); +void SetConsumption(int32 in_hunger, int32 in_thirst); - bool CheckTradeLoreConflict(Client* other); - void LinkDead(); - void Insight(uint32 t_id); - bool CheckDoubleAttack(bool tripleAttack = false); - bool CheckDoubleRangedAttack(); + bool CheckTradeLoreConflict(Client* other); + void LinkDead(); + void Insight(uint32 t_id); + bool CheckDoubleAttack(bool tripleAttack = false); + bool CheckDoubleRangedAttack(); //remove charges/multiple objects from inventory: - //bool DecreaseByType(uint32 type, uint8 amt); - bool DecreaseByID(uint32 type, uint8 amt); - uint8 SlotConvert2(uint8 slot); //Maybe not needed. - void Escape(); //AA Escape - void RemoveNoRent(bool client_update = true); - void RemoveDuplicateLore(bool client_update = true); - void MoveSlotNotAllowed(bool client_update = true); - virtual void RangedAttack(Mob* other, bool CanDoubleAttack = false); - virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false); - void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false); + //bool DecreaseByType(uint32 type, uint8 amt); + bool DecreaseByID(uint32 type, uint8 amt); + uint8 SlotConvert2(uint8 slot); //Maybe not needed. + void Escape(); //AA Escape + void RemoveNoRent(bool client_update = true); + void RemoveDuplicateLore(bool client_update = true); + void MoveSlotNotAllowed(bool client_update = true); + virtual void RangedAttack(Mob* other, bool CanDoubleAttack = false); + virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false); + void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false); - void SetZoneFlag(uint32 zone_id); - void ClearZoneFlag(uint32 zone_id); - bool HasZoneFlag(uint32 zone_id) const; - void SendZoneFlagInfo(Client *to) const; - void LoadZoneFlags(); + void SetZoneFlag(uint32 zone_id); + void ClearZoneFlag(uint32 zone_id); + bool HasZoneFlag(uint32 zone_id) const; + void SendZoneFlagInfo(Client *to) const; + void LoadZoneFlags(); - void ChangeSQLLog(const char *file); - void LogSQL(const char *fmt, ...); - bool CanFish(); - void GoFish(); - void ForageItem(bool guarantee = false); + void ChangeSQLLog(const char *file); + void LogSQL(const char *fmt, ...); + bool CanFish(); + void GoFish(); + void ForageItem(bool guarantee = false); //Calculate vendor price modifier based on CHA: (reverse==selling) - float CalcPriceMod(Mob* other = 0, bool reverse = false); - void ResetTrade(); - void DropInst(const ItemInst* inst); - bool TrainDiscipline(uint32 itemid); - void SendDisciplineUpdate(); - bool UseDiscipline(uint32 spell_id, uint32 target); + float CalcPriceMod(Mob* other = 0, bool reverse = false); + void ResetTrade(); + void DropInst(const ItemInst* inst); + bool TrainDiscipline(uint32 itemid); + void SendDisciplineUpdate(); + void SendDisciplineTimer(uint32 timer_id, uint32 duration); + bool UseDiscipline(uint32 spell_id, uint32 target); - bool CheckTitle(int titleset); - void EnableTitle(int titleset); - void RemoveTitle(int titleset); + bool CheckTitle(int titleset); + void EnableTitle(int titleset); + void RemoveTitle(int titleset); - void EnteringMessages(Client* client); - void SendRules(Client* client); + void EnteringMessages(Client* client); + void SendRules(Client* client); std::list consent_list; //Anti-Cheat Stuff @@ -893,99 +905,66 @@ 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); } - int FindSpellBookSlotBySpellID(uint16 spellid); - int GetNextAvailableSpellBookSlot(int starting_slot = 0); + void ProcessInspectRequest(Client* requestee, Client* requester); + bool ClientFinishedLoading() { return (conn_state == ClientConnectFinished); } + int FindSpellBookSlotBySpellID(uint16 spellid); + int GetNextAvailableSpellBookSlot(int starting_slot = 0); inline uint32 GetSpellByBookSlot(int book_slot) { return m_pp.spell_book[book_slot]; } inline bool HasSpellScribed(int spellid) { return (FindSpellBookSlotBySpellID(spellid) != -1 ? true : false); } - uint16 GetMaxSkillAfterSpecializationRules(SkillUseTypes skillid, uint16 maxSkill); - void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0); - void SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...); - bool PendingTranslocate; - time_t TranslocateTime; - bool PendingSacrifice; - std::string SacrificeCaster; - struct Translocate_Struct PendingTranslocateData; - void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID); + uint16 GetMaxSkillAfterSpecializationRules(SkillUseTypes skillid, uint16 maxSkill); + void SendPopupToClient(const char *Title, const char *Text, uint32 PopupID = 0, uint32 Buttons = 0, uint32 Duration = 0); + void SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...); + bool PendingTranslocate; + time_t TranslocateTime; + bool PendingSacrifice; + std::string SacrificeCaster; + PendingTranslocate_Struct PendingTranslocateData; + void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID); // Task System Methods - void LoadClientTaskState(); - void RemoveClientTaskState(); - void SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex, int TaskIncomplete=1); - void SendTaskFailed(int TaskID, int TaskIndex); - void SendTaskComplete(int TaskIndex); + void LoadClientTaskState(); + void RemoveClientTaskState(); + void SendTaskActivityComplete(int TaskID, int ActivityID, int TaskIndex, int TaskIncomplete=1); + void SendTaskFailed(int TaskID, int TaskIndex); + void SendTaskComplete(int TaskIndex); inline void CancelTask(int TaskIndex) { if(taskstate) taskstate->CancelTask(this, TaskIndex); } - inline bool SaveTaskState() { return (taskmanager ? taskmanager->SaveClientState(this, taskstate) : false); } - inline bool IsTaskStateLoaded() { return taskstate != nullptr; } - inline bool IsTaskActive(int TaskID) { return (taskstate ? taskstate->IsTaskActive(TaskID) : false); } - inline bool IsTaskActivityActive(int TaskID, int ActivityID) { return (taskstate ? taskstate->IsTaskActivityActive(TaskID, ActivityID) : false); } - inline ActivityState GetTaskActivityState(int index, int ActivityID) { return (taskstate ? taskstate->GetTaskActivityState(index, ActivityID) : ActivityHidden); } - inline void UpdateTaskActivity(int TaskID, int ActivityID, int Count) { if(taskstate) taskstate->UpdateTaskActivity(this, TaskID, ActivityID, Count); } - inline void ResetTaskActivity(int TaskID, int ActivityID) { if(taskstate) taskstate->ResetTaskActivity(this, TaskID, ActivityID); } - inline void UpdateTasksOnKill(int NPCTypeID) { if(taskstate) taskstate->UpdateTasksOnKill(this, NPCTypeID); } - inline void UpdateTasksForItem(ActivityType Type, int ItemID, int Count=1) { if(taskstate) taskstate->UpdateTasksForItem(this, Type, ItemID, Count); } - inline void UpdateTasksOnExplore(int ExploreID) { if(taskstate) taskstate->UpdateTasksOnExplore(this, ExploreID); } - inline bool UpdateTasksOnSpeakWith(int NPCTypeID) { if(taskstate) return taskstate->UpdateTasksOnSpeakWith(this, NPCTypeID); else return false; } - inline bool UpdateTasksOnDeliver(uint32 *Items, int Cash, int NPCTypeID) { if(taskstate) return taskstate->UpdateTasksOnDeliver(this, Items, Cash, NPCTypeID); else return false; } - inline void TaskSetSelector(Mob *mob, int TaskSetID) { if(taskmanager) taskmanager->TaskSetSelector(this, taskstate, mob, TaskSetID); } - inline void EnableTask(int TaskCount, int *TaskList) { if(taskstate) taskstate->EnableTask(CharacterID(), TaskCount, TaskList); } - inline void DisableTask(int TaskCount, int *TaskList) { if(taskstate) taskstate->DisableTask(CharacterID(), TaskCount, TaskList); } - inline bool IsTaskEnabled(int TaskID) { return (taskstate ? taskstate->IsTaskEnabled(TaskID) : false); } - inline void ProcessTaskProximities(float X, float Y, float Z) { if(taskstate) taskstate->ProcessTaskProximities(this, X, Y, Z); } - inline void AssignTask(int TaskID, int NPCID) { if(taskstate) taskstate->AcceptNewTask(this, TaskID, NPCID); } - inline int ActiveSpeakTask(int NPCID) { if(taskstate) return taskstate->ActiveSpeakTask(NPCID); else return 0; } - inline int ActiveSpeakActivity(int NPCID, int TaskID) { if(taskstate) return taskstate->ActiveSpeakActivity(NPCID, TaskID); else return 0; } - inline void FailTask(int TaskID) { if(taskstate) taskstate->FailTask(this, TaskID); } - inline int TaskTimeLeft(int TaskID) { return (taskstate ? taskstate->TaskTimeLeft(TaskID) : 0); } - inline int EnabledTaskCount(int TaskSetID) { return (taskstate ? taskstate->EnabledTaskCount(TaskSetID) : -1); } - inline int IsTaskCompleted(int TaskID) { return (taskstate ? taskstate->IsTaskCompleted(TaskID) : -1); } - inline void ShowClientTasks() { if(taskstate) taskstate->ShowClientTasks(this); } - inline void CancelAllTasks() { if(taskstate) taskstate->CancelAllTasks(this); } - inline int GetActiveTaskCount() { return (taskstate ? taskstate->GetActiveTaskCount() : 0); } - inline int GetActiveTaskID(int index) { return (taskstate ? taskstate->GetActiveTaskID(index) : -1); } - inline int GetTaskStartTime(int index) { return (taskstate ? taskstate->GetTaskStartTime(index) : -1); } - inline bool IsTaskActivityCompleted(int index, int ActivityID) { return (taskstate ? taskstate->IsTaskActivityCompleted(index, ActivityID) : false); } - inline int GetTaskActivityDoneCount(int ClientTaskIndex, int ActivityID) { return (taskstate ? taskstate->GetTaskActivityDoneCount(ClientTaskIndex, ActivityID) :0); } - inline int GetTaskActivityDoneCountFromTaskID(int TaskID, int ActivityID) { return (taskstate ? taskstate->GetTaskActivityDoneCountFromTaskID(TaskID, ActivityID) :0); } - inline int ActiveTasksInSet(int TaskSet) { return (taskstate ? taskstate->ActiveTasksInSet(TaskSet) :0); } - inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); } inline const EQClientVersion GetClientVersion() const { return ClientVersion; } @@ -1038,7 +1017,7 @@ public: void HandleLDoNSenseTraps(NPC *target, uint16 skill, uint8 type); void HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type); void HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type); - int LDoNChest_SkillCheck(NPC *target, int skill); + int LDoNChest_SkillCheck(NPC *target, int skill); void MarkSingleCompassLoc(float in_x, float in_y, float in_z, uint8 count=1); @@ -1051,9 +1030,9 @@ public: void DepopAllCorpses(); void DepopPlayerCorpse(uint32 dbid); void BuryPlayerCorpses(); - uint32 GetCorpseCount() { return database.GetPlayerCorpseCount(CharacterID()); } - uint32 GetCorpseID(int corpse) { return database.GetPlayerCorpseID(CharacterID(), corpse); } - uint32 GetCorpseItemAt(int corpse_id, int slot_id) { return database.GetPlayerCorpseItemAt(corpse_id, slot_id); } + uint32 GetCorpseCount() { return database.GetCharacterCorpseCount(CharacterID()); } + uint32 GetCorpseID(int corpse) { return database.GetCharacterCorpseID(CharacterID(), corpse); } + uint32 GetCorpseItemAt(int corpse_id, int slot_id) { return database.GetCharacterCorpseItemAt(corpse_id, slot_id); } void SuspendMinion(); void Doppelganger(uint16 spell_id, Mob *target, const char *name_override, int pet_count, int pet_duration); void NotifyNewTitlesAvailable(); @@ -1078,11 +1057,11 @@ public: inline bool GetPendingGuildInvitation() { return PendingGuildInvitation; } void LocateCorpse(); void SendTargetCommand(uint32 EntityID); - bool MoveItemToInventory(ItemInst *BInst, bool UpdateClient = false); + bool MoveItemToInventory(ItemInst *BInst, bool UpdateClient = false); void HandleRespawnFromHover(uint32 Option); bool IsHoveringForRespawn() { return RespawnFromHoverTimer.Enabled(); } std::list respawn_options; - void AddRespawnOption(std::string option_name, uint32 zoneid, float x, float y, float z, float h = 0, bool initial_selection = false, int8 position = -1); + void AddRespawnOption(std::string option_name, uint32 zoneid, uint16 instance_id, float x, float y, float z, float h = 0, bool initial_selection = false, int8 position = -1); bool RemoveRespawnOption(std::string option_name); bool RemoveRespawnOption(uint8 position); void ClearRespawnOptions() { respawn_options.clear(); } @@ -1097,7 +1076,7 @@ public: inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } void SendAltCurrencies(); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); - void AddAlternateCurrencyValue(uint32 currency_id, int32 amount); + void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); void SendAlternateCurrencyValues(); void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true); uint32 GetAlternateCurrencyValue(uint32 currency_id) const; @@ -1119,13 +1098,17 @@ public: void RemoveGroupXTargets(); void RemoveAutoXTargets(); void ShowXTargets(Client *c); + bool GroupFollow(Client* inviter); + void InitializeMercInfo(); + bool CheckCanSpawnMerc(uint32 template_id); bool CheckCanHireMerc(Mob* merchant, uint32 template_id); bool CheckCanRetainMerc(uint32 upkeep); bool CheckCanUnsuspendMerc(); - bool CheckCanDismissMerc(); - inline uint32 GetMercID() const { return mercid; } - inline uint8 GetMercSlot() const { return mercSlot; } + bool DismissMerc(uint32 MercID); + bool MercOnlyOrNoGroup(); + inline uint32 GetMercID() const { return mercid; } + inline uint8 GetMercSlot() const { return mercSlot; } void SetMercID( uint32 newmercid) { mercid = newmercid; } void SetMercSlot( uint8 newmercslot) { mercSlot = newmercslot; } Merc* GetMerc(); @@ -1133,9 +1116,11 @@ public: MercInfo& GetMercInfo() { return m_mercinfo[mercSlot]; } uint8 GetNumMercs(); void SetMerc(Merc* newmerc); + void SendMercResponsePackets(uint32 ResponseType); void SendMercMerchantResponsePacket(int32 response_type); void SendMercenaryUnknownPacket(uint8 type); void SendMercenaryUnsuspendPacket(uint8 type); + void SendMercTimer(Merc* merc = nullptr); void SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspended_time, int32 update_interval = 900000, int32 unk01 = 180000); void SendMercSuspendResponsePacket(uint32 suspended_time); void SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02); @@ -1148,10 +1133,12 @@ public: void UpdateMercLevel(); void CheckMercSuspendTimer(); Timer* GetMercTimer() { return &merc_timer; }; + const char* GetRacePlural(Client* client); 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); @@ -1162,16 +1149,16 @@ public: void TryItemTimer(int slot); void SendItemScale(ItemInst *inst); - int16 GetActSTR() { return( std::min(GetMaxSTR(), GetSTR()) ); } - int16 GetActSTA() { return( std::min(GetMaxSTA(), GetSTA()) ); } - int16 GetActDEX() { return( std::min(GetMaxDEX(), GetDEX()) ); } - int16 GetActAGI() { return( std::min(GetMaxAGI(), GetAGI()) ); } - int16 GetActINT() { return( std::min(GetMaxINT(), GetINT()) ); } - int16 GetActWIS() { return( std::min(GetMaxWIS(), GetWIS()) ); } - int16 GetActCHA() { return( std::min(GetMaxCHA(), GetCHA()) ); } + int32 GetActSTR() { return( std::min(GetMaxSTR(), GetSTR()) ); } + int32 GetActSTA() { return( std::min(GetMaxSTA(), GetSTA()) ); } + int32 GetActDEX() { return( std::min(GetMaxDEX(), GetDEX()) ); } + int32 GetActAGI() { return( std::min(GetMaxAGI(), GetAGI()) ); } + int32 GetActINT() { return( std::min(GetMaxINT(), GetINT()) ); } + int32 GetActWIS() { return( std::min(GetMaxWIS(), GetWIS()) ); } + int32 GetActCHA() { return( std::min(GetMaxCHA(), GetCHA()) ); } void LoadAccountFlags(); void SetAccountFlag(std::string flag, std::string val); - std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillUseTypes); + std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillUseTypes); void Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_consume); void PlayMP3(const char* fname); void ExpeditionSay(const char *str, int ExpID); @@ -1194,13 +1181,20 @@ public: int32 mod_client_xp(int32 in_exp, NPC *npc); uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level); int mod_client_haste_cap(int cap); - int mod_consume(Item_Struct *item, ItemUseTypes type, int change); - int mod_food_value(const Item_Struct *item, int change); - int mod_drink_value(const Item_Struct *item, int change); + int mod_consume(Item_Struct *item, ItemUseTypes type, int change); + int mod_food_value(const Item_Struct *item, int change); + int mod_drink_value(const Item_Struct *item, int change); void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; } bool GetEngagedRaidTarget() const { return EngagedRaidTarget; } - + + void ShowNumHits(); // work around function for numhits not showing on buffs + + void TripInterrogateInvState() { interrogateinv_flag = true; } + bool GetInterrogateInvState() { return interrogateinv_flag; } + + bool InterrogateInventory(Client* requester, bool log, bool silent, bool allowtrip, bool& error, bool autolog = true); + protected: friend class Mob; void CalcItemBonuses(StatBonuses* newbon); @@ -1212,10 +1206,10 @@ protected: void MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message = true); bool client_data_loaded; - int16 GetFocusEffect(focusType type, uint16 spell_id); - int16 GetSympatheticFocusEffect(focusType type, uint16 spell_id); + int16 GetFocusEffect(focusType type, uint16 spell_id); + int16 GetSympatheticFocusEffect(focusType type, uint16 spell_id); - Mob* bind_sight_target; + Mob* bind_sight_target; Map::Vertex aa_los_me; Map::Vertex aa_los_them; @@ -1240,52 +1234,52 @@ protected: private: eqFilterMode ClientFilters[_FilterCount]; - int32 HandlePacket(const EQApplicationPacket *app); - void OPTGB(const EQApplicationPacket *app); - void OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 InstanceID, float x, float y, float z); - void OPMemorizeSpell(const EQApplicationPacket *app); - void OPMoveCoin(const EQApplicationPacket* app); - void MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type); - void OPGMTraining(const EQApplicationPacket *app); - void OPGMEndTraining(const EQApplicationPacket *app); - void OPGMTrainSkill(const EQApplicationPacket *app); - void OPGMSummon(const EQApplicationPacket *app); - void OPCombatAbility(const EQApplicationPacket *app); + int32 HandlePacket(const EQApplicationPacket *app); + void OPTGB(const EQApplicationPacket *app); + void OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 InstanceID, float x, float y, float z); + void OPMemorizeSpell(const EQApplicationPacket *app); + void OPMoveCoin(const EQApplicationPacket* app); + void MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type); + void OPGMTraining(const EQApplicationPacket *app); + void OPGMEndTraining(const EQApplicationPacket *app); + void OPGMTrainSkill(const EQApplicationPacket *app); + void OPGMSummon(const EQApplicationPacket *app); + void OPCombatAbility(const EQApplicationPacket *app); // Bandolier Methods - void CreateBandolier(const EQApplicationPacket *app); - void RemoveBandolier(const EQApplicationPacket *app); - void SetBandolier(const EQApplicationPacket *app); + void CreateBandolier(const EQApplicationPacket *app); + void RemoveBandolier(const EQApplicationPacket *app); + void SetBandolier(const EQApplicationPacket *app); - void HandleTraderPriceUpdate(const EQApplicationPacket *app); + void HandleTraderPriceUpdate(const EQApplicationPacket *app); - int16 CalcAC(); - int16 GetACMit(); - int16 GetACAvoid(); - int16 CalcATK(); - int CalcHaste(); + int32 CalcAC(); + int32 GetACMit(); + int32 GetACAvoid(); + int32 CalcATK(); + int32 CalcHaste(); - int16 CalcAlcoholPhysicalEffect(); - int16 CalcSTR(); - int16 CalcSTA(); - int16 CalcDEX(); - int16 CalcAGI(); - int16 CalcINT(); - int16 CalcWIS(); - int16 CalcCHA(); + int32 CalcAlcoholPhysicalEffect(); + int32 CalcSTR(); + int32 CalcSTA(); + int32 CalcDEX(); + int32 CalcAGI(); + int32 CalcINT(); + int32 CalcWIS(); + int32 CalcCHA(); - int16 CalcMR(); - int16 CalcFR(); - int16 CalcDR(); - int16 CalcPR(); - int16 CalcCR(); - int16 CalcCorrup(); - int32 CalcMaxHP(); - int32 CalcBaseHP(); - int32 CalcHPRegen(); - int32 CalcManaRegen(); - int32 CalcBaseManaRegen(); - uint32 GetClassHPFactor(); + int32 CalcMR(); + int32 CalcFR(); + int32 CalcDR(); + int32 CalcPR(); + int32 CalcCR(); + int32 CalcCorrup(); + int32 CalcMaxHP(); + int32 CalcBaseHP(); + int32 CalcHPRegen(); + int32 CalcManaRegen(); + int32 CalcBaseManaRegen(); + uint32 GetClassHPFactor(); void DoHPRegen(); void DoManaRegen(); void DoStaminaUpdate(); @@ -1297,75 +1291,75 @@ private: EQStreamInterface* eqs; - uint32 ip; - uint16 port; - CLIENT_CONN_STATUS client_state; - uint32 character_id; - uint32 WID; - uint32 account_id; - char account_name[30]; - uint32 lsaccountid; - char lskey[30]; - int16 admin; - uint32 guild_id; - uint8 guildrank; // player's rank in the guild, 0-GUILD_MAX_RANK - bool GuildBanker; - uint16 duel_target; - bool duelaccepted; + uint32 ip; + uint16 port; + CLIENT_CONN_STATUS client_state; + uint32 character_id; + uint32 WID; + uint32 account_id; + char account_name[30]; + uint32 lsaccountid; + char lskey[30]; + int16 admin; + uint32 guild_id; + uint8 guildrank; // player's rank in the guild, 0-GUILD_MAX_RANK + bool GuildBanker; + uint16 duel_target; + bool duelaccepted; std::list keyring; - bool tellsoff; // GM /toggle - bool gmhideme; - bool LFG; - bool LFP; - uint8 LFGFromLevel; - uint8 LFGToLevel; - bool LFGMatchFilter; - char LFGComments[64]; - bool AFK; - bool auto_attack; - bool auto_fire; - uint8 gmspeed; - bool medding; - uint16 horseId; - bool revoked; - uint32 pQueuedSaveWorkID; - uint16 pClientSideTarget; - uint32 weight; - bool berserk; - bool dead; - uint16 BoatID; - uint16 TrackingID; - uint16 CustomerID; - uint32 account_creation; - uint8 firstlogon; - uint32 mercid; // current merc - uint8 mercSlot; // selected merc slot - bool Trader; - bool Buyer; - std::string BuyerWelcomeMessage; - bool AbilityTimer; + bool tellsoff; // GM /toggle + bool gmhideme; + bool LFG; + bool LFP; + uint8 LFGFromLevel; + uint8 LFGToLevel; + bool LFGMatchFilter; + char LFGComments[64]; + bool AFK; + bool auto_attack; + bool auto_fire; + uint8 gmspeed; + bool medding; + uint16 horseId; + bool revoked; + uint32 pQueuedSaveWorkID; + uint16 pClientSideTarget; + uint32 weight; + bool berserk; + bool dead; + uint16 BoatID; + uint16 TrackingID; + uint16 CustomerID; + uint32 account_creation; + uint8 firstlogon; + uint32 mercid; // current merc + uint8 mercSlot; // selected merc slot + bool Trader; + bool Buyer; + std::string BuyerWelcomeMessage; + bool AbilityTimer; int Haste; //precalced value - int32 max_end; - int32 cur_end; + int32 max_end; + int32 cur_end; - PlayerProfile_Struct m_pp; - ExtendedProfile_Struct m_epp; - Inventory m_inv; - Object* m_tradeskill_object; - PetInfo m_petinfo; // current pet data, used while loading from and saving to DB - PetInfo m_suspendedminion; // pet data for our suspended minion. - MercInfo m_mercinfo[MAXMERCS]; // current mercenary - InspectMessage_Struct m_inspect_message; + PlayerProfile_Struct m_pp; + ExtendedProfile_Struct m_epp; + Inventory m_inv; + Object* m_tradeskill_object; + PetInfo m_petinfo; // current pet data, used while loading from and saving to DB + PetInfo m_suspendedminion; // pet data for our suspended minion. + MercInfo m_mercinfo[MAXMERCS]; // current mercenary + InspectMessage_Struct m_inspect_message; void NPCSpawn(const Seperator* sep); uint32 GetEXPForLevel(uint16 level); - bool CanBeInZone(); - void SendLogoutPackets(); - bool AddPacket(const EQApplicationPacket *, bool); - bool AddPacket(EQApplicationPacket**, bool); - bool SendAllPackets(); + bool CanBeInZone(); + void SendLogoutPackets(); + bool AddPacket(const EQApplicationPacket *, bool); + bool AddPacket(EQApplicationPacket**, bool); + bool SendAllPackets(); LinkedList clientpackets; //Zoning related stuff @@ -1374,62 +1368,62 @@ private: void DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instance_id, float dest_x, float dest_y, float dest_z, float dest_h, int8 ignore_r); void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm); void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited); - float zonesummon_x; - float zonesummon_y; - float zonesummon_z; - uint16 zonesummon_id; - uint8 zonesummon_ignorerestrictions; + float zonesummon_x; + float zonesummon_y; + float zonesummon_z; + uint16 zonesummon_id; + uint8 zonesummon_ignorerestrictions; ZoneMode zone_mode; - Timer position_timer; - uint8 position_timer_counter; + Timer position_timer; + uint8 position_timer_counter; - PTimerList p_timers; //persistent timers - Timer hpupdate_timer; - Timer camp_timer; - Timer process_timer; - Timer stamina_timer; - Timer zoneinpacket_timer; - Timer linkdead_timer; - Timer dead_timer; - Timer global_channel_timer; - Timer shield_timer; - Timer fishing_timer; - Timer endupkeep_timer; - Timer forget_timer; // our 2 min everybody forgets you timer - Timer autosave_timer; + PTimerList p_timers; //persistent timers + Timer hpupdate_timer; + Timer camp_timer; + Timer process_timer; + Timer stamina_timer; + Timer zoneinpacket_timer; + Timer linkdead_timer; + Timer dead_timer; + Timer global_channel_timer; + Timer shield_timer; + Timer fishing_timer; + Timer endupkeep_timer; + Timer forget_timer; // our 2 min everybody forgets you timer + Timer autosave_timer; #ifdef REVERSE_AGGRO - Timer scanarea_timer; + Timer scanarea_timer; #endif - Timer tribute_timer; + Timer tribute_timer; - Timer proximity_timer; - Timer TaskPeriodic_Timer; - Timer charm_update_timer; - Timer rest_timer; - Timer charm_class_attacks_timer; - Timer charm_cast_timer; - Timer qglobal_purge_timer; - Timer TrackingTimer; - Timer RespawnFromHoverTimer; - Timer merc_timer; + Timer proximity_timer; + Timer TaskPeriodic_Timer; + Timer charm_update_timer; + Timer rest_timer; + Timer charm_class_attacks_timer; + Timer charm_cast_timer; + Timer qglobal_purge_timer; + Timer TrackingTimer; + Timer RespawnFromHoverTimer; + Timer merc_timer; - float proximity_x; - float proximity_y; - float proximity_z; + float proximity_x; + float proximity_y; + float proximity_z; - void BulkSendInventoryItems(); + void BulkSendInventoryItems(); faction_map factionvalues; uint32 tribute_master_id; FILE *SQL_log; - uint32 max_AAXP; - uint32 staminacount; - AA_Array* aa[MAX_PP_AA_ARRAY]; //this list contains pointers into our player profile + uint32 max_AAXP; + uint32 staminacount; + AA_Array* aa[MAX_PP_AA_ARRAY]; //this list contains pointers into our player profile std::map aa_points; bool npcflag; uint8 npclevel; @@ -1437,15 +1431,15 @@ private: bool zoning; bool tgb; bool instalog; - int32 last_reported_mana; - int32 last_reported_endur; + int32 last_reported_mana; + int32 last_reported_endur; unsigned int AggroCount; // How many mobs are aggro on us. - unsigned int RestRegenHP; - unsigned int RestRegenMana; - unsigned int RestRegenEndurance; - + unsigned int RestRegenHP; + unsigned int RestRegenMana; + unsigned int RestRegenEndurance; + bool EngagedRaidTarget; uint32 SavedRaidRestTimer; @@ -1462,7 +1456,7 @@ private: uint32 AttemptedMessages; EQClientVersion ClientVersion; - uint32 ClientVersionBit; + uint32 ClientVersionBit; int XPRate; @@ -1477,18 +1471,18 @@ private: //Connecting debug code. enum { //connecting states, used for debugging only - NoPacketsReceived, //havent gotten anything - //this is the point where the client changes to the loading screen - ReceivedZoneEntry, //got the first packet, loading up PP - PlayerProfileLoaded, //our DB work is done, sending it - ZoneInfoSent, //includes PP, tributes, tasks, spawns, time and weather - //this is the point where the client shows a status bar zoning in - NewZoneRequested, //received and sent new zone request - ClientSpawnRequested, //client sent ReqClientSpawn - ZoneContentsSent, //objects, doors, zone points - ClientReadyReceived, //client told us its ready, send them a bunch of crap like guild MOTD, etc - //this is the point where the client releases the mouse - ClientConnectFinished //client finally moved to finished state, were done here + NoPacketsReceived, //havent gotten anything + //this is the point where the client changes to the loading screen + ReceivedZoneEntry, //got the first packet, loading up PP + PlayerProfileLoaded, //our DB work is done, sending it + ZoneInfoSent, //includes PP, tributes, tasks, spawns, time and weather + //this is the point where the client shows a status bar zoning in + NewZoneRequested, //received and sent new zone request + ClientSpawnRequested, //client sent ReqClientSpawn + ZoneContentsSent, //objects, doors, zone points + ClientReadyReceived, //client told us its ready, send them a bunch of crap like guild MOTD, etc + //this is the point where the client releases the mouse + ClientConnectFinished //client finally moved to finished state, were done here } conn_state; void ReportConnectingState(); @@ -1496,8 +1490,8 @@ private: bool PendingGuildInvitation; int PendingRezzXP; uint32 PendingRezzDBID; - uint16 PendingRezzSpellID; // Only used for resurrect while hovering. - std::string PendingRezzCorpseName; // Only used for resurrect while hovering. + uint16 PendingRezzSpellID; // Only used for resurrect while hovering. + std::string PendingRezzCorpseName; // Only used for resurrect while hovering. std::set PlayerBlockedBuffs; std::set PetBlockedBuffs; @@ -1513,7 +1507,11 @@ private: std::map accountflags; uint8 initial_respawn_selection; + + bool interrogateinv_flag; // used to minimize log spamming by players + + void InterrogateInventory_(bool errorcheck, Client* requester, int16 head, int16 index, const ItemInst* inst, const ItemInst* parent, bool log, bool silent, bool &error, int depth); + bool InterrogateInventory_error(int16 head, int16 index, const ItemInst* inst, const ItemInst* parent, int depth); }; #endif - diff --git a/zone/client_logs.cpp b/zone/client_logs.cpp index c130cd698..1b5a9088e 100644 --- a/zone/client_logs.cpp +++ b/zone/client_logs.cpp @@ -33,8 +33,6 @@ 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(); @@ -98,6 +96,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 66eeb9d11..55b522378 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -18,31 +18,25 @@ #include #include "../common/debug.h" -#include "../common/spdat.h" -#include "../common/packet_dump.h" -#include "../common/packet_functions.h" -#include "../common/serverinfo.h" -#include "../common/ZoneNumbers.h" -#include "../common/moremath.h" -#include "../common/guilds.h" #include "../common/logsys.h" +#include "../common/spdat.h" +#include "../common/rulesys.h" #include "masterentity.h" +#include "npc_ai.h" +#include "petitions.h" +#include "string_ids.h" #include "worldserver.h" #include "zonedb.h" -#include "petitions.h" -#include "StringIDs.h" -#include "NpcAI.h" -// Return max stat value for level -int16 Client::GetMaxStat() const { +int32 Client::GetMaxStat() const { if((RuleI(Character, StatCap)) > 0) return (RuleI(Character, StatCap)); int level = GetLevel(); - int16 base = 0; + int32 base = 0; if (level < 61) { base = 255; @@ -60,11 +54,11 @@ int16 Client::GetMaxStat() const { return(base); } -int16 Client::GetMaxResist() const +int32 Client::GetMaxResist() const { int level = GetLevel(); - int16 base = 500; + int32 base = 500; if(level > 60) base += ((level - 60) * 5); @@ -72,79 +66,79 @@ int16 Client::GetMaxResist() const return base; } -int16 Client::GetMaxSTR() const { +int32 Client::GetMaxSTR() const { return GetMaxStat() + itembonuses.STRCapMod + spellbonuses.STRCapMod + aabonuses.STRCapMod; } -int16 Client::GetMaxSTA() const { +int32 Client::GetMaxSTA() const { return GetMaxStat() + itembonuses.STACapMod + spellbonuses.STACapMod + aabonuses.STACapMod; } -int16 Client::GetMaxDEX() const { +int32 Client::GetMaxDEX() const { return GetMaxStat() + itembonuses.DEXCapMod + spellbonuses.DEXCapMod + aabonuses.DEXCapMod; } -int16 Client::GetMaxAGI() const { +int32 Client::GetMaxAGI() const { return GetMaxStat() + itembonuses.AGICapMod + spellbonuses.AGICapMod + aabonuses.AGICapMod; } -int16 Client::GetMaxINT() const { +int32 Client::GetMaxINT() const { return GetMaxStat() + itembonuses.INTCapMod + spellbonuses.INTCapMod + aabonuses.INTCapMod; } -int16 Client::GetMaxWIS() const { +int32 Client::GetMaxWIS() const { return GetMaxStat() + itembonuses.WISCapMod + spellbonuses.WISCapMod + aabonuses.WISCapMod; } -int16 Client::GetMaxCHA() const { +int32 Client::GetMaxCHA() const { return GetMaxStat() + itembonuses.CHACapMod + spellbonuses.CHACapMod + aabonuses.CHACapMod; } -int16 Client::GetMaxMR() const { +int32 Client::GetMaxMR() const { return GetMaxResist() + itembonuses.MRCapMod + spellbonuses.MRCapMod + aabonuses.MRCapMod; } -int16 Client::GetMaxPR() const { +int32 Client::GetMaxPR() const { return GetMaxResist() + itembonuses.PRCapMod + spellbonuses.PRCapMod + aabonuses.PRCapMod; } -int16 Client::GetMaxDR() const { +int32 Client::GetMaxDR() const { return GetMaxResist() + itembonuses.DRCapMod + spellbonuses.DRCapMod + aabonuses.DRCapMod; } -int16 Client::GetMaxCR() const { +int32 Client::GetMaxCR() const { return GetMaxResist() + itembonuses.CRCapMod + spellbonuses.CRCapMod + aabonuses.CRCapMod; } -int16 Client::GetMaxFR() const { +int32 Client::GetMaxFR() const { return GetMaxResist() + itembonuses.FRCapMod + spellbonuses.FRCapMod + aabonuses.FRCapMod; } -int16 Client::GetMaxCorrup() const { +int32 Client::GetMaxCorrup() const { return GetMaxResist() + itembonuses.CorrupCapMod + spellbonuses.CorrupCapMod @@ -265,8 +259,8 @@ int32 Client::CalcMaxHP() { return max_hp; } -uint16 Mob::GetClassLevelFactor(){ - uint16 multiplier = 0; +uint32 Mob::GetClassLevelFactor(){ + uint32 multiplier = 0; uint8 mlevel=GetLevel(); switch(GetClass()) { @@ -386,8 +380,8 @@ int32 Client::CalcBaseHP() } } else { - uint16 Post255; - uint16 lm=GetClassLevelFactor(); + uint32 Post255; + uint32 lm=GetClassLevelFactor(); if((GetSTA()-255)/2 > 0) Post255 = (GetSTA()-255)/2; else @@ -446,8 +440,8 @@ uint32 Client::GetClassHPFactor() { } // This should return the combined AC of all the items the player is wearing. -int16 Client::GetRawItemAC() { - int16 Total = 0; +int32 Client::GetRawItemAC() { + int32 Total = 0; // this skips MainAmmo..add an '=' conditional if that slot is required (original behavior) for (int16 slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id < EmuConstants::EQUIPMENT_END; slot_id++) { @@ -460,7 +454,7 @@ int16 Client::GetRawItemAC() { return Total; } -int16 Client::acmod() { +int32 Client::acmod() { int agility = GetAGI(); int level = GetLevel(); if(agility < 1 || level < 1) @@ -838,7 +832,7 @@ int16 Client::acmod() { // ac1 and ac2 are probably the damage migitation and damage avoidance numbers, not sure which is which. // I forgot to include the iksar defense bonus and i cant find my notes now... // AC from spells are not included (cant even cast spells yet..) -int16 Client::CalcAC() { +int32 Client::CalcAC() { // new formula int avoidance = (acmod() + ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10)*16)/9); @@ -889,7 +883,7 @@ int16 Client::CalcAC() { return(AC); } -int16 Client::GetACMit() { +int32 Client::GetACMit() { int mitigation = 0; if (m_pp.class_ == WIZARD || m_pp.class_ == MAGICIAN || m_pp.class_ == NECROMANCER || m_pp.class_ == ENCHANTER) { @@ -915,9 +909,9 @@ int16 Client::GetACMit() { return(mitigation*1000/847); } -int16 Client::GetACAvoid() { +int32 Client::GetACAvoid() { - int avoidance = (acmod() + ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10)*16)/9); + int32 avoidance = (acmod() + ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10)*16)/9); if (avoidance < 0) avoidance = 0; @@ -1186,7 +1180,7 @@ uint32 Client::CalcCurrentWeight() { return Total; } -int16 Client::CalcAlcoholPhysicalEffect() +int32 Client::CalcAlcoholPhysicalEffect() { if(m_pp.intoxication <= 55) return 0; @@ -1194,10 +1188,10 @@ int16 Client::CalcAlcoholPhysicalEffect() return (m_pp.intoxication - 40) / 16; } -int16 Client::CalcSTR() { - int16 val = m_pp.STR + itembonuses.STR + spellbonuses.STR + CalcAlcoholPhysicalEffect(); +int32 Client::CalcSTR() { + int32 val = m_pp.STR + itembonuses.STR + spellbonuses.STR + CalcAlcoholPhysicalEffect(); - int16 mod = aabonuses.STR; + int32 mod = aabonuses.STR; STR = val + mod; @@ -1211,10 +1205,10 @@ int16 Client::CalcSTR() { return(STR); } -int16 Client::CalcSTA() { - int16 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect();; +int32 Client::CalcSTA() { + int32 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect();; - int16 mod = aabonuses.STA; + int32 mod = aabonuses.STA; STA = val + mod; @@ -1228,18 +1222,18 @@ int16 Client::CalcSTA() { return(STA); } -int16 Client::CalcAGI() { - int16 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect();; - int16 mod = aabonuses.AGI; +int32 Client::CalcAGI() { + int32 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect();; + int32 mod = aabonuses.AGI; - int16 str = GetSTR(); + int32 str = GetSTR(); //Encumbered penalty if(weight > (str * 10)) { //AGI is halved when we double our weight, zeroed (defaults to 1) when we triple it. this includes AGI from AAs float total_agi = float(val + mod); float str_float = float(str); - AGI = (int16)(((-total_agi) / (str_float * 2)) * (((float)weight / 10) - str_float) + total_agi); //casting to an int assumes this will be floor'd. without using floats & casting to int16, the calculation doesn't work right + AGI = (int32)(((-total_agi) / (str_float * 2)) * (((float)weight / 10) - str_float) + total_agi); //casting to an int assumes this will be floor'd. without using floats & casting to int16, the calculation doesn't work right } else AGI = val + mod; @@ -1253,10 +1247,10 @@ int16 Client::CalcAGI() { return(AGI); } -int16 Client::CalcDEX() { - int16 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect();; +int32 Client::CalcDEX() { + int32 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect();; - int16 mod = aabonuses.DEX; + int32 mod = aabonuses.DEX; DEX = val + mod; @@ -1270,16 +1264,16 @@ int16 Client::CalcDEX() { return(DEX); } -int16 Client::CalcINT() { - int16 val = m_pp.INT + itembonuses.INT + spellbonuses.INT; +int32 Client::CalcINT() { + int32 val = m_pp.INT + itembonuses.INT + spellbonuses.INT; - int16 mod = aabonuses.INT; + int32 mod = aabonuses.INT; INT = val + mod; if(m_pp.intoxication) { - int16 AlcINT = INT - (int16)((float)m_pp.intoxication / 200.0f * (float)INT) - 1; + int32 AlcINT = INT - (int32)((float)m_pp.intoxication / 200.0f * (float)INT) - 1; if((AlcINT < (int)(0.2 * INT))) INT = (int)(0.2f * (float)INT); @@ -1297,16 +1291,16 @@ int16 Client::CalcINT() { return(INT); } -int16 Client::CalcWIS() { - int16 val = m_pp.WIS + itembonuses.WIS + spellbonuses.WIS; +int32 Client::CalcWIS() { + int32 val = m_pp.WIS + itembonuses.WIS + spellbonuses.WIS; - int16 mod = aabonuses.WIS; + int32 mod = aabonuses.WIS; WIS = val + mod; if(m_pp.intoxication) { - int16 AlcWIS = WIS - (int16)((float)m_pp.intoxication / 200.0f * (float)WIS) - 1; + int32 AlcWIS = WIS - (int32)((float)m_pp.intoxication / 200.0f * (float)WIS) - 1; if((AlcWIS < (int)(0.2 * WIS))) WIS = (int)(0.2f * (float)WIS); @@ -1324,10 +1318,10 @@ int16 Client::CalcWIS() { return(WIS); } -int16 Client::CalcCHA() { - int16 val = m_pp.CHA + itembonuses.CHA + spellbonuses.CHA; +int32 Client::CalcCHA() { + int32 val = m_pp.CHA + itembonuses.CHA + spellbonuses.CHA; - int16 mod = aabonuses.CHA; + int32 mod = aabonuses.CHA; CHA = val + mod; @@ -1341,12 +1335,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,30 +1395,22 @@ 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 //The resistant discipline which I think should be here is implemented //in Mob::ResistSpell -int16 Client::CalcMR() +int32 Client::CalcMR() { //racial bases switch(GetBaseRace()) { @@ -1461,7 +1480,7 @@ int16 Client::CalcMR() return(MR); } -int16 Client::CalcFR() +int32 Client::CalcFR() { //racial bases switch(GetBaseRace()) { @@ -1537,7 +1556,7 @@ int16 Client::CalcFR() return(FR); } -int16 Client::CalcDR() +int32 Client::CalcDR() { //racial bases switch(GetBaseRace()) { @@ -1620,7 +1639,7 @@ int16 Client::CalcDR() return(DR); } -int16 Client::CalcPR() +int32 Client::CalcPR() { //racial bases switch(GetBaseRace()) { @@ -1703,7 +1722,7 @@ int16 Client::CalcPR() return(PR); } -int16 Client::CalcCR() +int32 Client::CalcCR() { //racial bases switch(GetBaseRace()) { @@ -1779,7 +1798,7 @@ int16 Client::CalcCR() return(CR); } -int16 Client::CalcCorrup() +int32 Client::CalcCorrup() { Corrup = GetBaseCorrup() + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; @@ -1789,17 +1808,17 @@ int16 Client::CalcCorrup() return(Corrup); } -int16 Client::CalcATK() { +int32 Client::CalcATK() { ATK = itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); return(ATK); } -uint16 Mob::GetInstrumentMod(uint16 spell_id) const +uint32 Mob::GetInstrumentMod(uint16 spell_id) const { if (GetClass() != BARD) return 10; - uint16 effectmod = 10; + uint32 effectmod = 10; int effectmodcap = RuleI(Character, BaseInstrumentSoftCap); //this should never use spell modifiers... @@ -1882,7 +1901,7 @@ uint16 Mob::GetInstrumentMod(uint16 spell_id) const void Client::CalcMaxEndurance() { - max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance; + max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance + aabonuses.Endurance; if (max_end < 0) { max_end = 0; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 084a4a205..8d9d22650 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -16,16 +16,13 @@ */ #include "../common/debug.h" -#include #include -#include -#include -#include +#include #include -#include -#include -#include #include +#include +#include +#include #ifdef _WINDOWS @@ -39,48 +36,37 @@ #include #endif -#include "masterentity.h" -#include "zonedb.h" -#include "../common/packet_functions.h" -#include "../common/packet_dump.h" -#include "worldserver.h" -#include "../common/rdtsc.h" -#include "../common/packet_dump_file.h" -#include "../common/StringUtil.h" -#include "../common/breakdowns.h" -#include "../common/guilds.h" -#include "../common/rulesys.h" -#include "../common/spdat.h" -#include "petitions.h" -#include "NpcAI.h" -#include "../common/skills.h" -#include "forage.h" -#include "zone.h" -#include "event_codes.h" -#include "../common/faction.h" #include "../common/crc32.h" -#include "StringIDs.h" -#include "map.h" -#include "titles.h" -#include "pets.h" -#include "ZoneConfig.h" +#include "../common/data_verification.h" +#include "../common/faction.h" +#include "../common/guilds.h" +#include "../common/rdtsc.h" +#include "../common/rulesys.h" +#include "../common/skills.h" +#include "../common/spdat.h" +#include "../common/string_util.h" +#include "../common/zone_numbers.h" +#include "event_codes.h" #include "guild_mgr.h" -#include "pathing.h" -#include "water_map.h" #include "merc.h" -#include "../common/ZoneNumbers.h" -#include "QuestParserCollection.h" +#include "petitions.h" +#include "pets.h" +#include "queryserv.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "titles.h" +#include "water_map.h" +#include "worldserver.h" +#include "zone.h" #include "remote_call.h" #include "remote_call_subscribe.h" +extern QueryServ* QServ; extern Zone* zone; 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 @@ -88,311 +74,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_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 +392,7 @@ void ClearMappedOpcode(EmuOpcode op) { } } +// client methods int Client::HandlePacket(const EQApplicationPacket *app) { if(is_log_enabled(CLIENT__NET_IN_TRACE)) { @@ -434,7 +424,7 @@ int Client::HandlePacket(const EQApplicationPacket *app) case CLIENT_CONNECTING: { if(ConnectingOpcodes.count(opcode) != 1) { //Hate const cast but everything in lua needs to be non-const even if i make it non-mutable - std::vector args; + std::vector args; args.push_back(const_cast(app)); parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 1, &args); @@ -463,7 +453,7 @@ int Client::HandlePacket(const EQApplicationPacket *app) ClientPacketProc p; p = ConnectedOpcodes[opcode]; if(p == nullptr) { - std::vector args; + std::vector args; args.push_back(const_cast(app)); parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 0, &args); @@ -473,10 +463,10 @@ int Client::HandlePacket(const EQApplicationPacket *app) mlog(CLIENT__NET_ERR, "Unhandled incoming opcode: %s", buffer); if(app->size < 1000) - DumpPacket(app->pBuffer, app->size); + DumpPacket(app, app->size); else{ std::cout << "Dump limited to 1000 characters:\n"; - DumpPacket(app->pBuffer, 1000); + DumpPacket(app, 1000); } #endif break; @@ -498,121 +488,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 +1015,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 +1034,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 +1045,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 +1071,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 +1105,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 +1134,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 +1148,7 @@ void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app) QueuePacket(outapp); safe_delete(outapp);*/ - if(IsInAGuild()) { + if (IsInAGuild()) { SendGuildMembers(); SendGuildURL(); SendGuildChannel(); @@ -753,6 +1163,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 +1236,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 +1266,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 +1297,3048 @@ 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) { + admin = atoi(row[0]); + strncpy(account_name, row[1], 30); + lsaccountid = atoi(row[2]); + 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(), false); //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. + // Some clients this seems to nuke the charm text (ex. Adventurer's Stone) + + 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; + const Item_Struct * item = database.GetItem(AugInfo->itemid); + + if (item) { + strn0cpy(AugInfo->augment_info, item->Name, 64); + AugInfo->itemid = 0; + QueuePacket(app); + } } -void Client::Handle_Connect_OP_UpdateAA(const EQApplicationPacket *app) { - SendAATable(); -} +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; + } -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) + // Delegate to tradeskill object to perform combine + AugmentItem_Struct* in_augment = (AugmentItem_Struct*)app->pBuffer; + bool deleteItems = false; + if (GetClientVersion() >= EQClientRoF) { - 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; + ItemInst *itemOneToPush = nullptr, *itemTwoToPush = nullptr; - 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; + //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); - case MQZone: - if(RuleB(Zone, EnableMQZoneDetector) && ((this->Admin() < RuleI(Zone, MQZoneExemptStatus) || (RuleI(Zone, MQZoneExemptStatus)) == -1))) + // 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; + } + } + + if(RemoteCallSubscriptionHandler::Instance()->IsSubscribed("Combat.States")) { + bool wi_aa = false; + if(app->pBuffer[0] == 0) { + wi_aa = false; + } + else if(app->pBuffer[0] == 1) { + wi_aa = true; + } + + std::vector params; + params.push_back(std::to_string(GetID())); + params.push_back(std::to_string(wi_aa)); + RemoteCallSubscriptionHandler::Instance()->OnEvent("Combat.States", params); + } +} + +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 ((IsValidSpell(bbs->SpellID[i])) && IsBeneficialSpell(bbs->SpellID[i]) && !spells[bbs->SpellID[i]].no_block) + { + 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 (!IsValidSpell(bbs->SpellID[i]) || !IsBeneficialSpell(bbs->SpellID[i]) || spells[bbs->SpellID[i]].no_block) + 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) +{ + // this sends unclean mob name, so capped at 64 + // a_boat006 + if (app->size <= 5 || app->size > 64) { + LogFile->write(EQEMuLog::Error, "Size mismatch in OP_BoardBoad. Expected greater than 5 less than 64, got %i", app->size); + DumpPacket(app); + return; + } + + char boatname[64]; + memcpy(boatname, app->pBuffer, app->size); + boatname[63] = '\0'; + + Mob* boat = entity_list.GetMob(boatname); + if (!boat || (boat->GetRace() != CONTROLLED_BOAT && boat->GetRace() != 502)) + return; + BoatID = boat->GetID(); // set the client's BoatID to show that it's on this boat + + 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; + + targetring_x = castspell->x_pos; + targetring_y = castspell->y_pos; + targetring_z = castspell->z_pos; + +#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; + } + + targetring_x = castspell->x_pos; + targetring_y = castspell->y_pos; + targetring_z = castspell->z_pos; + + 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)|| (castspell->slot == TARGET_RING_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) || (castspell->slot == TARGET_RING_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) @@ -1026,17 +4358,16 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) } PlayerPositionUpdateClient_Struct* ppu = (PlayerPositionUpdateClient_Struct*)app->pBuffer; - /* Web Interface */ - if (IsClient() && RemoteCallSubscriptionHandler::Instance()->IsSubscribed("Client.Position")) { + if(IsClient() && RemoteCallSubscriptionHandler::Instance()->IsSubscribed("Client.Position")) { std::vector params; - params.push_back(std::to_string((long)GetID())); + params.push_back(std::to_string(GetID())); params.push_back(GetCleanName()); - params.push_back(std::to_string((double)ppu->x_pos)); - params.push_back(std::to_string((double)ppu->y_pos)); - params.push_back(std::to_string((double)ppu->z_pos)); - params.push_back(std::to_string((double)heading)); - params.push_back(std::to_string((double)GetClass())); - params.push_back(std::to_string((double)GetRace())); + params.push_back(std::to_string(ppu->x_pos)); + params.push_back(std::to_string(ppu->y_pos)); + params.push_back(std::to_string(ppu->z_pos)); + params.push_back(std::to_string(heading)); + params.push_back(std::to_string(GetClass())); + params.push_back(std::to_string(GetRace())); RemoteCallSubscriptionHandler::Instance()->OnEvent("Client.Position", params); } @@ -1304,81 +4635,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; - } - - /* Web Interface: Combat State */ - if (RemoteCallSubscriptionHandler::Instance()->IsSubscribed("Combat.States")) { - bool wi_aa = false; - if (app->pBuffer[0] == 0){ wi_aa = false; } - else if (app->pBuffer[0] == 1){ wi_aa = true; } - std::vector params; - params.push_back(std::to_string((long)GetID())); - params.push_back(std::to_string((bool)wi_aa)); - RemoteCallSubscriptionHandler::Instance()->OnEvent("Combat.States", params); - } - - 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; } @@ -1426,494 +4714,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); // 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->IsRezzed() ? "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) @@ -1924,9 +4876,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)); @@ -1939,9 +4891,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)); @@ -1956,7 +4908,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; } @@ -1987,905 +4939,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(); @@ -2910,490 +5427,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(0); - 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; } - 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 && sayid > 0) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `phrase` FROM saylink WHERE `id` = '%i'", sayid),errbuf,&result)) - { - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - response = row[0]; - } - mysql_free_result(result); - } - else - { - Message(13, "Error: The saylink (%s) was not found in the database.",response.c_str()); - safe_delete_array(query); - return; - } - safe_delete_array(query); - } - - 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); + 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; } - 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_MoveItem(const EQApplicationPacket *app) -{ - if(!CharacterID()) + 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) { - 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(); @@ -3420,213 +5665,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, time_of_death, rezzed, IsBurried " + "FROM character_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], time_of_death[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(time_of_death, row[5], sizeof(time_of_death)); + + bool corpseRezzed = atoi(row[6]); + bool corpseBuried = atoi(row[7]); + + popupText += StringFormat("", + charName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, time_of_death, + 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)) { @@ -3649,7 +6344,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); @@ -3660,7 +6355,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; @@ -3696,2663 +6391,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 (!guild_mgr.IsGuildLeader(GuildID(), CharacterID())) - 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_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 (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. - if(!guild_mgr.SetGuild(CharacterID(), gj->guildeqid, gj->response)) { - 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); - SendGuildRanks(); - - } - } -} - -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; - - 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)) { - uint16 trade_count = 0; - - // Item trade count for packet sizing - for(int16 slot_id = EmuConstants::TRADE_BEGIN; slot_id <= EmuConstants::TRADE_END; slot_id++) { - if(other->GetInv().GetItem(slot_id)) { trade_count += other->GetInv().GetItem(slot_id)->GetTotalItemCount(); } - if(m_inv[slot_id]) { trade_count += m_inv[slot_id]->GetTotalItemCount(); } - } - - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogTrades, sizeof(QSPlayerLogTrade_Struct) + (sizeof(QSTradeItems_Struct) * trade_count)); - - // Perform actual trade - this->FinishTrade(other, qspack, true); - other->FinishTrade(this, qspack, false); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - // 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)) { - uint16 handin_count = 0; - - for(int16 slot_id = EmuConstants::TRADE_BEGIN; slot_id <= EmuConstants::TRADE_NPC_END; slot_id++) { - if(m_inv[slot_id]) { handin_count += m_inv[slot_id]->GetTotalItemCount(); } - } - - ServerPacket* qspack = new ServerPacket(ServerOP_QSPlayerLogHandins, sizeof(QSPlayerLogHandin_Struct) + (sizeof(QSHandinItems_Struct) * handin_count)); - - FinishTrade(with->CastToNPC(), qspack); - - qspack->Deflate(); - if(worldserver.Connected()) { worldserver.SendPacket(qspack); } - safe_delete(qspack); - } - else { - FinishTrade(with->CastToNPC()); - } - } -#ifdef BOTS - 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 factionlvl = GetFactionLevel(CharacterID(), tmp->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), tmp->CastToNPC()->GetPrimaryFaction(), tmp); - if(factionlvl >= 7) - { - char playerp[16] = "players"; - if(HatedByClass(GetRace(), GetClass(), GetDeity(), tmp->CastToNPC()->GetPrimaryFaction())) - strcpy(playerp,GetClassPlural(this)); - else - strcpy(playerp,GetRacePlural(this)); - - uint8 rand_ = rand() % 4; - switch(rand_){ - case 1: - Message(0,"%s says 'It's not enough that you %s have ruined your own lands. Now get lost!'", tmp->GetCleanName(), playerp); - break; - case 2: - Message(0,"%s says 'I have something here that %s use... let me see... it's the EXIT, now get LOST!'", tmp->GetCleanName(), playerp); - break; - case 3: - Message(0,"%s says 'Don't you %s have your own merchants? Whatever, I'm not selling anything to you!'", tmp->GetCleanName(), playerp); - break; - default: - Message(0,"%s says 'I don't like to speak to %s much less sell to them!'", tmp->GetCleanName(), playerp); - break; - } - 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, MerchantLogTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSMerchantLogTransactions, 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, MerchantLogTransactions)) { - ServerPacket* qspack = new ServerPacket(ServerOP_QSMerchantLogTransactions, 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; - 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; @@ -6366,12 +6404,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 @@ -6382,191 +6420,24 @@ void Client::Handle_OP_GroupCancelInvite(const EQApplicationPacket *app) safe_delete(pack); } - database.SetGroupID(GetName(), 0, CharacterID()); + if (!GetMerc()) + { + database.SetGroupID(GetName(), 0, CharacterID(), false); + } return; } -void Client::Handle_OP_GroupFollow(const EQApplicationPacket *app) +void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) { - Handle_OP_GroupFollow2(app); -} + //should check for leader, only they should be able to do this.. + Group* group = GetGroup(); + if (group) + group->DisbandGroup(); -void Client::Handle_OP_GroupFollow2(const EQApplicationPacket *app) -{ - if (app->size != sizeof(GroupGeneric_Struct)) { - LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupFollow: Expected: %i, Got: %i", - sizeof(GroupGeneric_Struct), app->size); - return; - } + if (LFP) + UpdateLFP(); - 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; - Mob* inviter = entity_list.GetClientByName(gf->name1); - - if(inviter != nullptr && inviter->IsClient()) { - isgrouped = true; - 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){ - // 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()){ - groupToUse = raid->members[x].GroupNumber; - break; - } - } - } - if(iraid == raid){ //both in same raid - uint32 ngid = raid->GetGroup(inviter->GetName()); - if(raid->GroupCount(ngid) < 6){ - raid->MoveMember(GetName(), ngid); - raid->SendGroupDisband(this); - //raid->SendRaidGroupAdd(GetName(), ngid); - //raid->SendGroupUpdate(this); - raid->GroupUpdate(ngid); //break - } - return; - } - if(raid->RaidCount() < MAX_RAID_MEMBERS){ - if(raid->GroupCount(groupToUse) < 6){ - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->AddMember(this, groupToUse); - raid->SendBulkRaid(this); - //raid->SendRaidGroupAdd(GetName(), groupToUse); - //raid->SendGroupUpdate(this); - raid->GroupUpdate(groupToUse); //break - if(raid->IsLocked()) { - raid->SendRaidLockTo(this); - } - return; - } - else{ - raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); - raid->AddMember(this); - raid->SendBulkRaid(this); - if(raid->IsLocked()) { - raid->SendRaidLockTo(this); - } - return; - } - } - } - - Group* group = entity_list.GetGroupByClient(inviter->CastToClient()); - - if(!group){ - //Make new group - group = new Group(inviter); - if(!group) - return; - entity_list.AddGroup(group); - - 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; - } - - //now we have a group id, can set inviter's id - database.SetGroupID(inviter->GetName(), group->GetID(), inviter->CastToClient()->CharacterID()); - database.SetGroupLeaderName(group->GetID(), inviter->GetName()); - - group->UpdateGroupAAs(); - - //Invite the inviter into the group first.....dont ask - if(inviter->CastToClient()->GetClientVersion() < EQClientSoD) - { - 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'. - group->GetGroupAAs(&outgj->leader_aas); - inviter->CastToClient()->QueuePacket(outapp); - safe_delete(outapp); - } - else - { - // SoD and later - // - inviter->CastToClient()->SendGroupCreatePacket(); - - inviter->CastToClient()->SendGroupLeaderChangePacket(inviter->GetName()); - - inviter->CastToClient()->SendGroupJoinAcknowledge(); - } - - } - if(!group) - return; - - inviter->CastToClient()->QueuePacket(app);//notify inviter the client accepted - - if(!group->AddMember(this)) - return; - - 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) - SendGroupJoinAcknowledge(); - - database.RefreshGroupFromDB(this); - group->SendHPPacketsTo(this); - - // Temporary hack for SoD, as things seem to work quite differently - if(inviter->CastToClient()->GetClientVersion() >= EQClientSoD) - database.RefreshGroupFromDB(inviter->CastToClient()); - - // Add the merc back into the new group - if (GetMerc()) { - if (Merc::AddMercToGroup(GetMerc(), group)) { - database.SetGroupID(GetMerc()->GetName(), group->GetID(), inviter->CastToClient()->CharacterID(), true); - database.RefreshGroupFromDB(this); - } - } - - //send updates to clients out of zone... - ServerPacket* pack = new ServerPacket(ServerOP_GroupJoin, sizeof(ServerGroupJoin_Struct)); - ServerGroupJoin_Struct* gj = (ServerGroupJoin_Struct*)pack->pBuffer; - gj->gid = group->GetID(); - gj->zoneid = zone->GetZoneID(); - gj->instance_id = zone->GetInstanceID(); - strcpy(gj->member_name, GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if(inviter == nullptr) - { - ServerPacket* pack = new ServerPacket(ServerOP_GroupFollow, sizeof(ServerGroupFollow_Struct)); - ServerGroupFollow_Struct *sgfs = (ServerGroupFollow_Struct *)pack->pBuffer; - sgfs->CharacterID = CharacterID(); - strn0cpy(sgfs->gf.name1, gf->name1, sizeof(sgfs->gf.name1)); - strn0cpy(sgfs->gf.name2, gf->name2, sizeof(sgfs->gf.name2)); - worldserver.SendPacket(pack); - safe_delete(pack); - } + return; } void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) @@ -6579,37 +6450,38 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) LogFile->write(EQEMuLog::Debug, "Member Disband Request from %s\n", GetName()); - GroupGeneric_Struct* gd = (GroupGeneric_Struct*) app->pBuffer; + GroupGeneric_Struct* gd = (GroupGeneric_Struct*)app->pBuffer; Raid *raid = entity_list.GetRaidByClient(this); - if(raid){ + if (raid) + { Mob* memberToDisband = nullptr; - if(!raid->IsGroupLeader(GetName())) + if (!raid->IsGroupLeader(GetName())) memberToDisband = this; else memberToDisband = GetTarget(); - if(!memberToDisband) + if (!memberToDisband) memberToDisband = entity_list.GetMob(gd->name2); - if(!memberToDisband) + if (!memberToDisband) memberToDisband = this; - if(!memberToDisband->IsClient()) + 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){ + if (grp < 12){ + if (wasGrpLdr){ raid->SetGroupLeader(memberToDisband->GetName(), false); - for(int x = 0; x < MAX_RAID_MEMBERS; x++) + for (int x = 0; x < MAX_RAID_MEMBERS; x++) { - if(raid->members[x].GroupNumber == grp) + if (raid->members[x].GroupNumber == grp) { - if(strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) + if (strlen(raid->members[x].membername) > 0 && strcmp(raid->members[x].membername, memberToDisband->GetName()) != 0) { raid->SetGroupLeader(raid->members[x].membername); break; @@ -6629,111 +6501,100 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) Group* group = GetGroup(); - if(!group) + 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)) { + if (Bot::GroupHasBot(group)) { + if (group->IsLeader(this)) { + if ((GetTarget() == 0 || GetTarget() == this) || (group->GroupCount() < 3)) { Bot::ProcessBotGroupDisband(this, std::string()); - } else { + } + else { Mob* tempMember = entity_list.GetMob(gd->name2); - if(tempMember) { - if(tempMember->IsBot()) + if (tempMember) { + if (tempMember->IsBot()) Bot::ProcessBotGroupDisband(this, std::string(tempMember->GetCleanName())); } } } } #endif - if((group->IsLeader(this) && (GetTarget() == 0 || GetTarget() == this)) || (group->GroupCount()<3)) { + if (group->GroupCount() < 3) + { group->DisbandGroup(); - if(GetMerc() != nullptr) + if (GetMerc()) GetMerc()->Suspend(); - } else { + } + else if (group->IsLeader(this) && GetTarget() == nullptr) + { + if (group->GroupCount() > 2 && GetMerc() && !GetMerc()->IsSuspended()) + { + group->DisbandGroup(); + GetMerc()->MercJoinClientGroup(); + } + else + { + group->DisbandGroup(); + if (GetMerc()) + GetMerc()->Suspend(); + } + } + else if (group->IsLeader(this) && GetTarget() == this) + { + LeaveGroup(); + if (GetMerc() && !GetMerc()->IsSuspended()) + { + GetMerc()->MercJoinClientGroup(); + } + } + else + { Mob* memberToDisband = nullptr; memberToDisband = GetTarget(); - if(!memberToDisband) + 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()) + if (memberToDisband) + { + if (group->IsLeader(this)) + { + // the group leader can kick other members out of the group... + if (memberToDisband->IsClient()) + { + group->DelMember(memberToDisband, false); + Client* memberClient = memberToDisband->CastToClient(); + Merc* memberMerc = memberToDisband->CastToClient()->GetMerc(); + if (memberClient && memberMerc) { - 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(); + memberMerc->MercJoinClientGroup(); } } - 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 if (memberToDisband->IsMerc()) + { + memberToDisband->CastToMerc()->Suspend(); } } + else + { + // ...but other members can only remove themselves + group->DelMember(this, false); + + if (GetMerc() && !GetMerc()->IsSuspended()) + { + GetMerc()->MercJoinClientGroup(); + } + } + } 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 (LFP) + { // If we are looking for players, update to show we are on our own now. UpdateLFP(); } @@ -6741,4684 +6602,222 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app) return; } -void Client::Handle_OP_GroupDelete(const EQApplicationPacket *app) +void Client::Handle_OP_GroupFollow(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; + Handle_OP_GroupFollow2(app); } -void Client::Handle_OP_GMEmoteZone(const EQApplicationPacket *app) +void Client::Handle_OP_GroupFollow2(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)); + if (app->size != sizeof(GroupGeneric_Struct)) { + LogFile->write(EQEMuLog::Error, "Invalid size for OP_GroupFollow: Expected: %i, Got: %i", + sizeof(GroupGeneric_Struct), app->size); 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); } + 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()); } -#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 + GroupGeneric_Struct* gf = (GroupGeneric_Struct*)app->pBuffer; + Mob* inviter = entity_list.GetClientByName(gf->name1); + + // Inviter and Invitee are in the same zone + if (inviter != nullptr && inviter->IsClient()) { - if(petition_list.FindPetitionByAccountName(AccountName())) + if (GroupFollow(inviter->CastToClient())) { - 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; + strn0cpy(gf->name1, inviter->GetName(), sizeof(gf->name1)); + strn0cpy(gf->name2, GetName(), sizeof(gf->name2)); + inviter->CastToClient()->QueuePacket(app);//notify inviter the client accepted } - 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) + else if (inviter == nullptr) { - if(pet->command == PET_LEADER) + // Inviter is in another zone - Remove merc from group now if any + LeaveGroup(); + + ServerPacket* pack = new ServerPacket(ServerOP_GroupFollow, sizeof(ServerGroupFollow_Struct)); + ServerGroupFollow_Struct *sgfs = (ServerGroupFollow_Struct *)pack->pBuffer; + sgfs->CharacterID = CharacterID(); + strn0cpy(sgfs->gf.name1, gf->name1, sizeof(sgfs->gf.name1)); + strn0cpy(sgfs->gf.name2, gf->name2, sizeof(sgfs->gf.name2)); + worldserver.SendPacket(pack); + safe_delete(pack); + } +} + +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(mypet && (!GetTarget() || GetTarget() == mypet)) + if(Invitee->CastToClient()->MercOnlyOrNoGroup() && !Invitee->IsRaidGrouped()) { - 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()); - } + 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); } - 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); - //safe_delete(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); - //safe_delete(outapp); - - //I think this should happen earlier, not sure - /* if(GetHideMe()) - SetHideMe(true); */ - // Moved to Handle_Connect_OP_SendExpZonein(); - - - //////////////////////////////////////////////////////////// - // 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); - - ////////////////////////////////////// - // Group Roles - // - ////////////////////////////////////// - /*if(group){ - group->NotifyMainTank(this, 1); - group->NotifyMainAssist(this, 1); - group->NotifyPuller(this, 1); - }*/ - - 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()){ - SendAppearancePacket(AT_GuildID, GuildID(), false); - SendAppearancePacket(AT_GuildRank, GuildRank(), 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 (buffs[j1].spellid > (uint32)SPDAT_RECORDS) - continue; - - const SPDat_Spell_Struct &spell = spells[buffs[j1].spellid]; - - 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); - - 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)); @@ -11428,332 +6827,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; } + break; } -} -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; - } - 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; } - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result = nullptr; - MYSQL_ROW row = 0; - float x(0),y(0),z(0); - uint32 zoneid = 0; - - uint32 StartCity = (uint32)strtol((const char*)app->pBuffer, nullptr, 10); - bool ValidCity = false; - database.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", - m_pp.class_, - m_pp.deity, - m_pp.race - ), - errbuf, - &result - ); - safe_delete_array(query); - - if(!result) { - LogFile->write(EQEMuLog::Error, "No valid start zones found for /setstartcity"); - return; } - - while(row = mysql_fetch_row(result)) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - - if(zoneid == StartCity) { - 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); - } - else { - database.RunQuery - ( - query, - MakeAnyLenString - ( - &query, - "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 - ), - errbuf, - &result - ); - safe_delete_array(query); - Message(15,"Use \"/startcity #\" to choose a home city from the following list:"); - char* name; - while(row = mysql_fetch_row(result)) { - if(atoi(row[1]) != 0) - zoneid = atoi(row[1]); - else - zoneid = atoi(row[0]); - database.GetZoneLongName(database.GetZoneName(zoneid),&name); - Message(15,"%d - %s", zoneid, name); - safe_delete_array(name); - } - } - - mysql_free_result(result); -} - -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 errbuf[MYSQL_ERRMSG_SIZE]; - char* Query = 0; - MYSQL_RES *Result; - MYSQL_ROW Row; - - char *EscSearchString = new char[129]; - - database.DoEscapeString(EscSearchString, gmscs->Name, strlen(gmscs->Name)); - - if (database.RunQuery(Query, MakeAnyLenString(&Query, "select charname, zoneid, x, y, z, timeofdeath, rezzed, IsBurried from " - "player_corpses where charname like '%%%s%%' order by charname limit %i", - EscSearchString, MaxResults), errbuf, &Result)) - { - - int NumberOfRows = mysql_num_rows(Result); - - if(NumberOfRows == 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'.", - NumberOfRows, gmscs->Name); - } - - if(NumberOfRows == 0) - { - mysql_free_result(Result); - safe_delete_array(Query); - return; - } - - char CharName[64], TimeOfDeath[20], Buffer[512]; - - std::string PopupText = ""; - - - while ((Row = mysql_fetch_row(Result))) - { - - 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]); - - sprintf(Buffer, "", - CharName, StaticGetZoneName(ZoneID), CorpseX, CorpseY, CorpseZ, TimeOfDeath, - CorpseRezzed ? "Yes" : "No", CorpseBuried ? "Yes" : "No"); - - PopupText += Buffer; - - 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
"; - - mysql_free_result(Result); - - SendPopupToClient("Corpses", PopupText.c_str()); - } - else{ - Message(0, "Query failed: %s.", errbuf); - - } - safe_delete_array(Query); - safe_delete_array(EscSearchString); } 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."); @@ -11770,11 +6876,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(); @@ -11782,9 +6888,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()); @@ -11794,700 +6900,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; @@ -12510,19 +7169,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; @@ -12531,13 +7190,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; @@ -12552,790 +7211,1059 @@ 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; + 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."); } - - 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); +void Client::Handle_OP_GuildDemote(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildDemote"); + mpkt(GUILDS__IN_PACKET_TRACE, app); - AltCurrencySelectItem_Struct *select = (AltCurrencySelectItem_Struct*)app->pBuffer; - NPC* tar = entity_list.GetNPCByID(select->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) + 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(tar->GetClass() != ALT_CURRENCY_MERCHANT) { + } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); return; } - uint32 alt_cur_id = tar->GetAltCurrencyType(); - if(alt_cur_id == 0) { + 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; } - ItemInst *inst = m_inv.GetItem(select->slot_id); - if(!inst) { - return; - } + if (invitee->IsClient()) { + Client* client = invitee->CastToClient(); - const Item_Struct* item = nullptr; - uint32 cost = 0; - uint32 current_currency = GetAlternateCurrencyValue(alt_cur_id); - uint32 merchant_id = tar->MerchantType; + //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; + } - 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; } + 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); - 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; + 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 (!found) { - cost = 0; + 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 + } +} + +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 { - 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); + 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_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_GuildLeader(const EQApplicationPacket *app) +{ + mlog(GUILDS__IN_PACKETS, "Received OP_GuildLeader"); + 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; - } - - 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 < 2) { + mlog(GUILDS__ERROR, "Invalid length %d on OP_GuildLeader", app->size); return; } - if(reclaim->reclaim_flag == 1) { //item -> altcur - uint32 removed = NukeItem(item_id, invWhereWorn | invWherePersonal | invWhereCursor); - if(removed > 0) { - AddAlternateCurrencyValue(reclaim->currency_id, removed); - } - } else { - uint32 max_currency = GetAlternateCurrencyValue(reclaim->currency_id); - 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)); - } - } -} + 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 { -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; + //NOTE: we could do cross-zone lookups here... - NPC* tar = entity_list.GetNPCByID(sell->merchant_entity_id); - if(tar) { - if(DistNoRoot(*tar) > USE_NPC_RANGE2) - return; + Client* newleader = entity_list.GetClientByName(gml->target); + if (newleader) { - if(tar->GetClass() != ALT_CURRENCY_MERCHANT) { - return; - } + mlog(GUILDS__ACTIONS, "Transfering leadership of %s (%d) to %s (%d)", + guild_mgr.GetGuildName(GuildID()), GuildID(), + newleader->GetName(), newleader->CharacterID()); - 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; - 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 + Message(0, "Failed to change leader, could not find target."); + } + // SendGuildMembers(GuildID(), true); + 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) { - if(inst->GetCharges() < sell->charges) - { - sell->charges = inst->GetCharges(); - } + Message(13, "You are not allowed to change the alt status of %s", gmb->member); + return; + } + } - if(sell->charges == 0) - { - Message(13, "Charge mismatch error."); - return; - } + if (gci.guild_id != GuildID()) { + Message(0, "You aren't in the same guild, what do you think you are doing?"); + return; + } - DeleteItemInInventory(sell->slot_id, sell->charges, false); - cost *= sell->charges; + if (IsCurrentlyABanker != NewBankerStatus) + { + if (!guild_mgr.SetBankerFlag(gci.char_id, NewBankerStatus)) { + Message(13, "Error setting guild banker flag."); + return; } - sell->cost = cost; + 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(); + + 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_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; + + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); + for (int16 L = EmuConstants::EQUIPMENT_BEGIN; L <= MainWaist; L++) { + const ItemInst* inst = GetInv().GetItem(L); + item = inst ? inst->GetItem() : nullptr; + + if (item) { + if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { + const Item_Struct *aug_weap = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = aug_weap->Icon; + } + else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { + strcpy(insr->itemnames[L], item->Name); + insr->itemicons[L] = inst->GetOrnamentationIcon(); + } + else { + 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) @@ -13511,14 +8439,985 @@ 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() || + (spells[spell_id].targettype == ST_Ring) || + (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; + } + + 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()) + { + SetLooting(ent->GetID()); //store the entity we are looting + 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); @@ -13527,41 +9426,52 @@ 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 (GetMercInfo().mercid) + { + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercPersonalInfo Request"); + SendMercPersonalInfo(); + } + else + { + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercPersonalInfo Not Sent - MercID (%i)", GetMercInfo().mercid); + } } - 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()); @@ -13569,10 +9479,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++; } @@ -13582,28 +9492,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; @@ -13611,19 +9521,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); @@ -13638,154 +9548,10 @@ void Client::Handle_OP_MercenaryDataRequest(const EQApplicationPacket *app) } } -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)) - { - LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryHire expected %i got %i", sizeof(MercenaryMerchantRequest_Struct), app->size); - - DumpPacket(app); - - return; - } - - 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) - 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)) - return; - - MercTemplate* merc_template = zone->GetMercTemplate(merc_template_id); - - if(merc_template) { - - Mob* merchant = entity_list.GetNPCByID(merchant_id); - if(!CheckCanHireMerc(merchant, merc_template_id)) { - return; - } - - if(RuleB(Mercs, ChargeMercPurchaseCost)) { - uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold - TakeMoneyFromPP(cost, true); - } - - // Set time remaining to max on Hire - GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - - // Get merc, assign it to client & spawn - Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id, false); - - if(merc) { - SpawnMerc(merc, true); - merc->Save(); - - // 0 is approved hire request - SendMercMerchantResponsePacket(0); - } - else { - //merc failed to spawn - SendMercMerchantResponsePacket(3); - } - } - else { - //merc doesn't exist in db - SendMercMerchantResponsePacket(2); - } -} - -void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) -{ - 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); - DumpPacket(app); - return; - } - - SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*) app->pBuffer; - uint32 merc_suspend = sm->SuspendMerc; // Seen 30 for suspending or unsuspending - - if(MERC_DEBUG > 0) - Message(7, "Mercenary Debug: Suspend ( %i ) received.", merc_suspend); - - 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) + 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); @@ -13793,10 +9559,10 @@ void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app return; } - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Data Update Request Received."); - if(GetMercID()) + if (GetMercID()) { SendMercPersonalInfo(); } @@ -13805,7 +9571,7 @@ void Client::Handle_OP_MercenaryDataUpdateRequest(const EQApplicationPacket *app void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) { // The payload is 0 or 1 bytes. - if(app->size > 1) + 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); @@ -13814,33 +9580,119 @@ void Client::Handle_OP_MercenaryDismiss(const EQApplicationPacket *app) } uint8 Command = 0; - if(app->size > 0) + if (app->size > 0) { char *InBuffer = (char *)app->pBuffer; Command = VARSTRUCT_DECODE_TYPE(uint8, InBuffer); } - if(MERC_DEBUG > 0) + if (MERC_DEBUG > 0) Message(7, "Mercenary Debug: Dismiss Request ( %i ) Received.", Command); // Handle the dismiss here... - if(GetMercID()) { - Merc* merc = GetMerc(); + DismissMerc(GetMercInfo().mercid); - if(merc) { - if(CheckCanDismissMerc()) { - merc->Dismiss(); - } - } +} + +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)) + { + LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_MercenaryHire expected %i got %i", sizeof(MercenaryMerchantRequest_Struct), app->size); + + DumpPacket(app); + + return; } - //SendMercMerchantResponsePacket(10); + 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) + 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)) + return; + + MercTemplate* merc_template = zone->GetMercTemplate(merc_template_id); + + if (merc_template) + { + + Mob* merchant = entity_list.GetNPCByID(merchant_id); + if (!CheckCanHireMerc(merchant, merc_template_id)) + { + return; + } + + // Set time remaining to max on Hire + GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); + + // Get merc, assign it to client & spawn + Merc* merc = Merc::LoadMerc(this, merc_template, merchant_id, false); + + if (merc) + { + SpawnMerc(merc, true); + merc->Save(); + + if (RuleB(Mercs, ChargeMercPurchaseCost)) + { + uint32 cost = Merc::CalcPurchaseCost(merc_template->MercTemplateID, GetLevel()) * 100; // Cost is in gold + TakeMoneyFromPP(cost, true); + } + + // approved hire request + SendMercMerchantResponsePacket(0); + } + else + { + //merc failed to spawn + SendMercMerchantResponsePacket(3); + } + } + else + { + //merc doesn't exist in db + SendMercMerchantResponsePacket(2); + } +} + +void Client::Handle_OP_MercenarySuspendRequest(const EQApplicationPacket *app) +{ + 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); + DumpPacket(app); + return; + } + + SuspendMercenary_Struct* sm = (SuspendMercenary_Struct*)app->pBuffer; + uint32 merc_suspend = sm->SuspendMerc; // Seen 30 for suspending or unsuspending + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Suspend ( %i ) received.", merc_suspend); + + if (!RuleB(Mercs, AllowMercs)) + return; + + // Check if the merc is suspended and if so, unsuspend, otherwise suspend it + SuspendMercCommand(); } 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); @@ -13848,10 +9700,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; } @@ -13860,30 +9712,107 @@ 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); + + bool error = false; + InterrogateInventory(this, false, true, false, error, false); + if (error) + InterrogateInventory(this, true, false, true, error); + } + + 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 @@ -13901,6 +9830,4276 @@ 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 = INVALID_INDEX; + 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; + } + + // this area needs some work..two inventory insertion check failure points + // below do not return player's money..is this the intended behavior? + + 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); + + // shouldn't we be reimbursing if these two fail? + + //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 (!stacked && 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 + // stacking purchases not supported at this time - entire process will need some work to catch them properly + 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 == INVALID_INDEX ? 0 : freeslotid; + qsaudit->items[0].item_id = item->ID; + qsaudit->items[0].charges = mpo->quantity; + + if (freeslotid == INVALID_INDEX) { + qsaudit->items[0].aug_1 = 0; + qsaudit->items[0].aug_2 = 0; + qsaudit->items[0].aug_3 = 0; + qsaudit->items[0].aug_4 = 0; + qsaudit->items[0].aug_5 = 0; + } + else { + qsaudit->items[0].aug_1 = m_inv[freeslotid]->GetAugmentItemID(0); + qsaudit->items[0].aug_2 = m_inv[freeslotid]->GetAugmentItemID(1); + qsaudit->items[0].aug_3 = m_inv[freeslotid]->GetAugmentItemID(2); + qsaudit->items[0].aug_4 = m_inv[freeslotid]->GetAugmentItemID(3); + qsaudit->items[0].aug_5 = m_inv[freeslotid]->GetAugmentItemID(4); + } + + 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; + + /* Save Spell Swaps */ + if (!database.SaveCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot)){ + database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[swapspell->from_slot], swapspell->from_slot); + } + if (!database.SaveCharacterSpell(this->CharacterID(), swapspelltemp, swapspell->to_slot)){ + database.DeleteCharacterSpell(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.spell_id; + 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.zone_id && + zone->GetInstanceID() == PendingTranslocateData.instance_id)) + { + PendingTranslocate = false; + GoToBind(); + return; + } + + ////Was sending the packet back to initiate client zone... + ////but that could be abusable, so lets go through proper channels + MovePC(PendingTranslocateData.zone_id, PendingTranslocateData.instance_id, + PendingTranslocateData.x, PendingTranslocateData.y, + PendingTranslocateData.z, PendingTranslocateData.heading, 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 1b687edb9..51b6713b7 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -1,291 +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_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 6045881e1..c2e3ae1f3 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -20,13 +20,8 @@ */ #include "../common/debug.h" #include -#include -#include -#include #include -#include #include -#include #ifdef _WINDOWS #include @@ -41,29 +36,22 @@ #include #endif -#include "masterentity.h" -#include "zonedb.h" -#include "../common/packet_functions.h" -#include "../common/packet_dump.h" -#include "worldserver.h" -#include "../common/packet_dump_file.h" -#include "../common/StringUtil.h" -#include "../common/spdat.h" -#include "petitions.h" -#include "NpcAI.h" -#include "../common/skills.h" -#include "forage.h" -#include "zone.h" -#include "event_codes.h" -#include "../common/faction.h" -#include "../common/crc32.h" #include "../common/rulesys.h" -#include "StringIDs.h" -#include "map.h" +#include "../common/skills.h" +#include "../common/spdat.h" +#include "../common/string_util.h" +#include "event_codes.h" #include "guild_mgr.h" -#include -#include "QuestParserCollection.h" +#include "map.h" +#include "petitions.h" +#include "queryserv.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "worldserver.h" +#include "zone.h" +#include "zonedb.h" +extern QueryServ* QServ; extern Zone* zone; extern volatile bool ZoneLoaded; extern WorldServer worldserver; @@ -76,7 +64,8 @@ bool Client::Process() { if(Connected() || IsLD()) { // try to send all packets that weren't sent before - if(!IsLD() && zoneinpacket_timer.Check()){ + if(!IsLD() && zoneinpacket_timer.Check()) + { SendAllPackets(); } @@ -143,10 +132,12 @@ bool Client::Process() { if(mana_timer.Check()) SendManaUpdatePacket(); - if(dead && dead_timer.Check()) { - database.MoveCharacterToZone(GetName(),database.GetZoneName(m_pp.binds[0].zoneId)); + + if(dead && dead_timer.Check()) { + database.MoveCharacterToZone(GetName(), database.GetZoneName(m_pp.binds[0].zoneId)); + m_pp.zone_id = m_pp.binds[0].zoneId; - m_pp.zoneInstance = 0; + m_pp.zoneInstance = m_pp.binds[0].instance_id; m_pp.x = m_pp.binds[0].x; m_pp.y = m_pp.binds[0].y; m_pp.z = m_pp.binds[0].z; @@ -174,14 +165,16 @@ bool Client::Process() { if(TaskPeriodic_Timer.Check() && taskstate) taskstate->TaskPeriodicChecks(this); - if(linkdead_timer.Check()){ + if(linkdead_timer.Check()) + { + LeaveGroup(); Save(); if (GetMerc()) { GetMerc()->Save(); GetMerc()->Depop(); } - LeaveGroup(); + Raid *myraid = entity_list.GetRaidByClient(this); if (myraid) { @@ -190,7 +183,8 @@ bool Client::Process() { return false; //delete client } - if (camp_timer.Check()) { + if (camp_timer.Check()) + { LeaveGroup(); Save(); if (GetMerc()) @@ -226,20 +220,21 @@ bool Client::Process() { } else { if(!ApplyNextBardPulse(bardsong, song_target, bardsong_slot)) InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong); -// SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana); + //SpellFinished(bardsong, bardsong_target, bardsong_slot, spells[bardsong].mana); } } if(GetMerc()) { - UpdateMercTimer(); + UpdateMercTimer(); } if(GetMercInfo().MercTemplateID != 0 && GetMercInfo().IsSuspended) { - if(p_timers.Expired(&database, pTimerMercSuspend, false)) { - CheckMercSuspendTimer(); - } + if(p_timers.Expired(&database, pTimerMercSuspend, false)) + { + CheckMercSuspendTimer(); + } } if(IsAIControlled()) @@ -570,8 +565,7 @@ bool Client::Process() { viral_timer_counter = 0; } - if(projectile_timer.Check()) - SpellProjectileEffect(); + ProjectileAttack(); if(spellbonuses.GravityEffect == 1) { if(gravity_timer.Check()) @@ -713,16 +707,13 @@ bool Client::Process() { } #endif - if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) { + if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) + { //client logged out or errored out //ResetTrade(); if (client_state != CLIENT_KICKED) { Save(); } - if (GetMerc()) - { - GetMerc()->Depop(); - } client_state = CLIENT_LINKDEAD; if (zoning || instalog || GetGM()) @@ -730,23 +721,32 @@ bool Client::Process() { Group *mygroup = GetGroup(); if (mygroup) { - if (!zoning) { + if (!zoning) + { entity_list.MessageGroup(this, true, 15, "%s logged out.", GetName()); - mygroup->DelMember(this); - } else { + LeaveGroup(); + } + else + { entity_list.MessageGroup(this, true, 15, "%s left the zone.", GetName()); mygroup->MemberZoned(this); + if (GetMerc() && GetMerc()->HasGroup()) + { + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + } } } Raid *myraid = entity_list.GetRaidByClient(this); if (myraid) { - if (!zoning) { + if (!zoning) + { //entity_list.MessageGroup(this,true,15,"%s logged out.",GetName()); - //mygroup->DelMember(this); myraid->MemberZoned(this); - } else { + } + else + { //entity_list.MessageGroup(this,true,15,"%s left the zone.",GetName()); myraid->MemberZoned(this); } @@ -770,38 +770,47 @@ bool Client::Process() { return ret; } -//just a set of actions preformed all over in Client::Process +/* Just a set of actions preformed all over in Client::Process */ void Client::OnDisconnect(bool hard_disconnect) { - if(hard_disconnect) { + if(hard_disconnect) + { LeaveGroup(); - + if (GetMerc()) + { + GetMerc()->Save(); + GetMerc()->Depop(); + } Raid *MyRaid = entity_list.GetRaidByClient(this); if (MyRaid) MyRaid->MemberZoned(this); - parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); + parse->EventPlayer(EVENT_DISCONNECT, this, "", 0); + + /* QS: PlayerLogConnectDisconnect */ + if (RuleB(QueryServ, PlayerLogConnectDisconnect)){ + std::string event_desc = StringFormat("Disconnect :: in zoneid:%i instid:%i", this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Connect_State, this->CharacterID(), event_desc); + } } - Mob *Other = trade->With(); - + Mob *Other = trade->With(); if(Other) { - mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items."); - + mlog(TRADING__CLIENT, "Client disconnected during a trade. Returning their items."); FinishTrade(this); if(Other->IsClient()) Other->CastToClient()->FinishTrade(Other); - trade->Reset(); - + /* Reset both sides of the trade */ + trade->Reset(); Other->trade->Reset(); } database.SetFirstLogon(CharacterID(), 0); //We change firstlogon status regardless of if a player logs out to zone or not, because we only want to trigger it on their first login from world. - //remove ourself from all proximities + /* Remove ourself from all proximities */ ClearAllProximities(); EQApplicationPacket *outapp = new EQApplicationPacket(OP_LogoutReply); @@ -832,8 +841,6 @@ void Client::BulkSendInventoryItems() { } } - // Where are cursor buffer items processed? They need to be validated as well... -U - bool deletenorent = database.NoRentExpired(GetName()); if(deletenorent){ RemoveNoRent(false); } //client was offline for more than 30 minutes, delete no rent items @@ -965,92 +972,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(GetLevel() < ml.level_required) { + if (merch->CastToNPC()->GetMerchantProbability() > ml.probability) + continue; + + 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) @@ -1066,32 +1077,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()); } @@ -1544,7 +1555,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"); } } } @@ -1577,7 +1593,7 @@ void Client::OPMoveCoin(const EQApplicationPacket* app) safe_delete(outapp); } - Save(); + SaveCurrency(); } void Client::OPGMTraining(const EQApplicationPacket *app) @@ -1600,16 +1616,19 @@ void Client::OPGMTraining(const EQApplicationPacket *app) if(DistNoRoot(*pTrainer) > USE_NPC_RANGE2) return; - SkillUseTypes sk; - for (sk = Skill1HBlunt; sk <= HIGHEST_SKILL; sk = (SkillUseTypes)(sk+1)) { + // if this for-loop acts up again (crashes linux), try enabling the before and after #pragmas +//#pragma GCC push_options +//#pragma GCC optimize ("O0") + for (int sk = Skill1HBlunt; sk <= HIGHEST_SKILL; ++sk) { if(sk == SkillTinkering && GetRace() != GNOME) { gmtrain->skills[sk] = 0; //Non gnomes can't tinker! } else { - gmtrain->skills[sk] = GetMaxSkillAfterSpecializationRules(sk, MaxSkill(sk, GetClass(), RuleI(Character, MaxLevel))); + gmtrain->skills[sk] = GetMaxSkillAfterSpecializationRules((SkillUseTypes)sk, MaxSkill((SkillUseTypes)sk, GetClass(), RuleI(Character, MaxLevel))); //this is the highest level that the trainer can train you to, this is enforced clientside so we can't just //Set it to 1 with CanHaveSkill or you wont be able to train past 1. } } +//#pragma GCC pop_options uchar ending[]={0x34,0x87,0x8a,0x3F,0x01 ,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9,0xC9 @@ -1712,6 +1731,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()); @@ -1721,7 +1741,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app) } SetSkill(skill, t_level); - } else { + } else { switch(skill) { case SkillBrewing: case SkillMakePoison: @@ -1917,16 +1937,21 @@ void Client::DoEnduranceRegen() } void Client::DoEnduranceUpkeep() { + + if (!HasEndurUpkeep()) + return; + int upkeep_sum = 0; - - int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction; - + int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction + aabonuses.EnduranceReduction; + + bool has_effect = false; uint32 buffs_i; uint32 buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep; if(upkeep > 0) { + has_effect = true; if(cost_redux > 0) { if(upkeep <= cost_redux) continue; //reduced to 0 @@ -1946,6 +1971,9 @@ void Client::DoEnduranceUpkeep() { SetEndurance(GetEndurance() - upkeep_sum); TryTriggerOnValueAmount(false, false, true); } + + if (!has_effect) + SetEndurUpkeep(false); } void Client::CalcRestState() { @@ -2076,7 +2104,8 @@ void Client::HandleRespawnFromHover(uint32 Option) BindStruct* b = &m_pp.binds[0]; default_to_bind = new RespawnOption; default_to_bind->name = "Bind Location"; - default_to_bind->zoneid = b->zoneId; + default_to_bind->zone_id = b->zoneId; + default_to_bind->instance_id = b->instance_id; default_to_bind->x = b->x; default_to_bind->y = b->y; default_to_bind->z = b->z; @@ -2085,7 +2114,7 @@ void Client::HandleRespawnFromHover(uint32 Option) is_rez = false; } - if (chosen->zoneid == zone->GetZoneID()) //If they should respawn in the current zone... + if (chosen->zone_id == zone->GetZoneID() && chosen->instance_id == zone->GetInstanceID()) //If they should respawn in the current zone... { if (is_rez) { @@ -2129,8 +2158,8 @@ void Client::HandleRespawnFromHover(uint32 Option) _log(SPELLS__REZ, "Found corpse. Marking corpse as rezzed."); - corpse->Rezzed(true); - corpse->CompleteRezz(); + corpse->IsRezzed(true); + corpse->CompleteResurrection(); } } else //Not rez @@ -2141,6 +2170,7 @@ void Client::HandleRespawnFromHover(uint32 Option) ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; gmg->bind_zone_id = zone->GetZoneID(); + gmg->bind_instance_id = chosen->instance_id; gmg->x = chosen->x; gmg->y = chosen->y; gmg->z = chosen->z; @@ -2186,13 +2216,13 @@ void Client::HandleRespawnFromHover(uint32 Option) if(r) r->MemberZoned(this); - m_pp.zone_id = chosen->zoneid; - m_pp.zoneInstance = 0; - database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(chosen->zoneid)); + m_pp.zone_id = chosen->zone_id; + m_pp.zoneInstance = chosen->instance_id; + database.MoveCharacterToZone(CharacterID(), database.GetZoneName(chosen->zone_id)); Save(); - MovePC(chosen->zoneid,chosen->x,chosen->y,chosen->z,chosen->heading,1); + MovePC(chosen->zone_id, chosen->instance_id, chosen->x, chosen->y, chosen->z, chosen->heading, 1); } safe_delete(default_to_bind); diff --git a/zone/command.cpp b/zone/command.cpp index 11f60bfd1..f4c2c8e91 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -42,37 +42,34 @@ #endif #include "../common/debug.h" -#include "../common/ptimer.h" -#include "../common/packet_functions.h" -#include "../common/packet_dump.h" -#include "../common/serverinfo.h" -#include "../common/opcodemgr.h" -#include "../common/EQPacket.h" -#include "../common/guilds.h" -#include "../common/rulesys.h" -#include "../common/StringUtil.h" -//#include "../common/servertalk.h" // for oocmute and revoke -#include "worldserver.h" -#include "masterentity.h" -#include "map.h" -#include "water_map.h" +#include "../common/eq_packet.h" #include "../common/features.h" -#include "pathing.h" -#include "client_logs.h" -#include "guild_mgr.h" -#include "titles.h" +#include "../common/guilds.h" #include "../common/patches/patches.h" +#include "../common/ptimer.h" +#include "../common/rulesys.h" +#include "../common/serverinfo.h" +#include "../common/string_util.h" -// these should be in the headers... +#include "client_logs.h" +#include "command.h" +#include "guild_mgr.h" +#include "map.h" +#include "pathing.h" +#include "qglobals.h" +#include "queryserv.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "titles.h" +#include "water_map.h" +#include "worldserver.h" + +extern QueryServ* QServ; extern WorldServer worldserver; extern TaskManager *taskmanager; void CatchSignal(int sig_num); -#include "QuestParserCollection.h" -#include "StringIDs.h" -#include "command.h" -#include "QGlobals.h" //struct cl_struct *commandlist; // the actual linked list of commands int commandcount; // how many commands we have @@ -148,32 +145,20 @@ Access Levels: int command_init(void) { if ( - command_add("resetaa","- Resets a Player's AA in their profile.",200,command_resetaa) || - command_add("qtest","- QueryServ testing command.",255,command_qtest) || + 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("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) || - command_add("optest","- solar's private test command",255,command_optest) || command_add("setstat","- Sets the stats to a specific value.",255,command_setstat) || command_add("incstat","- Increases or Decreases a client's stats permanently.",200,command_incstat) || command_add("help","[search term] - List available commands and their description, specify partial command as argument to search",0,command_help) || command_add("version","- Display current version of EQEmu server",0,command_version) || command_add("setfaction","[faction number] - Sets targeted NPC's faction in the database",170,command_setfaction) || - command_add("serversidename","- Prints target's server side name",0,command_serversidename) || - command_add("testspawn","[memloc] [value] - spawns a NPC for you only, with the specified values set in the spawn struct",200,command_testspawn) || - command_add("testspawnkill","- Sends an OP_Death packet for spawn made with #testspawn",200,command_testspawnkill) || command_add("wc","[wear slot] [material] - Sends an OP_WearChange for your target",200,command_wc) || - command_add("numauths","- TODO: describe this command",200,command_numauths) || command_add("setanim","[animnum] - Set target's appearance to animnum",200,command_setanim) || command_add("connectworldserver","- Make zone attempt to connect to worldserver",200,command_connectworldserver) || command_add("connectworld",nullptr,0,command_connectworldserver) || command_add("serverinfo","- Get OS info about server host",200,command_serverinfo) || - command_add("crashtest","- Crash the zoneserver",200,command_crashtest) || command_add("getvariable","[varname] - Get the value of a variable from the database",200,command_getvariable) || command_add("chat","[channel num] [message] - Send a channel message to all zones",200,command_chat) || - command_add("showpetspell","[spellid/searchstring] - search pet summoning spells",200,command_showpetspell) || - #ifdef IPC - command_add("ipc","- Toggle an NPC's interactive flag",200,command_ipc) || - #endif command_add("npcloot","[show/money/add/remove] [itemid/all/money: pp gp sp cp] - Manipulate the loot an NPC is carrying",80,command_npcloot) || command_add("log","- Search character event log",80,command_log) || command_add("gm","- Turn player target's or your GM flag on or off",80,command_gm) || @@ -181,7 +166,6 @@ int command_init(void) { command_add("zone","[zonename] [x] [y] [z] - Go to specified zone (coords optional)",50,command_zone) || command_add("zoneinstance","[instanceid] [x] [y] [z] - Go to specified instance zone (coords optional)",50,command_zone_instance) || command_add("peqzone","[zonename] - Go to specified zone, if you have > 75% health",0,command_peqzone) || - command_add("tgczone",nullptr,0,command_peqzone) || command_add("showbuffs","- List buffs active on your target or you if no target",50,command_showbuffs) || command_add("movechar","[charname] [zonename] - Move charname to zonename",50,command_movechar) || command_add("viewpetition","[petition number] - View a petition",20,command_viewpetition) || @@ -191,14 +175,12 @@ int command_init(void) { command_add("date","[yyyy] [mm] [dd] [HH] [MM] - Set EQ time",90,command_date) || command_add("time","[HH] [MM] - Set EQ time",90,command_time) || command_add("timezone","[HH] [MM] - Set timezone. Minutes are optional",90,command_timezone) || - command_add("synctod","- Send a time of day update to every client in zone",90,command_synctod) || command_add("invulnerable","[on/off] - Turn player target's or your invulnerable flag on or off",80,command_invul) || command_add("invul",nullptr,0,command_invul) || command_add("hideme","[on/off] - Hide yourself from spawn lists.",80,command_hideme) || command_add("gmhideme",nullptr,0,command_hideme) || command_add("emote","['name'/'world'/'zone'] [type] [message] - Send an emote message",80,command_emote) || command_add("fov","- Check wether you're behind or in your target's field of view",80,command_fov) || - command_add("manastat","- Report your or your target's cur/max mana",80,command_manastat) || command_add("npcstats","- Show stats about target NPC",80,command_npcstats) || command_add("zclip","[min] [max] - modifies and resends zhdr packet",80,command_zclip) || command_add("npccast","[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid",80,command_npccast) || @@ -209,7 +191,6 @@ int command_init(void) { command_add("permagender","[gendernum] - Change your or your player target's gender (zone to take effect)",80,command_permagender) || command_add("weather","[0/1/2/3] (Off/Rain/Snow/Manual) - Change the weather",80,command_weather) || command_add("zheader","[zonename] - Load zheader for zonename from the database",80,command_zheader) || - command_add("zhdr",nullptr,0,command_zheader) || command_add("zsky","[skytype] - Change zone sky type",80,command_zsky) || command_add("zcolor","[red] [green] [blue] - Change sky color",80,command_zcolor) || command_add("zuwcoords","[z coord] - Set underworld coord",80,command_zuwcoords) || @@ -217,14 +198,12 @@ int command_init(void) { command_add("zunderworld","[zcoord] - Sets the underworld using zcoord",80,command_zunderworld) || command_add("spon","- Sends OP_MemorizeSpell",80,command_spon) || command_add("spoff","- Sends OP_ManaChange",80,command_spoff) || - command_add("itemtest","- merth's test function",250,command_itemtest) || command_add("gassign","[id] - Assign targetted NPC to predefined wandering grid id",100,command_gassign) || command_add("ai","[factionid/spellslist/con/guard/roambox/stop/start] - Modify AI on NPC target",100,command_ai) || command_add("showspellslist","Shows spell list of targeted NPC",100,command_showspellslist) || 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) || @@ -254,22 +233,20 @@ int command_init(void) { command_add("spawn","[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC",10,command_spawn) || command_add("texture","[texture] [helmtexture] - Change your or your target's appearance, use 255 to show equipment",10,command_texture) || command_add("npctypespawn","[npctypeid] [factionid] - Spawn an NPC from the db",10,command_npctypespawn) || - command_add("dbspawn",nullptr,0,command_npctypespawn) || + command_add("dbspawn",nullptr,10,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("peekinv","[worn/inv/cursor/trib/bank/trade/world/all] - Print out contents of your player target's inventory",100,command_peekinv) || + command_add("interrogateinv","- use [help] argument for available options",0,command_interrogateinv) || 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("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) || command_add("reloadqst"," - Clear quest cache (any argument causes it to also stop all timers)",150,command_reloadqst) || command_add("reloadworld",nullptr,255,command_reloadworld) || command_add("reloadlevelmods",nullptr,255,command_reloadlevelmods) || - command_add("rq",nullptr,0,command_reloadqst) || command_add("reloadzonepoints","- Reload zone points from database",150,command_reloadzps) || command_add("reloadzps",nullptr,0,command_reloadzps) || command_add("zoneshutdown","[shortname] - Shut down a zone server",150,command_zoneshutdown) || @@ -332,11 +309,8 @@ 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) || command_add("face","- Change the face of your target",80,command_face) || command_add("helm","- Change the helm of your target",80,command_helm) || command_add("hair","- Change the hair style of your target",80,command_hair) || @@ -351,7 +325,6 @@ int command_init(void) { command_add("scribespell", "[spellid] - Scribe specified spell in your target's spell book.", 180, command_scribespell) || command_add("unscribespell", "[spellid] - Unscribe specified spell from your target's spell book.", 180, command_unscribespell) || command_add("interrupt","[message id] [color] - Interrupt your casting. Arguments are optional.",50,command_interrupt) || - command_add("d1","[type] [spell] [damage] - Send an OP_Action packet with the specified values",200,command_d1) || command_add("summonitem","[itemid] [charges] - Summon an item onto your cursor. Charges are optional.",200,command_summonitem) || command_add("si",nullptr,200,command_summonitem) || command_add("giveitem","[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.",200,command_giveitem) || @@ -373,8 +346,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) || @@ -385,7 +358,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) || @@ -406,7 +379,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) || @@ -452,7 +424,8 @@ int command_init(void) { command_add("merchant_open_shop", "Opens a merchants shop", 100, command_merchantopenshop) || command_add("open_shop", nullptr, 100, command_merchantopenshop) || command_add("merchant_close_shop", "Closes a merchant shop", 100, command_merchantcloseshop) || - command_add("close_shop", nullptr, 100, command_merchantcloseshop) + command_add("close_shop", nullptr, 100, command_merchantcloseshop) || + command_add("shownumhits", "Shows buffs numhits for yourself.", 0, command_shownumhits) ) { command_deinit(); @@ -588,6 +561,12 @@ int command_realdispatch(Client *c, const char *message) return(-1); } + /* 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); + } + #ifdef COMMANDS_LOGGING if(cur->access >= COMMANDS_LOGGING_MIN_STATUS) { LogFile->write(EQEMuLog::Commands, "%s (%s) used command: %s (target=%s)", c->GetName(), c->AccountName(), message, c->GetTarget()?c->GetTarget()->GetName():"NONE"); @@ -705,77 +684,6 @@ void command_resetaa(Client* c,const Seperator *sep){ c->Message(0,"Usage: Target a client and use #resetaa to reset the AA data in their Profile."); } -void command_sendop(Client *c,const Seperator *sep){ - - int RezSpell = 0; - - if(sep->arg[1][0]) { - RezSpell = atoi(sep->arg[1]); - } - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RezzRequest, sizeof(Resurrect_Struct)); - Resurrect_Struct *rs = (Resurrect_Struct*)outapp->pBuffer; - - strcpy(rs->your_name, c->GetName()); - strcpy(rs->rezzer_name, "A Cleric01"); - rs->spellid = RezSpell; - DumpPacket(outapp); - c->QueuePacket(outapp); - safe_delete(outapp); - return; -} - -void command_optest(Client *c, const Seperator *sep) -{ - if(sep->IsNumber(1)) - { - switch(atoi(sep->arg[1])) - { - case 1: - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureFinish, sizeof(AdventureFinish_Struct)); - AdventureFinish_Struct *af = (AdventureFinish_Struct*)outapp->pBuffer; - af->win_lose = 1; - af->points = 125; - c->FastQueuePacket(&outapp); - break; - } - case 2: - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AdventureData, sizeof(AdventureRequestResponse_Struct)); - AdventureRequestResponse_Struct *arr = (AdventureRequestResponse_Struct*)outapp->pBuffer; - if(sep->IsHexNumber(2)) - arr->unknown000 = hextoi(sep->arg[2]); - else - arr->unknown000 = 0xBFC40100; - arr->risk = 1; - arr->showcompass = 1; - strcpy(arr->text, "This is some text for an adventure packet!\0"); - arr->timeleft = 60*60; - //arr->timetoenter = 30*60; - if(sep->IsHexNumber(3)) - arr->unknown2080=hextoi(sep->arg[3]); - else - arr->unknown2080=0x0A; - arr->x = c->GetY(); - arr->y = c->GetX(); - arr->z = c->GetZ(); - c->FastQueuePacket(&outapp); - break; - } - case 3: - { - c->SendMarqueeMessage(15, 250, 0, 500, 5000, "Some msg"); - break; - } - default: - { - break; - } - } - } -} - void command_help(Client *c, const Seperator *sep) { int commands_shown=0; @@ -812,17 +720,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) @@ -833,50 +742,6 @@ void command_serversidename(Client *c, const Seperator *sep) c->Message(0, "Error: no target"); } -void command_testspawnkill(Client *c, const Seperator *sep) -{ -/* EQApplicationPacket* outapp = new EQApplicationPacket(OP_Death, sizeof(Death_Struct)); - Death_Struct* d = (Death_Struct*)outapp->pBuffer; - d->corpseid = 1000; - // d->unknown011 = 0x05; - d->spawn_id = 1000; - d->killer_id = c->GetID(); - d->damage = 1; - d->spell_id = 0; - d->type = BASH; - d->bindzoneid = 0; - c->FastQueuePacket(&outapp);*/ -} - -void command_testspawn(Client *c, const Seperator *sep) -{ - if (sep->IsNumber(1)) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_NewSpawn, sizeof(NewSpawn_Struct)); - NewSpawn_Struct* ns = (NewSpawn_Struct*)outapp->pBuffer; - c->FillSpawnStruct(ns, c); - strcpy(ns->spawn.name, "Test"); - ns->spawn.spawnId = 1000; - ns->spawn.NPC = 1; - if (sep->IsHexNumber(2)) { - if (strlen(sep->arg[2]) >= 3) // 0x00, 1 byte - *(&((uint8*) &ns->spawn)[atoi(sep->arg[1])]) = hextoi(sep->arg[2]); - else if (strlen(sep->arg[2]) >= 5) // 0x0000, 2 bytes - *((uint16*) &(((uint8*) &ns->spawn)[atoi(sep->arg[1])])) = hextoi(sep->arg[2]); - else if (strlen(sep->arg[2]) >= 9) // 0x0000, 2 bytes - *((uint32*) &(((uint8*) &ns->spawn)[atoi(sep->arg[1])])) = hextoi(sep->arg[2]); - else - c->Message(0, "Error: unexpected hex string length"); - } - else { - strcpy((char*) (&((uint8*) &ns->spawn)[atoi(sep->arg[1])]), sep->argplus[2]); - } - EncryptSpawnPacket(outapp); - c->FastQueuePacket(&outapp); - } - else - c->Message(0, "Usage: #testspawn [memloc] [value] - spawns a NPC for you only, with the specified values set in the spawn struct"); -} - void command_wc(Client *c, const Seperator *sep) { if(sep->argnum < 2) @@ -922,12 +787,6 @@ void command_wc(Client *c, const Seperator *sep) } } -void command_numauths(Client *c, const Seperator *sep) -{ - c->Message(0, "NumAuths: %i", zone->CountAuth()); - c->Message(0, "Your WID: %i", c->GetWID()); -} - void command_setanim(Client *c, const Seperator *sep) { if (c->GetTarget() && sep->IsNumber(1)) { @@ -967,13 +826,6 @@ char buffer[255]; #endif } -void command_crashtest(Client *c, const Seperator *sep) -{ - c->Message(0,"Alright, now we get an GPF ;) "); - char* gpf=0; - memcpy(gpf, "Ready to crash", 30); -} - void command_getvariable(Client *c, const Seperator *sep) { char tmp[512]; @@ -992,71 +844,6 @@ void command_chat(Client *c, const Seperator *sep) c->Message(0, "Error: World server disconnected"); } -void command_showpetspell(Client *c, const Seperator *sep) -{ - if (sep->arg[1][0] == 0) - c->Message(0, "Usage: #ShowPetSpells [spellid | searchstring]"); - else if (SPDAT_RECORDS <= 0) - c->Message(0, "Spells not loaded"); - else if (Seperator::IsNumber(sep->argplus[1])) - { - int spellid = atoi(sep->argplus[1]); - if (spellid <= 0 || spellid >= SPDAT_RECORDS) - c->Message(0, "Error: Number out of range"); - else - c->Message(0, " %i: %s, %s", spellid, spells[spellid].teleport_zone, spells[spellid].name); - } - else - { - int count=0; - char sName[64]; - char sCriteria[65]; - strn0cpy(sCriteria, sep->argplus[1], 64); - strupr(sCriteria); - for (int i = 0; i < SPDAT_RECORDS; i++) - { - if (spells[i].name[0] != 0 && (spells[i].effectid[0] == SE_SummonPet || spells[i].effectid[0] == SE_NecPet)) - { - strcpy(sName, spells[i].teleport_zone); - strupr(sName); - char* pdest = strstr(sName, sCriteria); - if ((pdest != nullptr) && (count <=20)) - { - c->Message(0, " %i: %s, %s", i, spells[i].teleport_zone, spells[i].name); - count++; - } - else if (count > 20) - break; - } - } - if (count > 20) - c->Message(0, "20 spells found... max reached."); - else - c->Message(0, "%i spells found.", count); - } -} - -#ifdef IPC -void command_ipc(Client *c, const Seperator *sep) -{ - if (c->GetTarget() && c->GetTarget()->IsNPC()) - { - if (c->GetTarget()->CastToNPC()->IsInteractive()) - { - c->GetTarget()->CastToNPC()->interactive = false; - c->Message(0, "Disabling IPC"); - } - else - { - c->GetTarget()->CastToNPC()->interactive = true; - c->Message(0, "Enabling IPC"); - } - } - else - c->Message(0, "Error: You must target an NPC"); -} -#endif /* IPC */ - void command_npcloot(Client *c, const Seperator *sep) { if (c->GetTarget() == 0) @@ -1506,77 +1293,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) @@ -1642,16 +1423,6 @@ void command_timezone(Client *c, const Seperator *sep) } } -void command_synctod(Client *c, const Seperator *sep) -{ - c->Message(13, "Updating Time/Date for all clients in zone..."); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_TimeOfDay, sizeof(TimeOfDay_Struct)); - TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer; - zone->zone_time.getEQTimeOfDay(time(0), tod); - entity_list.QueueClients(c, outapp); - safe_delete(outapp); -} - void command_invul(Client *c, const Seperator *sep) { bool state=atobool(sep->arg[1]); @@ -1715,15 +1486,6 @@ void command_fov(Client *c, const Seperator *sep) c->Message(0, "I Need a target!"); } -void command_manastat(Client *c, const Seperator *sep) -{ - Mob *target=c->GetTarget()?c->GetTarget():c; - - c->Message(0, "Mana for %s:", target->GetName()); - c->Message(0, " Current Mana: %d",target->GetMana()); - c->Message(0, " Max Mana: %d",target->GetMaxMana()); -} - void command_npcstats(Client *c, const Seperator *sep) { if (c->GetTarget() == 0) @@ -2150,8 +1912,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; @@ -2220,28 +1982,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); @@ -2416,7 +2156,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) @@ -2484,14 +2224,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); } } @@ -2783,85 +2523,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; @@ -2877,450 +2538,473 @@ void command_nukeitem(Client *c, const Seperator *sep) void command_peekinv(Client *c, const Seperator *sep) { - // Displays what the server thinks the user has in inventory + enum { + peekWorn = 0x01, + peekInv = 0x02, + peekCursor = 0x04, + peekTrib = 0x08, + peekBank = 0x10, + peekTrade = 0x20, + peekWorld = 0x40 + } ; + if (!c->GetTarget() || !c->GetTarget()->IsClient()) { c->Message(0, "You must have a PC target selected for this command"); return; } - bool bAll = (strcasecmp(sep->arg[1], "all") == 0); - bool bFound = false; - Client* client = c->GetTarget()->CastToClient(); - const Item_Struct* item = nullptr; - c->Message(0, "Displaying inventory for %s...", client->GetName()); + int scopeWhere = 0; - if (bAll || (strcasecmp(sep->arg[1], "worn")==0)) { - // Worn items - bFound = true; - 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())); - } - } - } - 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())); - } + if (strcasecmp(sep->arg[1], "all") == 0) { scopeWhere = ~0; } + else if (strcasecmp(sep->arg[1], "worn") == 0) { scopeWhere |= peekWorn; } + else if (strcasecmp(sep->arg[1], "inv") == 0) { scopeWhere |= peekInv; } + else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeWhere |= peekCursor; } + else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeWhere |= peekTrib; } + else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeWhere |= peekBank; } + else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeWhere |= peekTrade; } + else if (strcasecmp(sep->arg[1], "world") == 0) { scopeWhere |= peekWorld; } - 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())); - } - } - } - } - 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...) - // - was pointless to show bags on anything after slot 30[0], because it only repeated the 30[0] bag items. - if (bAll || (strcasecmp(sep->arg[1], "cursor")==0)) { - // Personal inventory items - bFound = true; - iter_queue it; - 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); - } - } - 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())); - } - - 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())); - } - } - } - } - } - } - - if (bAll || (strcasecmp(sep->arg[1], "trib")==0)) { - // Active tribute effect items - bFound = true; - 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())); - } - } - } - - if (bAll || (strcasecmp(sep->arg[1], "bank")==0)) { - // Bank and shared bank items - bFound = true; - int16 i = 0; - 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())); - } - - 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())); - } - } - } - } - 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())); - } - - 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())); - } - } - } - } - } - 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())); - } - - 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())); - } - - } - } - } - } - - if (!bFound) - { - c->Message(0, "Usage: #peekinv [worn|cursor|inv|bank|trade|trib|all]"); + if (scopeWhere == 0) { + c->Message(0, "Usage: #peekinv [worn|inv|cursor|trib|bank|trade|world|all]"); c->Message(0, " Displays a portion of the targeted user's inventory"); c->Message(0, " Caution: 'all' is a lot of information!"); + return; } + + Client* targetClient = c->GetTarget()->CastToClient(); + const ItemInst* instMain = nullptr; + const ItemInst* instSub = nullptr; + const Item_Struct* itemData = nullptr; + char* itemLinkCore = nullptr; + std::string itemLink; + + c->Message(0, "Displaying inventory for %s...", targetClient->GetName()); + + // worn + for (int16 indexMain = EmuConstants::EQUIPMENT_BEGIN; (scopeWhere & peekWorn) && (indexMain <= EmuConstants::EQUIPMENT_END); ++indexMain) { + instMain = targetClient->GetInv().GetItem(indexMain); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", + indexMain, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + } + + if ((scopeWhere & peekWorn) && (targetClient->GetClientVersion() >= EQClientSoF)) { + instMain = targetClient->GetInv().GetItem(MainPowerSource); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "WornSlot: %i, Item: %i (%s), Charges: %i", + MainPowerSource, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + } + + // inv + for (int16 indexMain = EmuConstants::GENERAL_BEGIN; (scopeWhere & peekInv) && (indexMain <= EmuConstants::GENERAL_END); ++indexMain) { + instMain = targetClient->GetInv().GetItem(indexMain); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "InvSlot: %i, Item: %i (%s), Charges: %i", + indexMain, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + + for (uint8 indexSub = SUB_BEGIN; instMain && instMain->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { + instSub = instMain->GetItem(indexSub); + itemData = (instSub ? instSub->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instSub); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instSub->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instSub->GetCharges())); + + safe_delete_array(itemLinkCore); + } + } + + // cursor + if (scopeWhere & peekCursor) { + if (targetClient->GetInv().CursorEmpty()) { + c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i", + MainCursor, 0, "null", 0); + } + else { + int cursorDepth = 0; + for (iter_queue it = targetClient->GetInv().cursor_begin(); (it != targetClient->GetInv().cursor_end()); ++it, ++cursorDepth) { + instMain = *it; + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i", + MainCursor, cursorDepth, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + + for (uint8 indexSub = SUB_BEGIN; (cursorDepth == 0) && instMain && instMain->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { + instSub = instMain->GetItem(indexSub); + itemData = (instSub ? instSub->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instSub); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instSub->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(MainCursor, indexSub), MainCursor, indexSub, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instSub->GetCharges())); + + safe_delete_array(itemLinkCore); + } + } + } + } + + // trib + for (int16 indexMain = EmuConstants::TRIBUTE_BEGIN; (scopeWhere & peekTrib) && (indexMain <= EmuConstants::TRIBUTE_END); ++indexMain) { + instMain = targetClient->GetInv().GetItem(indexMain); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "TributeSlot: %i, Item: %i (%s), Charges: %i", + indexMain, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + } + + // bank + for (int16 indexMain = EmuConstants::BANK_BEGIN; (scopeWhere & peekBank) && (indexMain <= EmuConstants::BANK_END); ++indexMain) { + instMain = targetClient->GetInv().GetItem(indexMain); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null" ); + + c->Message((itemData == 0), "BankSlot: %i, Item: %i (%s), Charges: %i", + indexMain, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + + for (uint8 indexSub = SUB_BEGIN; instMain && instMain->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { + instSub = instMain->GetItem(indexSub); + itemData = (instSub ? instSub->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instSub); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instSub->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instSub->GetCharges())); + + safe_delete_array(itemLinkCore); + } + } + + for (int16 indexMain = EmuConstants::SHARED_BANK_BEGIN; (scopeWhere & peekBank) && (indexMain <= EmuConstants::SHARED_BANK_END); ++indexMain) { + instMain = targetClient->GetInv().GetItem(indexMain); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "SharedBankSlot: %i, Item: %i (%s), Charges: %i", + indexMain, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + + for (uint8 indexSub = SUB_BEGIN; instMain && instMain->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { + instSub = instMain->GetItem(indexSub); + itemData = (instSub ? instSub->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instSub); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instSub->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instSub->GetCharges())); + + safe_delete_array(itemLinkCore); + } + } + + // trade + for (int16 indexMain = EmuConstants::TRADE_BEGIN; (scopeWhere & peekTrade) && (indexMain <= EmuConstants::TRADE_END); ++indexMain) { + instMain = targetClient->GetInv().GetItem(indexMain); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "TradeSlot: %i, Item: %i (%s), Charges: %i", + indexMain, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + + for (uint8 indexSub = SUB_BEGIN; instMain && instMain->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { + instSub = instMain->GetItem(indexSub); + itemData = (instSub ? instSub->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instSub); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instSub->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + Inventory::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instSub->GetCharges())); + + safe_delete_array(itemLinkCore); + } + } + + // world + if (scopeWhere & peekWorld) { + Object* objectTradeskill = targetClient->GetTradeskillObject(); + + if (objectTradeskill == nullptr) { + c->Message(1, "No world tradeskill object selected..."); + } + else { + c->Message(0, "[WorldObject DBID: %i (entityid: %i)]", objectTradeskill->GetDBID(), objectTradeskill->GetID()); + + for (int16 indexMain = MAIN_BEGIN; indexMain < EmuConstants::MAP_WORLD_SIZE; ++indexMain) { + instMain = objectTradeskill->GetItem(indexMain); + itemData = (instMain ? instMain->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instMain); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instMain->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), "WorldSlot: %i, Item: %i (%s), Charges: %i", + (EmuConstants::WORLD_BEGIN + indexMain), ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instMain->GetCharges())); + + safe_delete_array(itemLinkCore); + + for (uint8 indexSub = SUB_BEGIN; instMain && instMain->IsType(ItemClassContainer) && (indexSub < EmuConstants::ITEM_CONTAINER_SIZE); ++indexSub) { + instSub = instMain->GetItem(indexSub); + itemData = (instSub ? instSub->GetItem() : nullptr); + itemLinkCore = nullptr; + + if (itemData) + c->MakeItemLink(itemLinkCore, instSub); + + itemLink = (itemLinkCore ? StringFormat("%c%s%s%c", 0x12, itemLinkCore, instSub->GetItem()->Name, 0x12) : "null"); + + c->Message((itemData == 0), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i", + INVALID_INDEX, indexMain, indexSub, ((itemData == 0) ? 0 : itemData->ID), itemLink.c_str(), ((itemData == 0) ? 0 : instSub->GetCharges())); + + safe_delete_array(itemLinkCore); + } + } + } + } +} + +void command_interrogateinv(Client *c, const Seperator *sep) +{ + // 'command_interrogateinv' is an in-memory inventory interrogation tool only. + // + // it does not verify against actual database entries..but, the output can be + // used to verify that something has been corrupted in a player's inventory. + // any error condition should be assumed that the item in question will be + // lost when the player logs out or zones (or incurrs any action that will + // consume the Client-Inventory object instance in question.) + // + // any item instances located at a greater depth than a reported error should + // be treated as an error themselves regardless of whether they report as the + // same or not. + + if (strcasecmp(sep->arg[1], "help") == 0) { + if (c->Admin() < commandInterrogateInv) { + c->Message(0, "Usage: #interrogateinv"); + c->Message(0, " Displays your inventory's current in-memory nested storage references"); + } + else { + c->Message(0, "Usage: #interrogateinv [log] [silent]"); + c->Message(0, " Displays your or your Player target inventory's current in-memory nested storage references"); + c->Message(0, " [log] - Logs interrogation to file"); + c->Message(0, " [silent] - Omits the in-game message portion of the interrogation"); + } + return; + } + + Client* target = nullptr; + std::map instmap; + bool log = false; + bool silent = false; + bool error = false; + bool allowtrip = false; + + if (c->Admin() < commandInterrogateInv) { + if (c->GetInterrogateInvState()) { + c->Message(13, "The last use of #interrogateinv on this inventory instance discovered an error..."); + c->Message(13, "Logging out, zoning or re-arranging items at this point will result in item loss!"); + return; + } + target = c; + allowtrip = true; + } + else { + if (c->GetTarget() == nullptr) { + target = c; + } + else if (c->GetTarget()->IsClient()) { + target = c->GetTarget()->CastToClient(); + } + else { + c->Message(1, "Use of this command is limited to Client entities"); + return; + } + + if (strcasecmp(sep->arg[1], "log") == 0) + log = true; + if (strcasecmp(sep->arg[2], "silent") == 0) + silent = true; + } + + bool success = target->InterrogateInventory(c, log, silent, allowtrip, error); + + if (!success) + c->Message(13, "An unknown error occurred while processing Client::InterrogateInventory()"); } 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) @@ -3511,23 +3195,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) @@ -3736,7 +3417,7 @@ void command_corpse(Client *c, const Seperator *sep) c->Message(0, "Error: Target must be a player corpse."); else if (c->Admin() >= commandEditPlayerCorpses && target->IsPlayerCorpse()) { c->Message(0, "Depoping %s.", target->GetName()); - target->CastToCorpse()->DepopCorpse(); + target->CastToCorpse()->DepopPlayerCorpse(); if(!sep->arg[2][0] || atoi(sep->arg[2]) != 0) target->CastToCorpse()->Bury(); } @@ -4221,7 +3902,7 @@ void command_save(Client *c, const Seperator *sep) } else if (c->GetTarget()->IsPlayerCorpse()) { if (c->GetTarget()->CastToMob()->Save()) - c->Message(0, "%s successfully saved. (dbid=%u)", c->GetTarget()->GetName(), c->GetTarget()->CastToCorpse()->GetDBID()); + c->Message(0, "%s successfully saved. (dbid=%u)", c->GetTarget()->GetName(), c->GetTarget()->CastToCorpse()->GetCorpseDBID()); else c->Message(0, "Manual save for %s failed.", c->GetTarget()->GetName()); } @@ -4289,25 +3970,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) @@ -4652,40 +4332,38 @@ 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) { Mob *t=c->GetTarget()?c->GetTarget():c->CastToMob(); - c->Message(0, "%s's Location (XYZ): %1.1f, %1.1f, %1.1f; heading=%1.1f", t->GetName(), t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); + c->Message(0, "%s's Location (XYZ): %1.2f, %1.2f, %1.2f; heading=%1.1f", t->GetName(), t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); } void command_goto(Client *c, const Seperator *sep) @@ -5286,53 +4964,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)) @@ -5354,189 +4985,10 @@ void command_randomfeatures(Client *c, const Seperator *sep) c->Message(0,"Error: This command requires a target"); else { - uint16 Race = target->GetRace(); - if (Race <= 12 || Race == 128 || Race == 130 || Race == 330 || Race == 522) { - - uint8 Gender = target->GetGender(); - uint8 Texture = 0xFF; - uint8 HelmTexture = 0xFF; - uint8 HairColor = 0xFF; - uint8 BeardColor = 0xFF; - uint8 EyeColor1 = 0xFF; - uint8 EyeColor2 = 0xFF; - uint8 HairStyle = 0xFF; - uint8 LuclinFace = 0xFF; - uint8 Beard = 0xFF; - uint32 DrakkinHeritage = 0xFFFFFFFF; - uint32 DrakkinTattoo = 0xFFFFFFFF; - uint32 DrakkinDetails = 0xFFFFFFFF; - - // Set some common feature settings - EyeColor1 = MakeRandomInt(0, 9); - EyeColor2 = MakeRandomInt(0, 9); - LuclinFace = MakeRandomInt(0, 7); - - // Adjust all settings based on the min and max for each feature of each race and gender - switch (Race) - { - case 1: // Human - HairColor = MakeRandomInt(0, 19); - if (Gender == 0) { - BeardColor = HairColor; - HairStyle = MakeRandomInt(0, 3); - Beard = MakeRandomInt(0, 5); - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 2: // Barbarian - HairColor = MakeRandomInt(0, 19); - LuclinFace = MakeRandomInt(0, 87); - if (Gender == 0) { - BeardColor = HairColor; - HairStyle = MakeRandomInt(0, 3); - Beard = MakeRandomInt(0, 5); - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 3: // Erudite - if (Gender == 0) { - BeardColor = MakeRandomInt(0, 19); - Beard = MakeRandomInt(0, 5); - LuclinFace = MakeRandomInt(0, 57); - } - if (Gender == 1) { - LuclinFace = MakeRandomInt(0, 87); - } - break; - case 4: // WoodElf - HairColor = MakeRandomInt(0, 19); - if (Gender == 0) { - HairStyle = MakeRandomInt(0, 3); - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 5: // HighElf - HairColor = MakeRandomInt(0, 14); - if (Gender == 0) { - HairStyle = MakeRandomInt(0, 3); - LuclinFace = MakeRandomInt(0, 37); - BeardColor = HairColor; - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 6: // DarkElf - HairColor = MakeRandomInt(13, 18); - if (Gender == 0) { - HairStyle = MakeRandomInt(0, 3); - LuclinFace = MakeRandomInt(0, 37); - BeardColor = HairColor; - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 7: // HalfElf - HairColor = MakeRandomInt(0, 19); - if (Gender == 0) { - HairStyle = MakeRandomInt(0, 3); - LuclinFace = MakeRandomInt(0, 37); - BeardColor = HairColor; - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 8: // Dwarf - HairColor = MakeRandomInt(0, 19); - BeardColor = HairColor; - if (Gender == 0) { - HairStyle = MakeRandomInt(0, 3); - Beard = MakeRandomInt(0, 5); - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - LuclinFace = MakeRandomInt(0, 17); - } - break; - case 9: // Troll - EyeColor1 = MakeRandomInt(0, 10); - EyeColor2 = MakeRandomInt(0, 10); - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 3); - HairColor = MakeRandomInt(0, 23); - } - break; - case 10: // Ogre - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 3); - HairColor = MakeRandomInt(0, 23); - } - break; - case 11: // Halfling - HairColor = MakeRandomInt(0, 19); - if (Gender == 0) { - BeardColor = HairColor; - HairStyle = MakeRandomInt(0, 3); - Beard = MakeRandomInt(0, 5); - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 12: // Gnome - HairColor = MakeRandomInt(0, 24); - if (Gender == 0) { - BeardColor = HairColor; - HairStyle = MakeRandomInt(0, 3); - Beard = MakeRandomInt(0, 5); - } - if (Gender == 1) { - HairStyle = MakeRandomInt(0, 2); - } - break; - case 128: // Iksar - case 130: // VahShir - break; - case 330: // Froglok - LuclinFace = MakeRandomInt(0, 9); - case 522: // Drakkin - HairColor = MakeRandomInt(0, 3); - BeardColor = HairColor; - EyeColor1 = MakeRandomInt(0, 11); - EyeColor2 = MakeRandomInt(0, 11); - LuclinFace = MakeRandomInt(0, 6); - DrakkinHeritage = MakeRandomInt(0, 6); - DrakkinTattoo = MakeRandomInt(0, 7); - DrakkinDetails = MakeRandomInt(0, 7); - if (Gender == 0) { - Beard = MakeRandomInt(0, 12); - HairStyle = MakeRandomInt(0, 8); - } - if (Gender == 1) { - Beard = MakeRandomInt(0, 3); - HairStyle = MakeRandomInt(0, 7); - } - break; - default: - break; - } - - target->SendIllusionPacket(Race, Gender, Texture, HelmTexture, HairColor, BeardColor, - EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, - DrakkinHeritage, DrakkinTattoo, DrakkinDetails); - - c->Message(0,"NPC Features Randomized"); - } + if (target->RandomizeFeatures()) + c->Message(0,"Features Randomized"); else - c->Message(0,"This command requires a Playable Race as the Target"); + c->Message(0,"This command requires a Playable Race as the target"); } } @@ -6047,19 +5499,6 @@ void command_interrupt(Client *c, const Seperator *sep) c->InterruptSpell(ci_message, ci_color); } -void command_d1(Client *c, const Seperator *sep) -{ - EQApplicationPacket app(OP_Action, sizeof(Action_Struct)); - Action_Struct* a = (Action_Struct*)app.pBuffer; - a->target = c->GetTarget()->GetID(); - a->source = c->GetID(); - a->type = atoi(sep->arg[1]); - a->spell = atoi(sep->arg[2]); - a->sequence = atoi(sep->arg[3]); - app.priority = 1; - entity_list.QueueCloseClients(c, &app); -} - void command_summonitem(Client *c, const Seperator *sep) { if (!sep->IsNumber(1)) @@ -6266,13 +5705,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 { @@ -6299,123 +5738,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) @@ -6434,50 +5889,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) @@ -6587,12 +6033,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:"); @@ -6650,6 +6095,7 @@ void command_npcedit(Client *c, const Seperator *sep) c->Message(0, "#npcedit qglobal - Sets an NPC's quest global flag"); c->Message(0, "#npcedit limit - Sets an NPC's spawn limit counter"); c->Message(0, "#npcedit Attackspeed - Sets an NPC's attack speed modifier"); + c->Message(0, "#npcedit Attackdelay - Sets an NPC's attack delay"); c->Message(0, "#npcedit findable - Sets an NPC's findable flag"); c->Message(0, "#npcedit wep1 - Sets an NPC's primary weapon model"); c->Message(0, "#npcedit wep2 - Sets an NPC's secondary weapon model"); @@ -6664,654 +6110,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 hp_regen_rate=%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], "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 @@ -7416,56 +6939,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) @@ -7792,7 +7317,7 @@ void Client::Undye() { SendWearChange(cur_slot); } - Save(0); + database.DeleteCharacterDye(this->CharacterID()); } void command_undye(Client *c, const Seperator *sep) @@ -7972,8 +7497,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"); @@ -8006,17 +7529,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]); @@ -8024,39 +7551,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]); @@ -8076,7 +7607,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]); @@ -8096,9 +7630,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) { @@ -8644,25 +8179,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; @@ -8718,6 +8234,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 @@ -8756,7 +8273,7 @@ void command_setgraveyard(Client *c, const Seperator *sep) zoneid = database.GetZoneID(sep->arg[1]); if(zoneid > 0) { - graveyard_id = database.NewGraveyardRecord(zoneid, t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); + graveyard_id = database.CreateGraveyardRecord(zoneid, t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); if(graveyard_id > 0) { c->Message(0, "Successfuly added a new record for this graveyard!"); @@ -8819,7 +8336,7 @@ void command_summonburriedplayercorpse(Client *c, const Seperator *sep) return; } - Corpse* PlayerCorpse = database.SummonBurriedPlayerCorpse(t->CharacterID(), t->GetZoneID(), zone->GetInstanceID(), t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); + Corpse* PlayerCorpse = database.SummonBuriedCharacterCorpses(t->CharacterID(), t->GetZoneID(), zone->GetInstanceID(), t->GetX(), t->GetY(), t->GetZ(), t->GetHeading()); if(!PlayerCorpse) c->Message(0, "Your target doesn't have any burried corpses."); @@ -8838,7 +8355,7 @@ void command_getplayerburriedcorpsecount(Client *c, const Seperator *sep) return; } - uint32 CorpseCount = database.GetPlayerBurriedCorpseCount(t->CharacterID()); + uint32 CorpseCount = database.GetCharacterBuriedCorpseCount(t->CharacterID()); if(CorpseCount > 0) c->Message(0, "Your target has a total of %u burried corpses.", CorpseCount); @@ -8865,233 +8382,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) { @@ -9372,28 +8917,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; @@ -9410,1361 +8942,1046 @@ 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. + + // got an error message + if (!results.Success()) { + 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) @@ -11226,33 +10443,13 @@ void command_picklock(Client *c, const Seperator *sep) } } -void command_qtest(Client *c, const Seperator *sep) -{ - - - if(c && sep->arg[1][0]) - { - if(c->GetTarget()) - { - ServerPacket* pack = new ServerPacket(ServerOP_Speech, sizeof(Server_Speech_Struct)+strlen(sep->arg[1])+1); - Server_Speech_Struct* sem = (Server_Speech_Struct*) pack->pBuffer; - strcpy(sem->message, sep->arg[1]); - sem->minstatus = c->Admin(); - sem->type = 1; - strncpy(sem->to,c->GetTarget()->GetCleanName(), 64); - strncpy(sem->to,c->GetCleanName(), 64); - sem->guilddbid = c->GuildID(); - worldserver.SendPacket(pack); - safe_delete(pack); - } - } -} - 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"); @@ -11260,81 +10457,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) @@ -11422,8 +10608,6 @@ void command_augmentitem(Client *c, const Seperator *sep) AugmentItem_Struct* in_augment = new AugmentItem_Struct[sizeof(AugmentItem_Struct)]; in_augment->container_slot = 1000; // - in_augment->unknown02[0] = 0; - in_augment->unknown02[1] = 0; in_augment->augment_slot = -1; if(c->GetTradeskillObject() != nullptr) Object::HandleAugmentation(c, in_augment, c->GetTradeskillObject()); @@ -11550,3 +10734,9 @@ void command_merchantcloseshop(Client *c, const Seperator *sep) merchant->CastToNPC()->MerchantCloseShop(); } + +void command_shownumhits(Client *c, const Seperator *sep) +{ + c->ShowNumHits(); + return; +} diff --git a/zone/command.h b/zone/command.h index 087e1204c..72ee5c06a 100644 --- a/zone/command.h +++ b/zone/command.h @@ -20,9 +20,10 @@ #ifndef COMMAND_H #define COMMAND_H -#include "../common/seperator.h" -#include "../common/EQStream.h" -#include "client.h" +class Client; +class Seperator; + +#include "../common/types.h" #define COMMAND_CHAR '#' #define CMDALIASES 5 @@ -125,7 +126,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,9 +151,9 @@ 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_interrogateinv(Client *c, const Seperator *sep); void command_findnpctype(Client *c, const Seperator *sep); void command_findzone(Client *c, const Seperator *sep); void command_viewnpctype(Client *c, const Seperator *sep); @@ -217,7 +217,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); @@ -325,6 +324,7 @@ void command_showspellslist(Client *c, const Seperator *sep); void command_npctype_cache(Client *c, const Seperator *sep); void command_merchantopenshop(Client *c, const Seperator *sep); void command_merchantcloseshop(Client *c, const Seperator *sep); +void command_shownumhits(Client *c, const Seperator *sep); #ifdef EQPROFILE void command_profiledump(Client *c, const Seperator *sep); diff --git a/zone/common.h b/zone/common.h index 19cd51ce1..477d5b876 100644 --- a/zone/common.h +++ b/zone/common.h @@ -17,6 +17,11 @@ #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 TARGET_RING_SPELL_SLOT 12 +#define DISCIPLINE_SPELL_SLOT 10 +#define ABILITY_SPELL_SLOT 9 //LOS Parameters: #define HEAD_POSITION 0.9f //ratio of GetSize() where NPCs see from @@ -127,7 +132,9 @@ enum { FLEE_PERCENT = 37, ALLOW_BENEFICIAL = 38, DISABLE_MELEE = 39, - MAX_SPECIAL_ATTACK = 40 + NPC_CHASE_DISTANCE = 40, + ALLOW_TO_TANK = 41, + MAX_SPECIAL_ATTACK = 42 }; @@ -187,7 +194,7 @@ struct Buffs_Struct { }; struct StatBonuses { - int16 AC; + int32 AC; int32 HP; int32 HPRegen; int32 MaxHP; @@ -195,47 +202,47 @@ struct StatBonuses { int32 EnduranceRegen; int32 Mana; int32 Endurance; - int16 ATK; + int32 ATK; //would it be worth it to create a Stat_Struct? - int16 STR; - int16 STRCapMod; - int16 HeroicSTR; - int16 STA; - int16 STACapMod; - int16 HeroicSTA; - int16 DEX; - int16 DEXCapMod; - int16 HeroicDEX; - int16 AGI; - int16 AGICapMod; - int16 HeroicAGI; - int16 INT; - int16 INTCapMod; - int16 HeroicINT; - int16 WIS; - int16 WISCapMod; - int16 HeroicWIS; - int16 CHA; - int16 CHACapMod; - int16 HeroicCHA; - int16 MR; - int16 MRCapMod; - int16 HeroicMR; - int16 FR; - int16 FRCapMod; - int16 HeroicFR; - int16 CR; - int16 CRCapMod; - int16 HeroicCR; - int16 PR; - int16 PRCapMod; - int16 HeroicPR; - int16 DR; - int16 DRCapMod; - int16 HeroicDR; - int16 Corrup; - int16 CorrupCapMod; - int16 HeroicCorrup; + int32 STR; + int32 STRCapMod; + int32 HeroicSTR; + int32 STA; + int32 STACapMod; + int32 HeroicSTA; + int32 DEX; + int32 DEXCapMod; + int32 HeroicDEX; + int32 AGI; + int32 AGICapMod; + int32 HeroicAGI; + int32 INT; + int32 INTCapMod; + int32 HeroicINT; + int32 WIS; + int32 WISCapMod; + int32 HeroicWIS; + int32 CHA; + int32 CHACapMod; + int32 HeroicCHA; + int32 MR; + int32 MRCapMod; + int32 HeroicMR; + int32 FR; + int32 FRCapMod; + int32 HeroicFR; + int32 CR; + int32 CRCapMod; + int32 HeroicCR; + int32 PR; + int32 PRCapMod; + int32 HeroicPR; + int32 DR; + int32 DRCapMod; + int32 HeroicDR; + int32 Corrup; + int32 CorrupCapMod; + int32 HeroicCorrup; uint16 DamageShieldSpellID; int DamageShield; // this is damage done to mobs that attack this DmgShieldType DamageShieldType; @@ -245,90 +252,91 @@ struct StatBonuses { uint16 ReverseDamageShieldSpellID; DmgShieldType ReverseDamageShieldType; int movementspeed; - int16 haste; - int16 hastetype2; - int16 hastetype3; - int16 inhibitmelee; + int32 haste; + int32 hastetype2; + int32 hastetype3; + int32 inhibitmelee; float AggroRange; // when calculate just replace original value with this float AssistRange; - int16 skillmod[HIGHEST_SKILL+1]; + int32 skillmod[HIGHEST_SKILL+1]; int effective_casting_level; int reflect_chance; // chance to reflect incoming spell - uint16 singingMod; - uint16 Amplification; // stacks with singingMod - uint16 brassMod; - uint16 percussionMod; - uint16 windMod; - uint16 stringedMod; - uint16 songModCap; + uint32 singingMod; + uint32 Amplification; // stacks with singingMod + uint32 brassMod; + uint32 percussionMod; + uint32 windMod; + uint32 stringedMod; + uint32 songModCap; int8 hatemod; int32 EnduranceReduction; - int16 StrikeThrough; // PoP: Strike Through % - int16 MeleeMitigation; //i = Shielding - int16 MeleeMitigationEffect; //i = Spell Effect Melee Mitigation - int16 CriticalHitChance[HIGHEST_SKILL+2]; //i - int16 CriticalSpellChance; //i - int16 SpellCritDmgIncrease; //i - int16 SpellCritDmgIncNoStack; // increase - int16 DotCritDmgIncrease; //i - int16 CriticalHealChance; //i - int16 CriticalHealOverTime; //i - int16 CriticalDoTChance; //i - int16 CrippBlowChance; // - int16 AvoidMeleeChance; //AvoidMeleeChance/10 == % chance i = Avoidance (item mod) - int16 AvoidMeleeChanceEffect; //AvoidMeleeChance Spell Effect - int16 RiposteChance; //i - int16 DodgeChance; //i - int16 ParryChance; //i - int16 DualWieldChance; //i - int16 DoubleAttackChance; //i - int16 TripleAttackChance; //i - int16 DoubleRangedAttack; //i - int16 ResistSpellChance; //i - int16 ResistFearChance; //i + int32 StrikeThrough; // PoP: Strike Through % + int32 MeleeMitigation; //i = Shielding + int32 MeleeMitigationEffect; //i = Spell Effect Melee Mitigation + int32 CriticalHitChance[HIGHEST_SKILL+2]; //i + int32 CriticalSpellChance; //i + int32 SpellCritDmgIncrease; //i + int32 SpellCritDmgIncNoStack; // increase + int32 DotCritDmgIncrease; //i + int32 CriticalHealChance; //i + int32 CriticalHealOverTime; //i + int32 CriticalDoTChance; //i + int32 CrippBlowChance; // + int32 AvoidMeleeChance; //AvoidMeleeChance/10 == % chance i = Avoidance (item mod) + int32 AvoidMeleeChanceEffect; //AvoidMeleeChance Spell Effect + int32 RiposteChance; //i + int32 DodgeChance; //i + int32 ParryChance; //i + int32 DualWieldChance; //i + int32 DoubleAttackChance; //i + int32 TripleAttackChance; //i + int32 DoubleRangedAttack; //i + int32 ResistSpellChance; //i + int32 ResistFearChance; //i bool Fearless; //i bool IsFeared; //i - int16 StunResist; //i - int16 MeleeSkillCheck; //i + bool IsBlind; //i + int32 StunResist; //i + int32 MeleeSkillCheck; //i uint8 MeleeSkillCheckSkill; - int16 HitChance; //HitChance/15 == % increase i = Accuracy (Item: Accuracy) - int16 HitChanceEffect[HIGHEST_SKILL+2]; //Spell effect Chance to Hit, straight percent increase - int16 DamageModifier[HIGHEST_SKILL+2]; //i - int16 DamageModifier2[HIGHEST_SKILL+2]; //i - int16 MinDamageModifier[HIGHEST_SKILL+2]; //i - int16 ProcChance; // ProcChance/10 == % increase i = CombatEffects - int16 ProcChanceSPA; // ProcChance from spell effects - int16 ExtraAttackChance; - int16 DoTShielding; - int16 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger) - uint16 DeathSave[4]; // Death Pact [0](value = 1 partial 2 = full) [1]=slot [2]=LvLimit [3]=HealAmt - int16 FlurryChance; - int16 Accuracy[HIGHEST_SKILL+2]; //Accuracy/15 == % increase [Spell Effect: Accuracy) - int16 HundredHands; //extra haste, stacks with all other haste i - int16 MeleeLifetap; //i - int16 Vampirism; //i - int16 HealRate; // Spell effect that influences effectiveness of heals + int32 HitChance; //HitChance/15 == % increase i = Accuracy (Item: Accuracy) + int32 HitChanceEffect[HIGHEST_SKILL+2]; //Spell effect Chance to Hit, straight percent increase + int32 DamageModifier[HIGHEST_SKILL+2]; //i + int32 DamageModifier2[HIGHEST_SKILL+2]; //i + int32 MinDamageModifier[HIGHEST_SKILL+2]; //i + int32 ProcChance; // ProcChance/10 == % increase i = CombatEffects + int32 ProcChanceSPA; // ProcChance from spell effects + int32 ExtraAttackChance; + int32 DoTShielding; + int32 DivineSaveChance[2]; // Second Chance (base1 = chance, base2 = spell on trigger) + uint32 DeathSave[4]; // Death Pact [0](value = 1 partial 2 = full) [1]=slot [2]=LvLimit [3]=HealAmt + int32 FlurryChance; + int32 Accuracy[HIGHEST_SKILL+2]; //Accuracy/15 == % increase [Spell Effect: Accuracy) + int32 HundredHands; //extra haste, stacks with all other haste i + int32 MeleeLifetap; //i + int32 Vampirism; //i + int32 HealRate; // Spell effect that influences effectiveness of heals int32 MaxHPChange; // Spell Effect int16 SkillDmgTaken[HIGHEST_SKILL+2]; // All Skills + -1 int32 HealAmt; // Item Effect int32 SpellDmg; // Item Effect int32 Clairvoyance; // Item Effect - int16 DSMitigation; // Item Effect - int16 DSMitigationOffHand; // Lowers damage shield from off hand attacks. + int32 DSMitigation; // Item Effect + int32 DSMitigationOffHand; // Lowers damage shield from off hand attacks. uint32 SpellTriggers[MAX_SPELL_TRIGGER]; // Innate/Spell/Item Spells that trigger when you cast uint32 SpellOnKill[MAX_SPELL_TRIGGER*3]; // Chance to proc after killing a mob uint32 SpellOnDeath[MAX_SPELL_TRIGGER*2]; // Chance to have effect cast when you die - int16 CritDmgMob[HIGHEST_SKILL+2]; // All Skills + -1 - int16 SkillReuseTime[HIGHEST_SKILL+1]; // Reduces skill timers - int16 SkillDamageAmount[HIGHEST_SKILL+2]; // All Skills + -1 - int16 TwoHandBluntBlock; // chance to block when wielding two hand blunt weapon - uint16 ItemManaRegenCap; // Increases the amount of mana you have can over the cap(aa effect) - int16 GravityEffect; // Indictor of spell effect + int32 CritDmgMob[HIGHEST_SKILL+2]; // All Skills + -1 + int32 SkillReuseTime[HIGHEST_SKILL+1]; // Reduces skill timers + int32 SkillDamageAmount[HIGHEST_SKILL+2]; // All Skills + -1 + int32 TwoHandBluntBlock; // chance to block when wielding two hand blunt weapon + uint32 ItemManaRegenCap; // Increases the amount of mana you have can over the cap(aa effect) + int32 GravityEffect; // Indictor of spell effect bool AntiGate; // spell effect that prevents gating bool MagicWeapon; // spell effect that makes weapon magical - int16 IncreaseBlockChance; // overall block chance modifier - uint16 PersistantCasting; // chance to continue casting through a stun + int32 IncreaseBlockChance; // overall block chance modifier + uint32 PersistantCasting; // chance to continue casting through a stun int XPRateMod; //i int HPPercCap[2]; //Spell effect that limits you to being healed/regening beyond a % of your max int ManaPercCap[2]; // ^^ 0 = % Cap 1 = Flat Amount Cap @@ -336,64 +344,64 @@ struct StatBonuses { bool BlockNextSpell; // Indicates whether the client can block a spell or not //uint16 BlockSpellEffect[EFFECT_COUNT]; // Prevents spells with certain effects from landing on you *no longer used bool ImmuneToFlee; // Bypass the fleeing flag - uint16 VoiceGraft; // Stores the ID of the mob with which to talk through - int16 SpellProcChance; // chance to proc from sympathetic spell effects - int16 CharmBreakChance; // chance to break charm - int16 SongRange; // increases range of beneficial bard songs - uint16 HPToManaConvert; // Uses HP to cast spells at specific conversion - uint16 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have. + uint32 VoiceGraft; // Stores the ID of the mob with which to talk through + int32 SpellProcChance; // chance to proc from sympathetic spell effects + int32 CharmBreakChance; // chance to break charm + int32 SongRange; // increases range of beneficial bard songs + uint32 HPToManaConvert; // Uses HP to cast spells at specific conversion + uint32 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have. bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses) - int16 SkillDamageAmount2[HIGHEST_SKILL+2]; // Adds skill specific damage - uint16 NegateAttacks[3]; // 0 = bool HasEffect 1 = Buff Slot 2 = Max damage absorbed per hit - uint16 MitigateMeleeRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per hit 3 = Rune Amt - uint16 MeleeThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. - uint16 SpellThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. - uint16 MitigateSpellRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per spell 3 = Rune Amt - uint16 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt + int32 SkillDamageAmount2[HIGHEST_SKILL+2]; // Adds skill specific damage + uint32 NegateAttacks[3]; // 0 = bool HasEffect 1 = Buff Slot 2 = Max damage absorbed per hit + uint32 MitigateMeleeRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per hit 3 = Rune Amt + uint32 MeleeThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. + uint32 SpellThresholdGuard[3]; // 0 = Mitigation value 1 = Buff Slot 2 = Min damage to trigger. + uint32 MitigateSpellRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per spell 3 = Rune Amt + uint32 MitigateDotRune[4]; // 0 = Mitigation value 1 = Buff Slot 2 = Max mitigation per tick 3 = Rune Amt bool TriggerMeleeThreshold; // Has Melee Threshhold bool TriggerSpellThreshold; // Has Spell Threshhold - uint16 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot - int16 ShieldBlock; // Chance to Shield Block - int16 BlockBehind; // Chance to Block Behind (with our without shield) + uint32 ManaAbsorbPercentDamage[2]; // 0 = Mitigation value 1 = Buff Slot + int32 ShieldBlock; // Chance to Shield Block + int32 BlockBehind; // Chance to Block Behind (with our without shield) bool CriticalRegenDecay; // increase critical regen chance, decays based on spell level cast bool CriticalHealDecay; // increase critical heal chance, decays based on spell level cast bool CriticalDotDecay; // increase critical dot chance, decays based on spell level cast bool DivineAura; // invulnerability bool DistanceRemoval; // Check if Cancle if Moved effect is present - int16 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid + int32 ImprovedTaunt[3]; // 0 = Max Level 1 = Aggro modifier 2 = buffid int8 Root[2]; // The lowest buff slot a root can be found. [0] = Bool if has root [1] = buff slot - int16 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana. - uint16 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot - uint16 MeleeRune[2]; // 0 = rune value 1 = buff slot + int32 FrenziedDevastation; // base1= AArank(used) base2= chance increase spell criticals + all DD spells 2x mana. + uint32 AbsorbMagicAtt[2]; // 0 = magic rune value 1 = buff slot + uint32 MeleeRune[2]; // 0 = rune value 1 = buff slot bool NegateIfCombat; // Bool Drop buff if cast or melee int8 Screech; // -1 = Will be blocked if another Screech is +(1) - int16 AlterNPCLevel; // amount of lvls +/- - int16 AStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value - int16 BStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value - int16 CStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value - int16 DStacker[1]; // For buff stack blocking 0=Exists 1=Effect_value + int32 AlterNPCLevel; // amount of lvls +/- + int32 AStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value + int32 BStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value + int32 CStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value + int32 DStacker[2]; // For buff stack blocking 0=Exists 1=Effect_value bool BerserkSPA; // berserk effect - int16 Metabolism; // Food/drink consumption rates. + int32 Metabolism; // Food/drink consumption rates. bool Sanctuary; // Sanctuary effect, lowers place on hate list until cast on others. - int16 FactionModPct; // Modifies amount of faction gained. - int16 MeleeVulnerability; // Weakness/mitigation to melee damage + int32 FactionModPct; // Modifies amount of faction gained. + int32 MeleeVulnerability; // Weakness/mitigation to melee damage bool LimitToSkill[HIGHEST_SKILL+2]; // Determines if we need to search for a skill proc. - uint16 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. - uint16 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. + uint32 SkillProc[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs. + uint32 SkillProcSuccess[MAX_SKILL_PROCS]; // Max number of spells containing skill_procs_success. // AAs int8 Packrat; //weight reduction for items, 1 point = 10% uint8 BuffSlotIncrease; // Increases number of available buff slots - uint16 DelayDeath; // how far below 0 hp you can go + uint32 DelayDeath; // how far below 0 hp you can go int8 BaseMovementSpeed; // Adjust base run speed, does not stack with other movement bonuses. uint8 IncreaseRunSpeedCap; // Increase max run speed above cap. - int16 DoubleSpecialAttack; // Chance to to perform a double special attack (ie flying kick 2x) - int16 SpecialAttackKBProc[2]; // Chance to to do a knockback from special attacks. (0 = chance 1 = Skill) + int32 DoubleSpecialAttack; // Chance to to perform a double special attack (ie flying kick 2x) + int32 SpecialAttackKBProc[2]; // Chance to to do a knockback from special attacks. (0 = chance 1 = Skill) uint8 FrontalStunResist; // Chance to resist a frontal stun - int16 BindWound; // Increase amount of HP by percent. - int16 MaxBindWound; // Increase max amount of HP you can bind wound. - int16 ChannelChanceSpells; // Modify chance to channel a spell. - int16 ChannelChanceItems; // Modify chance to channel a items. + int32 BindWound; // Increase amount of HP by percent. + int32 MaxBindWound; // Increase max amount of HP you can bind wound. + int32 ChannelChanceSpells; // Modify chance to channel a spell. + int32 ChannelChanceItems; // Modify chance to channel a items. uint8 SeeInvis; // See Invs. uint8 TripleBackstab; // Chance to triple backstab bool FrontalBackstabMinDmg; // Allow frontal backstabs for min damage @@ -401,36 +409,36 @@ struct StatBonuses { uint8 ConsumeProjectile; // Chance to not consume arrow. uint8 ForageAdditionalItems; // Chance to forage another item. uint8 SalvageChance; // Chance to salvage a tradeskill components on fail. - uint16 ArcheryDamageModifier; // Increase Archery Damage by percent + uint32 ArcheryDamageModifier; // Increase Archery Damage by percent bool SecondaryDmgInc; // Allow off hand weapon to recieve damage bonus. - uint16 GiveDoubleAttack; // Allow classes to double attack with a specified chance. - int16 SlayUndead[2]; // Allow classes to do extra damage verse undead.(base1 = rate, base2 = damage mod) - int16 PetCriticalHit; // Allow pets to critical hit with % value. - int16 PetAvoidance; // Pet avoidance chance. - int16 CombatStability; // Melee damage mitigation. - int16 DoubleRiposte; // Chance to double riposte - int16 GiveDoubleRiposte[3]; // 0=Regular Chance, 1=Skill Attack Chance, 2=Skill - uint16 RaiseSkillCap[2]; // Raise a specific skill cap (1 = value, 2=skill) - int16 Ambidexterity; // Increase chance to duel wield by adding bonus 'skill'. - int16 PetMaxHP; // Increase the max hp of your pet. - int16 PetFlurry; // Chance for pet to flurry. + uint32 GiveDoubleAttack; // Allow classes to double attack with a specified chance. + int32 SlayUndead[2]; // Allow classes to do extra damage verse undead.(base1 = rate, base2 = damage mod) + int32 PetCriticalHit; // Allow pets to critical hit with % value. + int32 PetAvoidance; // Pet avoidance chance. + int32 CombatStability; // Melee damage mitigation. + int32 DoubleRiposte; // Chance to double riposte + int32 GiveDoubleRiposte[3]; // 0=Regular Chance, 1=Skill Attack Chance, 2=Skill + uint32 RaiseSkillCap[2]; // Raise a specific skill cap (1 = value, 2=skill) + int32 Ambidexterity; // Increase chance to duel wield by adding bonus 'skill'. + int32 PetMaxHP; // Increase the max hp of your pet. + int32 PetFlurry; // Chance for pet to flurry. uint8 MasteryofPast; // Can not fizzle spells below this level specified in value. bool GivePetGroupTarget; // All pets to recieve group buffs. (Pet Affinity) - int16 RootBreakChance; // Chance root will break; - int16 UnfailingDivinity; // Improves chance that DI will fire + increase partial heal. - int16 ItemHPRegenCap; // Increase item regen cap. - int16 SEResist[MAX_RESISTABLE_EFFECTS*2]; // Resist chance by specific spell effects. - int16 OffhandRiposteFail; // chance for opponent to fail riposte with offhand attack. - int16 ItemATKCap; // Raise item attack cap + int32 RootBreakChance; // Chance root will break; + int32 UnfailingDivinity; // Improves chance that DI will fire + increase partial heal. + int32 ItemHPRegenCap; // Increase item regen cap. + int32 SEResist[MAX_RESISTABLE_EFFECTS*2]; // Resist chance by specific spell effects. + int32 OffhandRiposteFail; // chance for opponent to fail riposte with offhand attack. + int32 ItemATKCap; // Raise item attack cap int32 FinishingBlow[2]; // Chance to do a finishing blow for specified damage amount. - uint16 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= ???) - int16 ShieldEquipHateMod; // Hate mod when shield equiped. - int16 ShieldEquipDmgMod[2]; // Damage mod when shield equiped. 0 = damage modifier 1 = Unknown + uint32 FinishingBlowLvl[2]; // Sets max level an NPC can be affected by FB. (base1 = lv, base2= ???) + int32 ShieldEquipHateMod; // Hate mod when shield equiped. + int32 ShieldEquipDmgMod[2]; // Damage mod when shield equiped. 0 = damage modifier 1 = Unknown bool TriggerOnValueAmount; // Triggers off various different conditions, bool to check if client has effect. int8 StunBashChance; // chance to stun with bash. int8 IncreaseChanceMemwipe; // increases chance to memory wipe int8 CriticalMend; // chance critical monk mend - int16 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy + int32 ImprovedReclaimEnergy; // Modifies amount of mana returned from reclaim energy uint32 HeadShot[2]; // Headshot AA (Massive dmg vs humaniod w/ archery) 0= ? 1= Dmg uint8 HSLevel; // Max Level Headshot will be effective at. uint32 Assassinate[2]; // Assassinate AA (Massive dmg vs humaniod w/ assassinate) 0= ? 1= Dmg @@ -451,6 +459,24 @@ struct Shielders_Struct { uint16 shielder_bonus; }; +typedef struct +{ + uint16 increment; + uint16 hit_increment; + uint16 target_id; + int32 wpn_dmg; + float origin_x; + float origin_y; + float origin_z; + float tlast_x; + float tlast_y; + uint32 ranged_id; + uint32 ammo_id; + int ammo_slot; + uint8 skill; + float speed_mod; +} tProjatk; + //eventually turn this into a typedef and //make DoAnim take it instead of int, to enforce its use. enum { //type arguments to DoAnim @@ -488,6 +514,8 @@ typedef enum { GroupSpell, // causes effect to caster + target's group CAHateList, // causes effect to all people on caster's hate list within some range DirectionalAE, + Beam, + TargetRing, CastActUnknown } CastAction_type; @@ -526,7 +554,7 @@ public: Mob* With(); // Add item from cursor slot to trade bucket (automatically does bag data too) - void AddEntity(uint16 from_slot_id, uint16 trade_slot_id); + void AddEntity(uint16 trade_slot_id, uint32 stack_size); // Audit trade void LogTrade(); @@ -557,7 +585,7 @@ struct ExtraAttackOptions { : damage_percent(1.0f), damage_flat(0), armor_pen_percent(0.0f), armor_pen_flat(0), crit_percent(1.0f), crit_flat(0.0f), - hate_percent(1.0f), hate_flat(0) + hate_percent(1.0f), hate_flat(0), hit_chance(0) { } float damage_percent; @@ -568,6 +596,7 @@ struct ExtraAttackOptions { float crit_flat; float hate_percent; int hate_flat; + int hit_chance; }; #endif diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 5ffe42e80..a16f0906d 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -35,14 +35,15 @@ Child of the Mob class. #include "masterentity.h" #include "../common/packet_functions.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "../common/crc32.h" -#include "StringIDs.h" +#include "string_ids.h" #include "worldserver.h" #include "../common/rulesys.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "remote_call_subscribe.h" + extern EntityList entity_list; extern Zone* zone; extern WorldServer worldserver; @@ -64,153 +65,146 @@ void Corpse::SendLootReqErrorPacket(Client* client, uint8 response) { safe_delete(outapp); } -Corpse* Corpse::LoadFromDBData(uint32 in_dbid, uint32 in_charid, char* in_charname, uchar* in_data, uint32 in_datasize, float in_x, float in_y, float in_z, float in_heading, char* timeofdeath, bool rezzed, bool wasAtGraveyard) { - if (in_datasize < sizeof(classic_db::DBPlayerCorpse_Struct)) { - LogFile->write(EQEMuLog::Error, "Corpse::LoadFromDBData: Corrupt data: in_datasize < sizeof(DBPlayerCorpse_Struct)"); - return 0; - } - classic_db::DBPlayerCorpse_Struct* dbpc = (classic_db::DBPlayerCorpse_Struct*) in_data; - bool isSoF = true; +Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, float in_x, float in_y, float in_z, float in_heading, std::string time_of_death, bool rezzed, bool was_at_graveyard){ + uint32 item_count = database.GetCharacterCorpseItemCount(in_dbid); + char *buffer = new char[sizeof(PlayerCorpse_Struct) + (item_count * sizeof(player_lootitem::ServerLootItem_Struct))]; + PlayerCorpse_Struct *pcs = (PlayerCorpse_Struct*)buffer; + database.LoadCharacterCorpseData(in_dbid, pcs); - uint32 esize1 = (sizeof(DBPlayerCorpse_Struct) + (dbpc->itemcount * sizeof(player_lootitem::ServerLootItem_Struct))); - uint32 esize2 = (sizeof(classic_db::DBPlayerCorpse_Struct) + (dbpc->itemcount * sizeof(player_lootitem::ServerLootItem_Struct))); - if (in_datasize != esize1) { - LogFile->write(EQEMuLog::Error, "Corpse::LoadFromDBData: Corrupt data: in_datasize (%i) != expected size (%i) Continuing on...", in_datasize, esize1); - if (in_datasize != esize2) { - LogFile->write(EQEMuLog::Error, "Corpse::LoadFromDBData: Corrupt data: in_datasize (%i) != expected size (%i) Your corpse is done broke, sir.", in_datasize, esize2); - return 0; - } - else - { - isSoF = false; - } + /* Load Items */ + ItemList itemlist; + ServerLootItem_Struct* tmp = 0; + for (unsigned int i = 0; i < pcs->itemcount; i++) { + tmp = new ServerLootItem_Struct; + memcpy(tmp, &pcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); + itemlist.push_back(tmp); } - if(isSoF) - { - DBPlayerCorpse_Struct* dbpcs = (DBPlayerCorpse_Struct*) in_data; - if (dbpcs->crc != CRC32::Generate(&((uchar*) dbpcs)[4], in_datasize - 4)) { - LogFile->write(EQEMuLog::Error, "Corpse::LoadFromDBData: Corrupt data: crc failure"); - return 0; - } - ItemList itemlist; - ServerLootItem_Struct* tmp = 0; - for (unsigned int i=0; i < dbpcs->itemcount; i++) { - tmp = new ServerLootItem_Struct; - memcpy(tmp, &dbpcs->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); - itemlist.push_back(tmp); - } - - // Little hack to account for the fact the race in the corpse struct is a uint8 and Froglok/Drakkin race number > 255 - // and to maintain backwards compatability with existing corpses in the database. - uint16 RealRace; - - switch(dbpcs->race) { - case 254: - RealRace = DRAKKIN; - break; - case 255: - RealRace = FROGLOK; - break; - default: - RealRace = dbpc->race; - } - - Corpse* pc = new Corpse(in_dbid, in_charid, in_charname, &itemlist, dbpcs->copper, dbpcs->silver, dbpcs->gold, dbpcs->plat, in_x, in_y, in_z, in_heading, dbpcs->size, dbpcs->gender, RealRace, dbpcs->class_, dbpcs->deity, dbpcs->level, dbpcs->texture, dbpcs->helmtexture, dbpcs->exp, wasAtGraveyard); - if (dbpcs->locked) - pc->Lock(); - - // load tints - memcpy(pc->item_tint, dbpcs->item_tint, sizeof(pc->item_tint)); - // appearance - pc->haircolor = dbpcs->haircolor; - pc->beardcolor = dbpcs->beardcolor; - pc->eyecolor1 = dbpcs->eyecolor1; - pc->eyecolor2 = dbpcs->eyecolor2; - pc->hairstyle = dbpcs->hairstyle; - pc->luclinface = dbpcs->face; - pc->beard = dbpcs->beard; - pc->drakkin_heritage = dbpcs->drakkin_heritage; - pc->drakkin_tattoo = dbpcs->drakkin_tattoo; - pc->drakkin_details = dbpcs->drakkin_details; - pc->Rezzed(rezzed); - pc->become_npc = false; - return pc; + /* Create Corpse Entity */ + Corpse* pc = new Corpse( + in_dbid, // uint32 in_dbid + in_charid, // uint32 in_charid + in_charname.c_str(), // char* in_charname + &itemlist, // ItemList* in_itemlist + pcs->copper, // uint32 in_copper + pcs->silver, // uint32 in_silver + pcs->gold, // uint32 in_gold + pcs->plat, // uint32 in_plat + in_x, // float in_x + in_y, // float in_y + in_z, // float in_z + in_heading, // float in_heading + pcs->size, // float in_size + pcs->gender, // uint8 in_gender + pcs->race, // uint16 in_race + pcs->class_, // uint8 in_class + pcs->deity, // uint8 in_deity + pcs->level, // uint8 in_level + pcs->texture, // uint8 in_texture + pcs->helmtexture, // uint8 in_helmtexture + pcs->exp, // uint32 in_rezexp + was_at_graveyard // bool wasAtGraveyard + ); + if (pcs->locked){ + pc->Lock(); } - else - { - if (dbpc->crc != CRC32::Generate(&((uchar*) dbpc)[4], in_datasize - 4)) { - LogFile->write(EQEMuLog::Error, "Corpse::LoadFromDBData: Corrupt data: crc failure"); - return 0; - } - ItemList itemlist; - ServerLootItem_Struct* tmp = 0; - for (unsigned int i=0; i < dbpc->itemcount; i++) { - tmp = new ServerLootItem_Struct; - memcpy(tmp, &dbpc->items[i], sizeof(player_lootitem::ServerLootItem_Struct)); - itemlist.push_back(tmp); - } - // Little hack to account for the fact the race in the corpse struct is a uint8 and Froglok/Drakkin race number > 255 - // and to maintain backwards compatability with existing corpses in the database. - uint16 RealRace; + /* Load Item Tints */ + pc->item_tint[0].color = pcs->item_tint[0].color; + pc->item_tint[1].color = pcs->item_tint[1].color; + pc->item_tint[2].color = pcs->item_tint[2].color; + pc->item_tint[3].color = pcs->item_tint[3].color; + pc->item_tint[4].color = pcs->item_tint[4].color; + pc->item_tint[5].color = pcs->item_tint[5].color; + pc->item_tint[6].color = pcs->item_tint[6].color; + pc->item_tint[7].color = pcs->item_tint[7].color; + pc->item_tint[8].color = pcs->item_tint[8].color; - switch(dbpc->race) { - case 254: - RealRace = DRAKKIN; - break; - case 255: - RealRace = FROGLOK; - break; - default: - RealRace = dbpc->race; - } + /* Load Physical Appearance */ + pc->haircolor = pcs->haircolor; + pc->beardcolor = pcs->beardcolor; + pc->eyecolor1 = pcs->eyecolor1; + pc->eyecolor2 = pcs->eyecolor2; + pc->hairstyle = pcs->hairstyle; + pc->luclinface = pcs->face; + pc->beard = pcs->beard; + pc->drakkin_heritage = pcs->drakkin_heritage; + pc->drakkin_tattoo = pcs->drakkin_tattoo; + pc->drakkin_details = pcs->drakkin_details; + pc->IsRezzed(rezzed); + pc->become_npc = false; - Corpse* pc = new Corpse(in_dbid, in_charid, in_charname, &itemlist, dbpc->copper, dbpc->silver, dbpc->gold, dbpc->plat, in_x, in_y, in_z, in_heading, dbpc->size, dbpc->gender, RealRace, dbpc->class_, dbpc->deity, dbpc->level, dbpc->texture, dbpc->helmtexture,dbpc->exp, wasAtGraveyard); - if (dbpc->locked) - pc->Lock(); + safe_delete_array(pcs); - // load tints - memcpy(pc->item_tint, dbpc->item_tint, sizeof(pc->item_tint)); - // appearance - pc->haircolor = dbpc->haircolor; - pc->beardcolor = dbpc->beardcolor; - pc->eyecolor1 = dbpc->eyecolor1; - pc->eyecolor2 = dbpc->eyecolor2; - pc->hairstyle = dbpc->hairstyle; - pc->luclinface = dbpc->face; - pc->beard = dbpc->beard; - pc->drakkin_heritage = 0; - pc->drakkin_tattoo = 0; - pc->drakkin_details = 0; - pc->Rezzed(rezzed); - pc->become_npc = false; - return pc; - } + return pc; } -// To be used on NPC death and ZoneStateLoad -// Mongrel: added see_invis and see_invis_undead Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NPCType** in_npctypedata, uint32 in_decaytime) -// vesuvias - appearence fix -: Mob("Unnamed_Corpse","",0,0,in_npc->GetGender(),in_npc->GetRace(),in_npc->GetClass(),BT_Humanoid,//bodytype added - in_npc->GetDeity(),in_npc->GetLevel(),in_npc->GetNPCTypeID(),in_npc->GetSize(),0, - in_npc->GetHeading(),in_npc->GetX(),in_npc->GetY(),in_npc->GetZ(),0, - in_npc->GetTexture(),in_npc->GetHelmTexture(), - 0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0xff,0,0,0,0,0,0,0,0,0), + : Mob("Unnamed_Corpse", // const char* in_name, + "", // const char* in_lastname, + 0, // int32 in_cur_hp, + 0, // int32 in_max_hp, + in_npc->GetGender(), // uint8 in_gender, + in_npc->GetRace(), // uint16 in_race, + in_npc->GetClass(), // uint8 in_class, + BT_Humanoid, // bodyType in_bodytype, + in_npc->GetDeity(), // uint8 in_deity, + in_npc->GetLevel(), // uint8 in_level, + in_npc->GetNPCTypeID(), // uint32 in_npctype_id, + in_npc->GetSize(), // float in_size, + 0, // float in_runspeed, + in_npc->GetHeading(), // float in_heading, + in_npc->GetX(), // float in_x_pos, + in_npc->GetY(), // float in_y_pos, + in_npc->GetZ(), // float in_z_pos, + 0, // uint8 in_light, + in_npc->GetTexture(), // uint8 in_texture, + in_npc->GetHelmTexture(), // uint8 in_helmtexture, + 0, // uint16 in_ac, + 0, // uint16 in_atk, + 0, // uint16 in_str, + 0, // uint16 in_sta, + 0, // uint16 in_dex, + 0, // uint16 in_agi, + 0, // uint16 in_int, + 0, // uint16 in_wis, + 0, // uint16 in_cha, + 0, // uint8 in_haircolor, + 0, // uint8 in_beardcolor, + 0, // uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? + 0, // uint8 in_eyecolor2, + 0, // uint8 in_hairstyle, + 0, // uint8 in_luclinface, + 0, // uint8 in_beard, + 0, // uint32 in_drakkin_heritage, + 0, // uint32 in_drakkin_tattoo, + 0, // uint32 in_drakkin_details, + 0, // uint32 in_armor_tint[_MaterialCount], + 0xff, // uint8 in_aa_title, + 0, // uint8 in_see_invis, // see through invis/ivu + 0, // uint8 in_see_invis_undead, + 0, // uint8 in_see_hide, + 0, // uint8 in_see_improved_hide, + 0, // int32 in_hp_regen, + 0, // int32 in_mana_regen, + 0, // uint8 in_qglobal, + 0, // uint8 in_maxlevel, + 0 // uint32 in_scalerate +), corpse_decay_timer(in_decaytime), - corpse_res_timer(0), + corpse_rez_timer(0), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), corpse_graveyard_timer(0), loot_cooldown_timer(10) { corpse_graveyard_timer.Disable(); + memset(item_tint, 0, sizeof(item_tint)); - pIsChanged = false; - p_PlayerCorpse = false; - pLocked = false; - BeingLootedBy = 0xFFFFFFFF; + + is_corpse_changed = false; + is_player_corpse = false; + is_locked = false; + being_looted_by = 0xFFFFFFFF; if (in_itemlist) { itemlist = *in_itemlist; in_itemlist->clear(); @@ -219,13 +213,13 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP SetCash(in_npc->GetCopper(), in_npc->GetSilver(), in_npc->GetGold(), in_npc->GetPlatinum()); npctype_id = in_npctypeid; - SetPKItem(0); - charid = 0; - dbid = 0; - p_depop = false; - strcpy(orgname, in_npc->GetName()); + SetPlayerKillItemID(0); + char_id = 0; + corpse_db_id = 0; + player_corpse_depop = false; + strcpy(corpse_name, in_npc->GetName()); strcpy(name, in_npc->GetName()); - // Added By Hogie + for(int count = 0; count < 100; count++) { if ((level >= npcCorpseDecayTimes[count].minlvl) && (level <= npcCorpseDecayTimes[count].maxlvl)) { corpse_decay_timer.SetTimer(npcCorpseDecayTimes[count].seconds*1000); @@ -240,109 +234,112 @@ Corpse::Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NP corpse_delay_timer.SetTimer(corpse_decay_timer.GetRemainingTime() + 1000); } - // Added By Hogie -- End - for (int i=0; irezzexp = 0; + for (int i = 0; i < MAX_LOOTERS; i++){ + allowed_looters[i] = 0; + } + this->rez_experience = 0; } -// To be used on PC death -// Mongrel: added see_invis and see_invis_undead -Corpse::Corpse(Client* client, int32 in_rezexp) -// vesuvias - appearence fix -: Mob -( - "Unnamed_Corpse", - "", - 0, - 0, - client->GetGender(), - client->GetRace(), - client->GetClass(), - BT_Humanoid, // bodytype added - client->GetDeity(), - client->GetLevel(), - 0, - client->GetSize(), - 0, - client->GetHeading(), // heading - client->GetX(), - client->GetY(), - client->GetZ(), - 0, - client->GetTexture(), - client->GetHelmTexture(), - 0, // AC - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, // CHA - client->GetPP().haircolor, - client->GetPP().beardcolor, - client->GetPP().eyecolor1, - client->GetPP().eyecolor2, - client->GetPP().hairstyle, - client->GetPP().face, - client->GetPP().beard, - client->GetPP().drakkin_heritage, - client->GetPP().drakkin_tattoo, - client->GetPP().drakkin_details, - 0, - 0xff, // aa title - 0, - 0, - 0, - 0, - 0, - 0, - 0, // qglobal - 0, // maxlevel - 0 // scalerate -), +Corpse::Corpse(Client* client, int32 in_rezexp) : Mob ( + "Unnamed_Corpse", // const char* in_name, + "", // const char* in_lastname, + 0, // int32 in_cur_hp, + 0, // int32 in_max_hp, + client->GetGender(), // uint8 in_gender, + client->GetRace(), // uint16 in_race, + client->GetClass(), // uint8 in_class, + BT_Humanoid, // bodyType in_bodytype, + client->GetDeity(), // uint8 in_deity, + client->GetLevel(), // uint8 in_level, + 0, // uint32 in_npctype_id, + client->GetSize(), // float in_size, + 0, // float in_runspeed, + client->GetHeading(), // float in_heading, + client->GetX(), // float in_x_pos, + client->GetY(), // float in_y_pos, + client->GetZ(), // float in_z_pos, + 0, // uint8 in_light, + client->GetTexture(), // uint8 in_texture, + client->GetHelmTexture(), // uint8 in_helmtexture, + 0, // uint16 in_ac, + 0, // uint16 in_atk, + 0, // uint16 in_str, + 0, // uint16 in_sta, + 0, // uint16 in_dex, + 0, // uint16 in_agi, + 0, // uint16 in_int, + 0, // uint16 in_wis, + 0, // uint16 in_cha, + client->GetPP().haircolor, // uint8 in_haircolor, + client->GetPP().beardcolor, // uint8 in_beardcolor, + client->GetPP().eyecolor1, // uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? + client->GetPP().eyecolor2, // uint8 in_eyecolor2, + client->GetPP().hairstyle, // uint8 in_hairstyle, + client->GetPP().face, // uint8 in_luclinface, + client->GetPP().beard, // uint8 in_beard, + client->GetPP().drakkin_heritage, // uint32 in_drakkin_heritage, + client->GetPP().drakkin_tattoo, // uint32 in_drakkin_tattoo, + client->GetPP().drakkin_details, // uint32 in_drakkin_details, + 0, // uint32 in_armor_tint[_MaterialCount], + 0xff, // uint8 in_aa_title, + 0, // uint8 in_see_invis, // see through invis + 0, // uint8 in_see_invis_undead, // see through invis vs. undead + 0, // uint8 in_see_hide, + 0, // uint8 in_see_improved_hide, + 0, // int32 in_hp_regen, + 0, // int32 in_mana_regen, + 0, // uint8 in_qglobal, + 0, // uint8 in_maxlevel, + 0 // uint32 in_scalerate + ), corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), - corpse_res_timer(RuleI(Character, CorpseResTimeMS)), + corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), - loot_cooldown_timer(10) + loot_cooldown_timer(10) { int i; + PlayerProfile_Struct *pp = &client->GetPP(); ItemInst *item; + /* Check if Zone has Graveyard First */ if(!zone->HasGraveyard()) { corpse_graveyard_timer.Disable(); } memset(item_tint, 0, sizeof(item_tint)); - for (i=0; iCharacterID(); - dbid = 0; - p_depop = false; + for (i = 0; i < MAX_LOOTERS; i++){ + allowed_looters[i] = 0; + } + + is_corpse_changed = true; + rez_experience = in_rezexp; + can_corpse_be_rezzed = true; + is_player_corpse = true; + is_locked = false; + being_looted_by = 0xFFFFFFFF; + char_id = client->CharacterID(); + corpse_db_id = 0; + player_corpse_depop = false; copper = 0; silver = 0; gold = 0; platinum = 0; - strcpy(orgname, pp->name); - strcpy(name, pp->name); - //become_npc was not being initialized which led to some pretty funky things with newly created corpses + strcpy(corpse_name, pp->name); + strcpy(name, pp->name); + + /* become_npc was not being initialized which led to some pretty funky things with newly created corpses */ become_npc = false; - SetPKItem(0); + SetPlayerKillItemID(0); - if(!RuleB(Character, LeaveNakedCorpses) || RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) { + /* Check Rule to see if we can leave corpses */ + if(!RuleB(Character, LeaveNakedCorpses) || + RuleB(Character, LeaveCorpses) && + GetLevel() >= RuleI(Character, DeathItemLossLevel)) { // cash // Let's not move the cash when 'RespawnFromHover = true' && 'client->GetClientVersion() < EQClientSoF' since the client doesn't. // (change to first client that supports 'death hover' mode, if not SoF.) @@ -363,8 +360,7 @@ Corpse::Corpse(Client* client, int32 in_rezexp) // worn + inventory + cursor std::list removed_list; bool cursor = false; - for(i = MAIN_BEGIN; i < EmuConstants::MAP_POSSESSIONS_SIZE; i++) - { + for(i = MAIN_BEGIN; i < EmuConstants::MAP_POSSESSIONS_SIZE; i++) { if(i == MainAmmo && client->GetClientVersion() >= EQClientSoF) { item = client->GetInv().GetItem(MainPowerSource); if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) { @@ -388,10 +384,9 @@ Corpse::Corpse(Client* client, int32 in_rezexp) // this was mainly for client profile state reflection..should match db player inventory entries now. iter_queue it; - for(it=client->GetInv().cursor_begin(),i=8001; it!=client->GetInv().cursor_end(); ++it,i++) { + for (it = client->GetInv().cursor_begin(), i = 8001; it != client->GetInv().cursor_end(); ++it, i++) { item = *it; - if((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) - { + if ((item && (!client->IsBecomeNPC())) || (item && client->IsBecomeNPC() && !item->GetItem()->NoRent)) { std::list slot_list = MoveItemToCorpse(client, item, i); removed_list.merge(slot_list); cursor = true; @@ -399,27 +394,29 @@ Corpse::Corpse(Client* client, int32 in_rezexp) } } - if(removed_list.size() != 0) { + database.TransactionBegin(); + if (removed_list.size() != 0) { std::stringstream ss(""); ss << "DELETE FROM inventory WHERE charid=" << client->CharacterID(); ss << " AND ("; std::list::const_iterator iter = removed_list.begin(); bool first = true; - while(iter != removed_list.end()) { - if(first) { + while (iter != removed_list.end()) { + if (first) { first = false; - } else { + } + else { ss << " OR "; } ss << "slotid=" << (*iter); ++iter; } ss << ")"; - database.RunQuery(ss.str().c_str(), ss.str().length()); + database.QueryDatabase(ss.str().c_str()); } - if(cursor) { // all cursor items should be on corpse (client < SoF or RespawnFromHover = false) - while(!client->GetInv().CursorEmpty()) + if (cursor) { // all cursor items should be on corpse (client < SoF or RespawnFromHover = false) + while (!client->GetInv().CursorEmpty()) client->DeleteItemInInventory(MainCursor, 0, false, false); } else { // only visible cursor made it to corpse (client >= Sof and RespawnFromHover = true) @@ -430,13 +427,18 @@ Corpse::Corpse(Client* client, int32 in_rezexp) client->CalcBonuses(); // will only affect offline profile viewing of dead characters..unneeded overhead client->Save(); + + IsRezzed(false); + Save(); + database.TransactionCommit(); + + return; } //end "not leaving naked corpses" - Rezzed(false); + IsRezzed(false); Save(); } -// solar: helper function for client corpse constructor std::list Corpse::MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot) { int bagindex; @@ -448,16 +450,13 @@ std::list Corpse::MoveItemToCorpse(Client *client, ItemInst *item, int16 returnlist.push_back(equipslot); // Qualified bag slot iterations. processing bag slots that don't exist is probably not a good idea. - if(item->IsType(ItemClassContainer) && ((equipslot >= EmuConstants::GENERAL_BEGIN && equipslot <= MainCursor))) // Limit the bag check to inventory and cursor slots. - { - for(bagindex = SUB_BEGIN; bagindex <= EmuConstants::ITEM_CONTAINER_SIZE; bagindex++) - { + if (item->IsType(ItemClassContainer) && ((equipslot >= EmuConstants::GENERAL_BEGIN && equipslot <= MainCursor))) { + for (bagindex = SUB_BEGIN; bagindex <= EmuConstants::ITEM_CONTAINER_SIZE; bagindex++) { // For empty bags in cursor queue, slot was previously being resolved as SLOT_INVALID (-1) interior_slot = Inventory::CalcSlotId(equipslot, bagindex); interior_item = client->GetInv().GetItem(interior_slot); - if(interior_item) - { + if (interior_item) { AddItem(interior_item->GetItem()->ID, interior_item->GetCharges(), interior_slot, interior_item->GetAugmentItemID(0), interior_item->GetAugmentItemID(1), interior_item->GetAugmentItemID(2), interior_item->GetAugmentItemID(3), interior_item->GetAugmentItemID(4)); returnlist.push_back(Inventory::CalcSlotId(equipslot, bagindex)); client->DeleteItemInInventory(interior_slot, 0, true, false); @@ -468,52 +467,103 @@ std::list Corpse::MoveItemToCorpse(Client *client, ItemInst *item, int16 return returnlist; } -// To be called from LoadFromDBData -// Mongrel: added see_invis and see_invis_undead -Corpse::Corpse(uint32 in_dbid, uint32 in_charid, char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture,uint32 in_rezexp, bool wasAtGraveyard) - : Mob("Unnamed_Corpse","",0,0,in_gender, in_race, in_class, BT_Humanoid, in_deity, in_level,0, in_size, 0, in_heading, in_x, in_y, in_z,0,in_texture,in_helmtexture, - 0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0xff, - 0,0,0,0,0,0,0,0,0), +/* Called from Database Load */ + +Corpse::Corpse(uint32 in_dbid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture,uint32 in_rezexp, bool wasAtGraveyard) + : Mob("Unnamed_Corpse", // const char* in_name, + "", // const char* in_lastname, + 0, // int32 in_cur_hp, + 0, // int32 in_max_hp, + in_gender, // uint8 in_gender, + in_race, // uint16 in_race, + in_class, // uint8 in_class, + BT_Humanoid, // bodyType in_bodytype, + in_deity, // uint8 in_deity, + in_level, // uint8 in_level, + 0, // uint32 in_npctype_id, + in_size, // float in_size, + 0, // float in_runspeed, + in_heading, // float in_heading, + in_x, // float in_x_pos, + in_y, // float in_y_pos, + in_z, // float in_z_pos, + 0, // uint8 in_light, + in_texture, // uint8 in_texture, + in_helmtexture, // uint8 in_helmtexture, + 0, // uint16 in_ac, + 0, // uint16 in_atk, + 0, // uint16 in_str, + 0, // uint16 in_sta, + 0, // uint16 in_dex, + 0, // uint16 in_agi, + 0, // uint16 in_int, + 0, // uint16 in_wis, + 0, // uint16 in_cha, + 0, // uint8 in_haircolor, + 0, // uint8 in_beardcolor, + 0, // uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye? + 0, // uint8 in_eyecolor2, + 0, // uint8 in_hairstyle, + 0, // uint8 in_luclinface, + 0, // uint8 in_beard, + 0, // uint32 in_drakkin_heritage, + 0, // uint32 in_drakkin_tattoo, + 0, // uint32 in_drakkin_details, + 0, // uint32 in_armor_tint[_MaterialCount], + 0xff, // uint8 in_aa_title, + 0, // uint8 in_see_invis, // see through invis/ivu + 0, // uint8 in_see_invis_undead, + 0, // uint8 in_see_hide, + 0, // uint8 in_see_improved_hide, + 0, // int32 in_hp_regen, + 0, // int32 in_mana_regen, + 0, // uint8 in_qglobal, + 0, // uint8 in_maxlevel, + 0), // uint32 in_scalerate corpse_decay_timer(RuleI(Character, CorpseDecayTimeMS)), - corpse_res_timer(RuleI(Character, CorpseResTimeMS)), + corpse_rez_timer(RuleI(Character, CorpseResTimeMS)), corpse_delay_timer(RuleI(NPC, CorpseUnlockTimer)), corpse_graveyard_timer(RuleI(Zone, GraveyardTimeMS)), loot_cooldown_timer(10) { - //we really should be loading the decay timer here... LoadPlayerCorpseDecayTime(in_dbid); - if(!zone->HasGraveyard() || wasAtGraveyard) + if (!zone->HasGraveyard() || wasAtGraveyard){ corpse_graveyard_timer.Disable(); + } memset(item_tint, 0, sizeof(item_tint)); - pIsChanged = false; - p_PlayerCorpse = true; - pLocked = false; - BeingLootedBy = 0xFFFFFFFF; - dbid = in_dbid; - p_depop = false; - charid = in_charid; + + is_corpse_changed = false; + is_player_corpse = true; + is_locked = false; + being_looted_by = 0xFFFFFFFF; + corpse_db_id = in_dbid; + player_corpse_depop = false; + char_id = in_charid; itemlist = *in_itemlist; in_itemlist->clear(); - strcpy(orgname, in_charname); + strcpy(corpse_name, in_charname); strcpy(name, in_charname); + this->copper = in_copper; this->silver = in_silver; this->gold = in_gold; this->platinum = in_plat; - rezzexp = in_rezexp; - for (int i=0; iCountItems(); - uint32 tmpsize = sizeof(DBPlayerCorpse_Struct) + (tmp * sizeof(player_lootitem::ServerLootItem_Struct)); - DBPlayerCorpse_Struct* dbpc = (DBPlayerCorpse_Struct*) new uchar[tmpsize]; + uint32 tmpsize = sizeof(PlayerCorpse_Struct) + (tmp * sizeof(player_lootitem::ServerLootItem_Struct)); + + PlayerCorpse_Struct* dbpc = (PlayerCorpse_Struct*) new uchar[tmpsize]; memset(dbpc, 0, tmpsize); dbpc->itemcount = tmp; dbpc->size = this->size; - dbpc->locked = pLocked; + dbpc->locked = is_locked; dbpc->copper = this->copper; dbpc->silver = this->silver; dbpc->gold = this->gold; dbpc->plat = this->platinum; - - // Little hack to account for the fact the race in the corpse struct is a uint8 and Froglok/Drakkin race number > 255 - // and to maintain backwards compatability with existing corpses in the database. - uint16 CorpseRace; - - switch(race) { - case DRAKKIN: - CorpseRace = 254; - break; - case FROGLOK: - CorpseRace = 255; - break; - default: - CorpseRace = race; - } - - dbpc->race = CorpseRace; + dbpc->race = this->race; dbpc->class_ = class_; dbpc->gender = gender; dbpc->deity = deity; dbpc->level = level; dbpc->texture = this->texture; dbpc->helmtexture = this->helmtexture; - dbpc->exp = rezzexp; + dbpc->exp = rez_experience; memcpy(dbpc->item_tint, item_tint, sizeof(dbpc->item_tint)); dbpc->haircolor = haircolor; @@ -594,50 +631,45 @@ bool Corpse::Save() { dbpc->drakkin_details = drakkin_details; uint32 x = 0; - ItemList::iterator cur,end; + ItemList::iterator cur, end; cur = itemlist.begin(); end = itemlist.end(); - for(; cur != end; ++cur) { + for (; cur != end; ++cur) { ServerLootItem_Struct* item = *cur; - memcpy((char*) &dbpc->items[x++], (char*) item, sizeof(player_lootitem::ServerLootItem_Struct)); + memcpy((char*)&dbpc->items[x++], (char*)item, sizeof(ServerLootItem_Struct)); } - dbpc->crc = CRC32::Generate(&((uchar*) dbpc)[4], tmpsize - 4); - - if (dbid == 0) - { - dbid = database.CreatePlayerCorpse(charid, orgname, zone->GetZoneID(), zone->GetInstanceID(), (uchar*) dbpc, tmpsize, x_pos, y_pos, z_pos, heading); - if(RuleB(Zone, UsePlayerCorpseBackups) == true) - database.CreatePlayerCorpseBackup(dbid, charid, orgname, zone->GetZoneID(), zone->GetInstanceID(), (uchar*) dbpc, tmpsize, x_pos, y_pos, z_pos, heading); + /* Create New Corpse*/ + if (corpse_db_id == 0) { + corpse_db_id = database.SaveCharacterCorpse(char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, x_pos, y_pos, z_pos, heading); } - else - dbid = database.UpdatePlayerCorpse(dbid, charid, orgname, zone->GetZoneID(), zone->GetInstanceID(), (uchar*) dbpc, tmpsize, x_pos, y_pos, z_pos, heading,Rezzed()); + /* Update Corpse Data */ + else{ + corpse_db_id = database.UpdateCharacterCorpse(corpse_db_id, char_id, corpse_name, zone->GetZoneID(), zone->GetInstanceID(), dbpc, x_pos, y_pos, z_pos, heading, IsRezzed()); + } + safe_delete_array(dbpc); - if (dbid == 0) { - std::cout << "Error: Failed to save player corpse '" << this->GetName() << "'" << std::endl; - return false; - } + return true; } void Corpse::Delete() { - if (IsPlayerCorpse() && dbid != 0) - database.DeletePlayerCorpse(dbid); - dbid = 0; - - p_depop = true; + if (IsPlayerCorpse() && corpse_db_id != 0) + database.DeleteCharacterCorpse(corpse_db_id); + + corpse_db_id = 0; + player_corpse_depop = true; } void Corpse::Bury() { - if (IsPlayerCorpse() && dbid != 0) - database.BuryPlayerCorpse(dbid); - dbid = 0; - - p_depop = true; + if (IsPlayerCorpse() && corpse_db_id != 0){ + database.BuryCharacterCorpse(corpse_db_id); + } + corpse_db_id = 0; + player_corpse_depop = true; } -void Corpse::Depop() { - /* Web Interface: Corpse Depop */ +void Corpse::DepopNPCCorpse() { if (RemoteCallSubscriptionHandler::Instance()->IsSubscribed("Entity.Events")) { std::vector params; params.push_back(std::to_string((long)EntityEvents::Entity_Corpse_Bury)); @@ -646,11 +678,11 @@ void Corpse::Depop() { } if (IsNPCCorpse()) - p_depop = true; + player_corpse_depop = true; } -void Corpse::DepopCorpse() { - p_depop = true; +void Corpse::DepopPlayerCorpse() { + player_corpse_depop = true; } uint32 Corpse::CountItems() { @@ -660,47 +692,45 @@ uint32 Corpse::CountItems() { void Corpse::AddItem(uint32 itemnum, uint16 charges, int16 slot, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) { if (!database.GetItem(itemnum)) return; - pIsChanged = true; + + is_corpse_changed = true; + ServerLootItem_Struct* item = new ServerLootItem_Struct; + memset(item, 0, sizeof(ServerLootItem_Struct)); item->item_id = itemnum; item->charges = charges; - item->equipSlot = slot; - item->aug1=aug1; - item->aug2=aug2; - item->aug3=aug3; - item->aug4=aug4; - item->aug5=aug5; + item->equip_slot = slot; + item->aug_1=aug1; + item->aug_2=aug2; + item->aug_3=aug3; + item->aug_4=aug4; + item->aug_5=aug5; itemlist.push_back(item); } -ServerLootItem_Struct* Corpse::GetItem(uint16 lootslot, ServerLootItem_Struct** bag_item_data) -{ +ServerLootItem_Struct* Corpse::GetItem(uint16 lootslot, ServerLootItem_Struct** bag_item_data) { ServerLootItem_Struct *sitem = 0, *sitem2; - // find the item ItemList::iterator cur,end; cur = itemlist.begin(); end = itemlist.end(); for(; cur != end; ++cur) { - if((*cur)->lootslot == lootslot) - { + if((*cur)->lootslot == lootslot) { sitem = *cur; break; } } - if (sitem && bag_item_data && Inventory::SupportsContainers(sitem->equipSlot)) - { - int16 bagstart = Inventory::CalcSlotId(sitem->equipSlot, SUB_BEGIN); + if (sitem && bag_item_data && Inventory::SupportsContainers(sitem->equip_slot)) { + int16 bagstart = Inventory::CalcSlotId(sitem->equip_slot, SUB_BEGIN); cur = itemlist.begin(); end = itemlist.end(); - for(; cur != end; ++cur) { + for (; cur != end; ++cur) { sitem2 = *cur; - if(sitem2->equipSlot >= bagstart && sitem2->equipSlot < bagstart + 10) - { - bag_item_data[sitem2->equipSlot - bagstart] = sitem2; + if (sitem2->equip_slot >= bagstart && sitem2->equip_slot < bagstart + 10) { + bag_item_data[sitem2->equip_slot - bagstart] = sitem2; } } } @@ -714,8 +744,7 @@ uint32 Corpse::GetWornItem(int16 equipSlot) const { end = itemlist.end(); for(; cur != end; ++cur) { ServerLootItem_Struct* item = *cur; - if (item->equipSlot == equipSlot) - { + if (item->equip_slot == equipSlot) { return item->item_id; } } @@ -723,40 +752,34 @@ uint32 Corpse::GetWornItem(int16 equipSlot) const { return 0; } -void Corpse::RemoveItem(uint16 lootslot) -{ - +void Corpse::RemoveItem(uint16 lootslot) { if (lootslot == 0xFFFF) return; ItemList::iterator cur,end; cur = itemlist.begin(); end = itemlist.end(); - for(; cur != end; ++cur) { + for (; cur != end; ++cur) { ServerLootItem_Struct* sitem = *cur; - if (sitem->lootslot == lootslot) - { + if (sitem->lootslot == lootslot) { RemoveItem(sitem); return; } } } -void Corpse::RemoveItem(ServerLootItem_Struct* item_data) -{ - uint8 material; - +void Corpse::RemoveItem(ServerLootItem_Struct* item_data){ + uint8 material; ItemList::iterator cur,end; cur = itemlist.begin(); end = itemlist.end(); for(; cur != end; ++cur) { ServerLootItem_Struct* sitem = *cur; - if (sitem == item_data) - { - pIsChanged = true; + if (sitem == item_data) { + is_corpse_changed = true; itemlist.erase(cur); - material = Inventory::CalcMaterialFromSlot(sitem->equipSlot); + material = Inventory::CalcMaterialFromSlot(sitem->equip_slot); if(material != _MaterialInvalid) SendWearChange(material); @@ -772,7 +795,7 @@ void Corpse::SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 this->silver = in_silver; this->gold = in_gold; this->platinum = in_platinum; - pIsChanged = true; + is_corpse_changed = true; } void Corpse::RemoveCash() { @@ -780,43 +803,45 @@ void Corpse::RemoveCash() { this->silver = 0; this->gold = 0; this->platinum = 0; - pIsChanged = true; + is_corpse_changed = true; } bool Corpse::IsEmpty() const { if (copper != 0 || silver != 0 || gold != 0 || platinum != 0) return false; + return(itemlist.size() == 0); } bool Corpse::Process() { - if (p_depop) + if (player_corpse_depop){ return false; + } - if(corpse_delay_timer.Check()) - { - for (int i=0; iHasGraveyard()) { + if (corpse_graveyard_timer.Check()) { + if (zone->HasGraveyard()) { Save(); - p_depop = true; - database.GraveyardPlayerCorpse(dbid, zone->graveyard_zoneid(), + player_corpse_depop = true; + database.SendCharacterCorpseToGraveyard(corpse_db_id, zone->graveyard_zoneid(), (zone->GetZoneID() == zone->graveyard_zoneid()) ? zone->GetInstanceID() : 0, zone->graveyard_x(), zone->graveyard_y(), zone->graveyard_z(), zone->graveyard_heading()); corpse_graveyard_timer.Disable(); ServerPacket* pack = new ServerPacket(ServerOP_SpawnPlayerCorpse, sizeof(SpawnPlayerCorpse_Struct)); SpawnPlayerCorpse_Struct* spc = (SpawnPlayerCorpse_Struct*)pack->pBuffer; - spc->player_corpse_id = dbid; + spc->player_corpse_id = corpse_db_id; spc->zone_id = zone->graveyard_zoneid(); worldserver.SendPacket(pack); safe_delete(pack); LogFile->write(EQEMuLog::Debug, "Moved %s player corpse to the designated graveyard in zone %s.", this->GetName(), database.GetZoneName(zone->graveyard_zoneid())); - dbid = 0; + corpse_db_id = 0; } corpse_graveyard_timer.Disable(); @@ -828,18 +853,26 @@ bool Corpse::Process() { corpse_res_timer.Disable(); } */ - if(corpse_decay_timer.Check()) { - if(!RuleB(Zone, EnableShadowrest)) + + /* This is when a corpse hits decay timer and does checks*/ + if (corpse_decay_timer.Check()) { + /* NPC */ + if (IsNPCCorpse()){ + corpse_decay_timer.Disable(); + return false; + } + /* Client */ + if (!RuleB(Zone, EnableShadowrest)){ Delete(); + } else { - if(database.BuryPlayerCorpse(dbid)) { + if (database.BuryCharacterCorpse(corpse_db_id)) { Save(); - p_depop = true; - dbid = 0; + player_corpse_depop = true; + corpse_db_id = 0; LogFile->write(EQEMuLog::Debug, "Tagged %s player corpse has burried.", this->GetName()); } - else - { + else { LogFile->write(EQEMuLog::Error, "Unable to bury %s player corpse.", this->GetName()); return true; } @@ -858,109 +891,107 @@ void Corpse::SetDecayTimer(uint32 decaytime) { corpse_decay_timer.Start(decaytime); } -bool Corpse::CanMobLoot(int charid) { - uint8 z=0; - for(int i=0; i= MAX_LOOTERS) return; if(them == nullptr || !them->IsClient()) return; - looters[slot] = them->CastToClient()->CharacterID(); + allowed_looters[slot] = them->CastToClient()->CharacterID(); } -// @merth: this function needs some work void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* app) { // Added 12/08. Started compressing loot struct on live. char tmp[10]; - if(p_depop) { + if(player_corpse_depop) { SendLootReqErrorPacket(client, 0); return; } - if(IsPlayerCorpse() && dbid == 0) { + if(IsPlayerCorpse() && corpse_db_id == 0) { // SendLootReqErrorPacket(client, 0); client->Message(13, "Warning: Corpse's dbid = 0! Corpse will not survive zone shutdown!"); std::cout << "Error: PlayerCorpse::MakeLootRequestPackets: dbid = 0!" << std::endl; // return; } - if(pLocked && client->Admin() < 100) { + if(is_locked && client->Admin() < 100) { SendLootReqErrorPacket(client, 0); client->Message(13, "Error: Corpse locked by GM."); return; } - if(BeingLootedBy == 0) { BeingLootedBy = 0xFFFFFFFF; } + if(being_looted_by == 0) { + being_looted_by = 0xFFFFFFFF; + } - if(this->BeingLootedBy != 0xFFFFFFFF) { + if(this->being_looted_by != 0xFFFFFFFF) { // lets double check.... - Entity* looter = entity_list.GetID(this->BeingLootedBy); - if(looter == 0) { this->BeingLootedBy = 0xFFFFFFFF; } + Entity* looter = entity_list.GetID(this->being_looted_by); + if(looter == 0) { + this->being_looted_by = 0xFFFFFFFF; + } } - uint8 tCanLoot = 1; - bool lootcoin = false; - if(database.GetVariable("LootCoin", tmp, 9)) { lootcoin = (atoi(tmp) == 1); } + uint8 Loot_Request_Type = 1; + bool loot_coin = false; + if(database.GetVariable("LootCoin", tmp, 9)) { loot_coin = (atoi(tmp) == 1); } - if(this->BeingLootedBy != 0xFFFFFFFF && this->BeingLootedBy != client->GetID()) { + if (this->being_looted_by != 0xFFFFFFFF && this->being_looted_by != client->GetID()) { SendLootReqErrorPacket(client, 0); - tCanLoot = 0; + Loot_Request_Type = 0; + } + else if (IsPlayerCorpse() && char_id == client->CharacterID()) { + Loot_Request_Type = 2; + } + else if ((IsNPCCorpse() || become_npc) && CanPlayerLoot(client->CharacterID())) { + Loot_Request_Type = 2; + } + else if (GetPlayerKillItem() == -1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot all items, variable cash */ + Loot_Request_Type = 3; + } + else if (GetPlayerKillItem() == 1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot 1 item, variable cash */ + Loot_Request_Type = 4; + } + else if (GetPlayerKillItem() > 1 && CanPlayerLoot(client->CharacterID())) { /* PVP loot 1 set item, variable cash */ + Loot_Request_Type = 5; } - else if(IsPlayerCorpse() && charid == client->CharacterID()) { tCanLoot = 2; } - else if((IsNPCCorpse() || become_npc) && CanMobLoot(client->CharacterID())) { tCanLoot = 2; } - else if(GetPKItem() == -1 && CanMobLoot(client->CharacterID())) { tCanLoot = 3; } //pvp loot all items, variable cash - else if(GetPKItem() == 1 && CanMobLoot(client->CharacterID())) { tCanLoot = 4; } //pvp loot 1 item, variable cash - else if(GetPKItem() > 1 && CanMobLoot(client->CharacterID())) { tCanLoot = 5; } //pvp loot 1 set item, variable cash - if(tCanLoot == 1) { if(client->Admin() < 100 || !client->GetGM()) { SendLootReqErrorPacket(client, 2); } } + if (Loot_Request_Type == 1) { + if (client->Admin() < 100 || !client->GetGM()) { + SendLootReqErrorPacket(client, 2); + } + } - if(tCanLoot >= 2 || (tCanLoot == 1 && client->Admin() >= 100 && client->GetGM())) { - this->BeingLootedBy = client->GetID(); + if(Loot_Request_Type >= 2 || (Loot_Request_Type == 1 && client->Admin() >= 100 && client->GetGM())) { + this->being_looted_by = client->GetID(); EQApplicationPacket* outapp = new EQApplicationPacket(OP_MoneyOnCorpse, sizeof(moneyOnCorpseStruct)); moneyOnCorpseStruct* d = (moneyOnCorpseStruct*) outapp->pBuffer; d->response = 1; d->unknown1 = 0x42; d->unknown2 = 0xef; - if(tCanLoot == 2 || (tCanLoot >= 3 && lootcoin)) { // dont take the coin off if it's a gm peeking at the corpse - if(zone->lootvar != 0) { - int admin = client->Admin(); - if(zone->lootvar == 7) { client->LogLoot(client, this, 0); } - else if((admin >= 10) && (admin < 20)) { - if((zone->lootvar < 8) && (zone->lootvar > 5)) { client->LogLoot(client, this, 0); } - } - else if(admin <= 20) { - if((zone->lootvar < 8) && (zone->lootvar > 4)) { client->LogLoot(client, this, 0); } - } - else if(admin <= 80) { - if((zone->lootvar < 8) && (zone->lootvar > 3)) { client->LogLoot(client, this, 0); } - } - else if(admin <= 100) { - if((zone->lootvar < 9) && (zone->lootvar > 2)) { client->LogLoot(client, this, 0); } - } - else if(admin <= 150) { - if(((zone->lootvar < 8) && (zone->lootvar > 1)) || (zone->lootvar == 9)) { client->LogLoot(client, this, 0); } - } - else if (admin <= 255) { - if((zone->lootvar < 8) && (zone->lootvar > 0)) { client->LogLoot(client, this, 0); } - } - } + /* Dont take the coin off if it's a gm peeking at the corpse */ + if(Loot_Request_Type == 2 || (Loot_Request_Type >= 3 && loot_coin)) { if(!IsPlayerCorpse() && client->IsGrouped() && client->AutoSplitEnabled() && client->GetGroup()) { d->copper = 0; d->silver = 0; @@ -978,22 +1009,21 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a } RemoveCash(); - Save(); - client->Save(); + Save(); } outapp->priority = 6; client->QueuePacket(outapp); safe_delete(outapp); - if(tCanLoot == 5) { - int pkitem = GetPKItem(); + if(Loot_Request_Type == 5) { + int pkitem = GetPlayerKillItem(); const Item_Struct* item = database.GetItem(pkitem); ItemInst* inst = database.CreateItem(item, item->MaxCharges); if(inst) { client->SendItemPacket(EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); safe_delete(inst); } - else { client->Message(13, "Could not find item number %i to send!!", GetPKItem()); } + else { client->Message(13, "Could not find item number %i to send!!", GetPlayerKillItem()); } client->QueuePacket(app); return; @@ -1005,8 +1035,6 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a cur = itemlist.begin(); end = itemlist.end(); - uint8 containercount = 0; - int corpselootlimit = EQLimits::InventoryMapSize(MapCorpse, client->GetClientVersion()); for(; cur != end; ++cur) { @@ -1016,12 +1044,12 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a // Dont display the item if it's in a bag // Added cursor queue slots to corpse item visibility list. Nothing else should be making it to corpse. - if(!IsPlayerCorpse() || item_data->equipSlot <= MainCursor || item_data->equipSlot == MainPowerSource || tCanLoot>=3 || - (item_data->equipSlot >= 8000 && item_data->equipSlot <= 8999)) { + if(!IsPlayerCorpse() || item_data->equip_slot <= MainCursor || item_data->equip_slot == MainPowerSource || Loot_Request_Type>=3 || + (item_data->equip_slot >= 8000 && item_data->equip_slot <= 8999)) { if(i < corpselootlimit) { item = database.GetItem(item_data->item_id); if(client && item) { - ItemInst* inst = database.CreateItem(item, item_data->charges, item_data->aug1, item_data->aug2, item_data->aug3, item_data->aug4, item_data->aug5); + ItemInst* inst = database.CreateItem(item, item_data->charges, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5); if(inst) { // MainGeneral1 is the corpse inventory start offset for Ti(EMu) - CORPSE_END = MainGeneral1 + MainCursor client->SendItemPacket(i + EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); @@ -1036,7 +1064,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a } } - if(IsPlayerCorpse() && (charid == client->CharacterID() || client->GetGM())) { + if(IsPlayerCorpse() && (char_id == client->CharacterID() || client->GetGM())) { if(i > corpselootlimit) { client->Message(15, "*** This corpse contains more items than can be displayed! ***"); client->Message(0, "Remove items and re-loot corpse to access remaining inventory."); @@ -1046,14 +1074,14 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a if(IsPlayerCorpse() && i == 0 && itemlist.size() > 0) { // somehow, player corpse contains items, but client doesn't see them... client->Message(13, "This corpse contains items that are inaccessable!"); client->Message(15, "Contact a GM for item replacement, if necessary."); - client->Message(15, "BUGGED CORPSE [DBID: %i, Name: %s, Item Count: %i]", GetDBID(), GetName(), itemlist.size()); + client->Message(15, "BUGGED CORPSE [DBID: %i, Name: %s, Item Count: %i]", GetCorpseDBID(), GetName(), itemlist.size()); cur = itemlist.begin(); end = itemlist.end(); for(; cur != end; ++cur) { ServerLootItem_Struct* item_data = *cur; item = database.GetItem(item_data->item_id); - LogFile->write(EQEMuLog::Debug, "Corpse Looting: %s was not sent to client loot window (corpse_dbid: %i, charname: %s(%s))", item->Name, GetDBID(), client->GetName(), client->GetGM() ? "GM" : "Owner"); + LogFile->write(EQEMuLog::Debug, "Corpse Looting: %s was not sent to client loot window (corpse_dbid: %i, charname: %s(%s))", item->Name, GetCorpseDBID(), client->GetName(), client->GetGM() ? "GM" : "Owner"); client->Message(0, "Inaccessable Corpse Item: %s", item->Name); } } @@ -1068,46 +1096,51 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a if(client->GetClientVersion() >= EQClientSoD) { SendLootReqErrorPacket(client, 6); } } -void Corpse::LootItem(Client* client, const EQApplicationPacket* app) -{ - //this gets sent out no matter what as a sort of 'ack', so send it here. +void Corpse::LootItem(Client* client, const EQApplicationPacket* app) { + /* This gets sent no matter what as a sort of ACK */ client->QueuePacket(app); - if(!loot_cooldown_timer.Check()) - { + if (!loot_cooldown_timer.Check()) { SendEndLootErrorPacket(client); + //unlock corpse for others + if (this->being_looted_by = client->GetID()) { + being_looted_by = 0xFFFFFFFF; + } return; } - // To prevent item loss for a player using 'Loot All' who doesn't have inventory space for all their items. - if(RuleB(Character, CheckCursorEmptyWhenLooting) && !client->GetInv().CursorEmpty()) - { + /* To prevent item loss for a player using 'Loot All' who doesn't have inventory space for all their items. */ + if (RuleB(Character, CheckCursorEmptyWhenLooting) && !client->GetInv().CursorEmpty()) { client->Message(13, "You may not loot an item while you have an item on your cursor."); SendEndLootErrorPacket(client); + /* Unlock corpse for others */ + if (this->being_looted_by = client->GetID()) { + being_looted_by = 0xFFFFFFFF; + } return; } LootingItem_Struct* lootitem = (LootingItem_Struct*)app->pBuffer; - if (this->BeingLootedBy != client->GetID()) { + if (this->being_looted_by != client->GetID()) { client->Message(13, "Error: Corpse::LootItem: BeingLootedBy != client"); SendEndLootErrorPacket(client); return; } - if (IsPlayerCorpse() && !CanMobLoot(client->CharacterID()) && !become_npc && (charid != client->CharacterID() && client->Admin() < 150)) { + if (IsPlayerCorpse() && !CanPlayerLoot(client->CharacterID()) && !become_npc && (char_id != client->CharacterID() && client->Admin() < 150)) { client->Message(13, "Error: This is a player corpse and you dont own it."); SendEndLootErrorPacket(client); return; } - if (pLocked && client->Admin() < 100) { + if (is_locked && client->Admin() < 100) { SendLootReqErrorPacket(client, 0); client->Message(13, "Error: Corpse locked by GM."); return; } - if(IsPlayerCorpse() && (charid != client->CharacterID()) && CanMobLoot(client->CharacterID()) && GetPKItem()==0){ + if (IsPlayerCorpse() && (char_id != client->CharacterID()) && CanPlayerLoot(client->CharacterID()) && GetPlayerKillItem() == 0){ client->Message(13, "Error: You cannot loot any more items from this corpse."); SendEndLootErrorPacket(client); - BeingLootedBy = 0xFFFFFFFF; + being_looted_by = 0xFFFFFFFF; return; } const Item_Struct* item = 0; @@ -1115,50 +1148,46 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) ServerLootItem_Struct* item_data = nullptr, *bag_item_data[10]; memset(bag_item_data, 0, sizeof(bag_item_data)); - if(GetPKItem()>1) - item = database.GetItem(GetPKItem()); - else if(GetPKItem()==-1 || GetPKItem()==1) + if (GetPlayerKillItem() > 1){ + item = database.GetItem(GetPlayerKillItem()); + } + else if (GetPlayerKillItem() == -1 || GetPlayerKillItem() == 1){ item_data = GetItem(lootitem->slot_id - EmuConstants::CORPSE_BEGIN); //dont allow them to loot entire bags of items as pvp reward - else + } + else{ item_data = GetItem(lootitem->slot_id - EmuConstants::CORPSE_BEGIN, bag_item_data); + } - if (GetPKItem()<=1 && item_data != 0) - { + if (GetPlayerKillItem()<=1 && item_data != 0) { item = database.GetItem(item_data->item_id); } - if (item != 0) - { - if(item_data) - inst = database.CreateItem(item, item_data?item_data->charges:0, item_data->aug1, item_data->aug2, item_data->aug3, item_data->aug4, item_data->aug5); - else + if (item != 0) { + if (item_data){ + inst = database.CreateItem(item, item_data ? item_data->charges : 0, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5); + } + else { inst = database.CreateItem(item); + } } - if (client && inst) - { - - if (client->CheckLoreConflict(item)) - { - client->Message_StringID(0,LOOT_LORE_ERROR); + if (client && inst) { + if (client->CheckLoreConflict(item)) { + client->Message_StringID(0, LOOT_LORE_ERROR); SendEndLootErrorPacket(client); - BeingLootedBy = 0; + being_looted_by = 0; delete inst; return; } - if(inst->IsAugmented()) - { - for (int i = AUG_BEGIN; iIsAugmented()) { + for (int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { ItemInst *itm = inst->GetAugment(i); - if(itm) - { - if(client->CheckLoreConflict(itm->GetItem())) - { - client->Message_StringID(0,LOOT_LORE_ERROR); + if (itm) { + if (client->CheckLoreConflict(itm->GetItem())) { + client->Message_StringID(0, LOOT_LORE_ERROR); SendEndLootErrorPacket(client); - BeingLootedBy = 0; + being_looted_by = 0; delete inst; return; } @@ -1168,99 +1197,67 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) char buf[88]; char corpse_name[64]; - strcpy(corpse_name, orgname); + strcpy(corpse_name, corpse_name); snprintf(buf, 87, "%d %d %s", inst->GetItem()->ID, inst->GetCharges(), EntityList::RemoveNumbers(corpse_name)); buf[87] = '\0'; - std::vector args; + std::vector args; args.push_back(inst); args.push_back(this); parse->EventPlayer(EVENT_LOOT, client, buf, 0, &args); parse->EventItem(EVENT_LOOT, client, inst, this, buf, 0); - if ((RuleB(Character, EnableDiscoveredItems))) - { - if(client && !client->GetGM() && !client->IsDiscovered(inst->GetItem()->ID)) + if ((RuleB(Character, EnableDiscoveredItems))) { + if (client && !client->GetGM() && !client->IsDiscovered(inst->GetItem()->ID)) client->DiscoverItem(inst->GetItem()->ID); } - if (zone->lootvar != 0) - { - int admin=client->Admin(); - if (zone->lootvar==7){ - client->LogLoot(client,this,item); - } - else if ((admin>=10) && (admin<20)){ - if ((zone->lootvar<8) && (zone->lootvar>5)) - client->LogLoot(client,this,item); - } - else if (admin<=20){ - if ((zone->lootvar<8) && (zone->lootvar>4)) - client->LogLoot(client,this,item); - } - else if (admin<=80){ - if ((zone->lootvar<8) && (zone->lootvar>3)) - client->LogLoot(client,this,item); - } - else if (admin<=100){ - if ((zone->lootvar<9) && (zone->lootvar>2)) - client->LogLoot(client,this,item); - } - else if (admin<=150){ - if (((zone->lootvar<8) && (zone->lootvar>1)) || (zone->lootvar==9)) - client->LogLoot(client,this,item); - } - else if (admin<=255){ - if ((zone->lootvar<8) && (zone->lootvar>0)) - client->LogLoot(client,this,item); - } - } - - if(zone->adv_data) - { + if (zone->adv_data) { ServerZoneAdventureDataReply_Struct *ad = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if(ad->type == Adventure_Collect && !IsPlayerCorpse()) - { - if(ad->data_id == inst->GetItem()->ID) - { + if (ad->type == Adventure_Collect && !IsPlayerCorpse()) { + if (ad->data_id == inst->GetItem()->ID) { zone->DoAdventureCountIncrease(); } } } - // first add it to the looter - this will do the bag contents too - if(lootitem->auto_loot) - { - if(!client->AutoPutLootInInventory(*inst, true, true, bag_item_data)) + /* First add it to the looter - this will do the bag contents too */ + if (lootitem->auto_loot) { + if (!client->AutoPutLootInInventory(*inst, true, true, bag_item_data)) client->PutLootInInventory(MainCursor, *inst, bag_item_data); } - else - { + else { client->PutLootInInventory(MainCursor, *inst, bag_item_data); } - // Update any tasks that have an activity to loot this item. - if(RuleB(TaskSystem, EnableTaskSystem)) + + /* Update any tasks that have an activity to loot this item */ + if (RuleB(TaskSystem, EnableTaskSystem)) client->UpdateTasksForItem(ActivityLoot, item->ID); - // now remove it from the corpse - if(item_data) - RemoveItem(item_data->lootslot); - // remove bag contents too - if (item->ItemClass == ItemClassContainer && (GetPKItem()!=-1 || GetPKItem()!=1)) - { - for (int i = SUB_BEGIN; i < EmuConstants::ITEM_CONTAINER_SIZE; i++) - { - if (bag_item_data[i]) - { - RemoveItem(bag_item_data[i]); + + /* Remove it from Corpse */ + if (item_data){ + /* Delete needs to be before RemoveItem because its deletes the pointer for item_data/bag_item_data */ + database.DeleteItemOffCharacterCorpse(this->corpse_db_id, item_data->equip_slot, item_data->item_id); + /* Delete Item Instance */ + RemoveItem(item_data->lootslot); + } + + /* Remove Bag Contents */ + if (item->ItemClass == ItemClassContainer && (GetPlayerKillItem() != -1 || GetPlayerKillItem() != 1)) { + for (int i = SUB_BEGIN; i < EmuConstants::ITEM_CONTAINER_SIZE; i++) { + if (bag_item_data[i]) { + /* Delete needs to be before RemoveItem because its deletes the pointer for item_data/bag_item_data */ + database.DeleteItemOffCharacterCorpse(this->corpse_db_id, bag_item_data[i]->equip_slot, bag_item_data[i]->item_id); + /* Delete Item Instance */ + RemoveItem(bag_item_data[i]); } } } - if(GetPKItem()!=-1) - SetPKItem(0); + if (GetPlayerKillItem() != -1){ + SetPlayerKillItemID(0); + } - //now send messages to all interested parties - - //creates a link for the item + /* Send message with item link to groups and such */ char *link = 0, *link2 = 0; //just like a db query :-) client->MakeItemLink(link2, inst); MakeAnyLenString(&link, "%c" "%s" "%s" "%c", @@ -1284,17 +1281,18 @@ void Corpse::LootItem(Client* client, const EQApplicationPacket* app) } safe_delete_array(link); } - else - { + else { SendEndLootErrorPacket(client); safe_delete(inst); return; } - if (IsPlayerCorpse()) + if (IsPlayerCorpse()){ client->SendItemLink(inst); - else + } + else{ client->SendItemLink(inst, true); + } safe_delete(inst); } @@ -1306,16 +1304,14 @@ void Corpse::EndLoot(Client* client, const EQApplicationPacket* app) { client->QueuePacket(outapp); safe_delete(outapp); - //client->Save(); //inventory operations auto-commit - this->BeingLootedBy = 0xFFFFFFFF; + this->being_looted_by = 0xFFFFFFFF; if (this->IsEmpty()) Delete(); else Save(); } -void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) -{ +void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { Mob::FillSpawnStruct(ns, ForWho); ns->spawn.max_hp = 120; @@ -1326,8 +1322,7 @@ void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.NPC = 2; } -void Corpse::QueryLoot(Client* to) { - +void Corpse::QueryLoot(Client* to) { int x = 0, y = 0; // x = visible items, y = total items to->Message(0, "Coin: %ip, %ig, %is, %ic", platinum, gold, silver, copper); @@ -1341,7 +1336,7 @@ void Corpse::QueryLoot(Client* to) { ServerLootItem_Struct* sitem = *cur; if (IsPlayerCorpse()) { - if (sitem->equipSlot >= EmuConstants::GENERAL_BAGS_BEGIN && sitem->equipSlot <= EmuConstants::CURSOR_BAG_END) + if (sitem->equip_slot >= EmuConstants::GENERAL_BAGS_BEGIN && sitem->equip_slot <= EmuConstants::CURSOR_BAG_END) sitem->lootslot = 0xFFFF; else x < corpselootlimit ? sitem->lootslot = x : sitem->lootslot = 0xFFFF; @@ -1349,7 +1344,7 @@ void Corpse::QueryLoot(Client* to) { const Item_Struct* item = database.GetItem(sitem->item_id); if (item) - to->Message((sitem->lootslot == 0xFFFF), "LootSlot: %i (EquipSlot: %i) Item: %s (%d), Count: %i", static_cast(sitem->lootslot), sitem->equipSlot, item->Name, item->ID, sitem->charges); + to->Message((sitem->lootslot == 0xFFFF), "LootSlot: %i (EquipSlot: %i) Item: %s (%d), Count: %i", static_cast(sitem->lootslot), sitem->equip_slot, item->Name, item->ID, sitem->charges); else to->Message((sitem->lootslot == 0xFFFF), "Error: 0x%04x", sitem->item_id); @@ -1372,15 +1367,14 @@ void Corpse::QueryLoot(Client* to) { } if (IsPlayerCorpse()) { - to->Message(0, "%i visible %s (%i total) on %s (DBID: %i).", x, x==1?"item":"items", y, this->GetName(), this->GetDBID()); + to->Message(0, "%i visible %s (%i total) on %s (DBID: %i).", x, x==1?"item":"items", y, this->GetName(), this->GetCorpseDBID()); } else { to->Message(0, "%i %s on %s.", y, y==1?"item":"items", this->GetName()); } } -bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) -{ +bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { uint32 dist2 = 10000; // pow(100, 2); if (!spell) { if (this->GetCharID() == client->CharacterID()) { @@ -1388,13 +1382,11 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) client->Message(13, "That corpse is locked by a GM."); return false; } - if (!CheckDistance || (DistNoRootNoZ(*client) <= dist2)) - { + if (!CheckDistance || (DistNoRootNoZ(*client) <= dist2)) { GMMove(client->GetX(), client->GetY(), client->GetZ()); - pIsChanged = true; + is_corpse_changed = true; } - else - { + else { client->Message(0, "Corpse is too far away."); return false; } @@ -1403,25 +1395,20 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) { bool consented = false; std::list::iterator itr; - for(itr = client->consent_list.begin(); itr != client->consent_list.end(); ++itr) - { - if(strcmp(this->GetOwnerName(), itr->c_str()) == 0) - { - if (!CheckDistance || (DistNoRootNoZ(*client) <= dist2)) - { + for(itr = client->consent_list.begin(); itr != client->consent_list.end(); ++itr) { + if(strcmp(this->GetOwnerName(), itr->c_str()) == 0) { + if (!CheckDistance || (DistNoRootNoZ(*client) <= dist2)) { GMMove(client->GetX(), client->GetY(), client->GetZ()); - pIsChanged = true; + is_corpse_changed = true; } - else - { + else { client->Message(0, "Corpse is too far away."); return false; } consented = true; } } - if(!consented) - { + if(!consented) { client->Message(0, "You do not have permission to move this corpse."); return false; } @@ -1429,15 +1416,15 @@ bool Corpse::Summon(Client* client, bool spell, bool CheckDistance) } else { GMMove(client->GetX(), client->GetY(), client->GetZ()); - pIsChanged = true; + is_corpse_changed = true; } Save(); return true; } -void Corpse::CompleteRezz(){ - rezzexp = 0; - pIsChanged = true; +void Corpse::CompleteResurrection(){ + rez_experience = 0; + is_corpse_changed = true; this->Save(); } @@ -1448,539 +1435,10 @@ void Corpse::Spawn() { safe_delete(app); } -bool ZoneDatabase::DeleteGraveyard(uint32 zone_id, uint32 graveyard_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256]; - uint32 query_length = 0; - uint32 affected_rows = 0; - - query_length = sprintf(query,"UPDATE zone SET graveyard_id=0 WHERE zoneidnumber=%u AND version=0", zone_id); - - if (!RunQuery(query, query_length, errbuf, 0, &affected_rows)) { - safe_delete_array(query); - std::cerr << "Error1 in DeleteGraveyard query " << errbuf << std::endl; - return false; - } - - if (affected_rows == 0) { - std::cerr << "Error2 in DeleteGraveyard query: affected_rows = 0" << std::endl; - return false; - } - - query_length = sprintf(query,"DELETE FROM graveyard WHERE id=%u", graveyard_id); - - if (!RunQuery(query, query_length, errbuf, 0, &affected_rows)) { - safe_delete_array(query); - std::cerr << "Error3 in DeleteGraveyard query " << errbuf << std::endl; - return false; - } - safe_delete_array(query); - - if (affected_rows == 0) { - std::cerr << "Error4 in DeleteGraveyard query: affected_rows = 0" << std::endl; - return false; - } - - return true; -} -uint32 ZoneDatabase::AddGraveyardIDToZone(uint32 zone_id, uint32 graveyard_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256]; - char* end = query; - uint32 affected_rows = 0; - - end += sprintf(end,"UPDATE zone SET graveyard_id=%u WHERE zoneidnumber=%u AND version=0", graveyard_id, zone_id); - - if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - std::cerr << "Error1 in AddGraveyardIDToZone query " << errbuf << std::endl; - return 0; - } - safe_delete_array(query); - - if (affected_rows == 0) { - std::cerr << "Error2 in AddGraveyardIDToZone query: affected_rows = 0" << std::endl; - return 0; - } - - return zone_id; -} -uint32 ZoneDatabase::NewGraveyardRecord(uint32 graveyard_zoneid, float graveyard_x, float graveyard_y, float graveyard_z, float graveyard_heading) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256]; - char* end = query; - uint32 affected_rows = 0; - uint32 new_graveyard_id = 0; - - end += sprintf(end,"INSERT INTO graveyard SET zone_id=%u, x=%1.1f, y=%1.1f, z=%1.1f, heading=%1.1f", graveyard_zoneid, graveyard_x, graveyard_y, graveyard_z, graveyard_heading); - - if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows, &new_graveyard_id)) { - safe_delete_array(query); - std::cerr << "Error1 in NewGraveyardRecord query " << errbuf << std::endl; - return 0; - } - safe_delete_array(query); - - if (affected_rows == 0) { - std::cerr << "Error2 in NewGraveyardRecord query: affected_rows = 0" << std::endl; - return 0; - } - - if(new_graveyard_id <= 0) { - std::cerr << "Error3 in NewGraveyardRecord query: new_graveyard_id <= 0" << std::endl; - return 0; - } - - return new_graveyard_id; -} -uint32 ZoneDatabase::GraveyardPlayerCorpse(uint32 dbid, uint32 zoneid, uint16 instanceid, float x, float y, float z, float heading) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256]; - char* end = query; - uint32 affected_rows = 0; - - // We probably don't want a graveyard located in an instance. - end += sprintf(end,"Update player_corpses SET zoneid=%u, instanceid=0, x=%1.1f, y=%1.1f, z=%1.1f, heading=%1.1f, WasAtGraveyard=1 WHERE id=%d", zoneid, x, y, z, heading, dbid); - - if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - std::cerr << "Error1 in GraveyardPlayerCorpse query " << errbuf << std::endl; - return 0; - } - safe_delete_array(query); - - if (affected_rows == 0) { - std::cerr << "Error2 in GraveyardPlayerCorpse query: affected_rows = 0" << std::endl; - return 0; - } - return dbid; -} -uint32 ZoneDatabase::UpdatePlayerCorpse(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading, bool rezzed) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256+(datasize*2)]; - char* end = query; - uint32 affected_rows = 0; - - end += sprintf(end, "Update player_corpses SET data="); - *end++ = '\''; - end += DoEscapeString(end, (char*)data, datasize); - *end++ = '\''; - end += sprintf(end,", charname='%s', zoneid=%u, instanceid=%u, charid=%d, x=%1.1f, y=%1.1f, z=%1.1f, heading=%1.1f WHERE id=%d", charname, zoneid, instanceid, charid, x, y, z, heading, dbid); - - if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - std::cerr << "Error1 in UpdatePlayerCorpse query " << errbuf << std::endl; - return 0; - } - safe_delete_array(query); - - if (affected_rows == 0) { - std::cerr << "Error2 in UpdatePlayerCorpse query: affected_rows = 0" << std::endl; - return 0; - } - if(rezzed){ - if (!RunQuery(query, MakeAnyLenString(&query, "update player_corpses set rezzed = 1 WHERE id=%d",dbid), errbuf)) { - std::cerr << "Error in UpdatePlayerCorpse/Rezzed query: " << errbuf << std::endl; - } - safe_delete_array(query); - } - return dbid; -} - -void ZoneDatabase::MarkCorpseAsRezzed(uint32 dbid) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - if(!database.RunQuery(query,MakeAnyLenString(&query, "UPDATE player_corpses SET rezzed = 1 WHERE id = %i", dbid), errbuf)) - { - LogFile->write(EQEMuLog::Error, "MarkCorpseAsRezzed failed: %s, %s", query, errbuf); - } - safe_delete_array(query); -} - -uint32 ZoneDatabase::CreatePlayerCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256+(datasize*2)]; - char* end = query; - //MYSQL_RES *result; - //MYSQL_ROW row; - uint32 affected_rows = 0; - uint32 last_insert_id = 0; - - end += sprintf(end, "Insert into player_corpses SET data="); - *end++ = '\''; - end += DoEscapeString(end, (char*)data, datasize); - *end++ = '\''; - end += sprintf(end,", charname='%s', zoneid=%u, instanceid=%u, charid=%d, x=%1.1f, y=%1.1f, z=%1.1f, heading=%1.1f, timeofdeath=Now(), IsBurried=0", charname, zoneid, instanceid, charid, x, y, z, heading); - - if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows, &last_insert_id)) { - safe_delete_array(query); - std::cerr << "Error1 in CreatePlayerCorpse query " << errbuf << std::endl; - return 0; - } - safe_delete_array(query); - - if (affected_rows == 0) { - std::cerr << "Error2 in CreatePlayerCorpse query: affected_rows = 0" << std::endl; - return 0; - } - - if (last_insert_id == 0) { - std::cerr << "Error3 in CreatePlayerCorpse query: last_insert_id = 0" << std::endl; - return 0; - } - - return last_insert_id; -} - -bool ZoneDatabase::CreatePlayerCorpseBackup(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256+(datasize*2)]; - char* end = query; - uint32 affected_rows = 0; - uint32 last_insert_id = 0; - bool result = false; - DBPlayerCorpse_Struct* dbpcs = (DBPlayerCorpse_Struct*) data; - - if (dbid != 0) { - if(RuleB(Character, LeaveCorpses) == true && dbpcs->level >= RuleI(Character, DeathItemLossLevel)){ - end += sprintf(end, "Insert into player_corpses_backup SET data="); - *end++ = '\''; - end += DoEscapeString(end, (char*)data, datasize); - *end++ = '\''; - end += sprintf(end,", charname='%s', zoneid=%u, instanceid=%u, charid=%d, x=%1.1f, y=%1.1f, z=%1.1f, heading=%1.1f, timeofdeath=Now(), IsBurried=0, id=%u", charname, zoneid, instanceid, charid, x, y, z, heading, dbid); - - if (RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { - if (affected_rows == 1) - result = true; - else - std::cerr << "Error in CreatePlayerCorpseBackup query: affected_rows != 1" << std::endl; - } - else - std::cerr << "Error in CreatePlayerCorpseBackup query " << errbuf << std::endl; - } - safe_delete_array(query); - } - else { - std::cerr << "Error in CreatePlayerCorpseBackup: dbid = 0" << std::endl; - } - return result; -} - -uint32 ZoneDatabase::GetPlayerBurriedCorpseCount(uint32 char_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 CorpseCount = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "select count(*) from player_corpses where charid = '%u' and IsBurried = 1", char_id), errbuf, &result)) { - row = mysql_fetch_row(result); - CorpseCount = atoi(row[0]); - mysql_free_result(result); - } - else { - std::cerr << "Error in GetPlayerBurriedCorpseCount query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); - - return CorpseCount; -} - -uint32 ZoneDatabase::GetPlayerCorpseCount(uint32 char_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 CorpseCount = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "select count(*) from player_corpses where charid = '%u'", char_id), errbuf, &result)) { - row = mysql_fetch_row(result); - CorpseCount = atoi(row[0]); - mysql_free_result(result); - } - else { - std::cerr << "Error in GetPlayerCorpseCount query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); - - return CorpseCount; -} - -uint32 ZoneDatabase::GetPlayerCorpseID(uint32 char_id, uint8 corpse) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 id = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "select id from player_corpses where charid = '%u'", char_id), errbuf, &result)) { - for (int i=0; iGetWornItem(slotid); - tmp->DepopCorpse(); - } - return itemid; -} - -Corpse* ZoneDatabase::SummonBurriedPlayerCorpse(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, float dest_x, float dest_y, float dest_z, float dest_heading) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - Corpse* NewCorpse = 0; - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, charname, data, timeofdeath, rezzed FROM player_corpses WHERE charid='%u' AND IsBurried=1 ORDER BY timeofdeath LIMIT 1", char_id), errbuf, &result)) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if(row) { - NewCorpse = Corpse::LoadFromDBData(atoi(row[0]), char_id, row[1], (uchar*) row[2], lengths[2], dest_x, dest_y, dest_z, dest_heading, row[3],atoi(row[4])==1, false); - if(NewCorpse) { - entity_list.AddCorpse(NewCorpse); - NewCorpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); - NewCorpse->Spawn(); - if(!UnburyPlayerCorpse(NewCorpse->GetDBID(), dest_zoneid, dest_instanceid, dest_x, dest_y, dest_z, dest_heading)) - LogFile->write(EQEMuLog::Error, "Unable to unbury a summoned player corpse for character id %u.", char_id); - } - else - LogFile->write(EQEMuLog::Error, "Unable to construct a player corpse from a burried player corpse for character id %u.", char_id); - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in SummonBurriedPlayerCorpse query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); - - return NewCorpse; -} - -bool ZoneDatabase::SummonAllPlayerCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, - float dest_x, float dest_y, float dest_z, float dest_heading) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - Corpse* NewCorpse = 0; - int CorpseCount = 0; - unsigned long* lengths; - - if(!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET zoneid = %i, instanceid = %i, x = %f, y = %f, z = %f, " - "heading = %f, IsBurried = 0, WasAtGraveyard = 0 WHERE charid = %i", - dest_zoneid, dest_instanceid, dest_x, dest_y, dest_z, dest_heading, char_id), errbuf)) - LogFile->write(EQEMuLog::Error, "Error moving corpses, Query = %s, Error = %s\n", query, errbuf); - - safe_delete_array(query); - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, charname, data, timeofdeath, rezzed FROM player_corpses WHERE charid='%u'" - "ORDER BY timeofdeath", char_id), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - lengths = mysql_fetch_lengths(result); - NewCorpse = Corpse::LoadFromDBData(atoi(row[0]), char_id, row[1], (uchar*) row[2], lengths[2], dest_x, dest_y, - dest_z, dest_heading, row[3],atoi(row[4])==1, false); - if(NewCorpse) { - entity_list.AddCorpse(NewCorpse); - NewCorpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); - NewCorpse->Spawn(); - ++CorpseCount; - } - else - LogFile->write(EQEMuLog::Error, "Unable to construct a player corpse for character id %u.", char_id); - } - - mysql_free_result(result); - } - else - LogFile->write(EQEMuLog::Error, "Error in SummonAllPlayerCorpses Query = %s, Error = %s\n", query, errbuf); - - safe_delete_array(query); - - return (CorpseCount > 0); -} - -bool ZoneDatabase::UnburyPlayerCorpse(uint32 dbid, uint32 new_zoneid, uint16 new_instanceid, float new_x, float new_y, float new_z, float new_heading) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = new char[256]; - char* end = query; - uint32 affected_rows = 0; - bool Result = false; - - end += sprintf(end, "UPDATE player_corpses SET IsBurried=0, zoneid=%u, instanceid=%u, x=%f, y=%f, z=%f, heading=%f, timeofdeath=Now(), WasAtGraveyard=0 WHERE id=%u", new_zoneid, new_instanceid, new_x, new_y, new_z, new_heading, dbid); - - if (RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { - if (affected_rows == 1) - Result = true; - else - std::cerr << "Error2 in UnburyPlayerCorpse query: affected_rows NOT EQUAL to 1, as expected." << std::endl; - } - else - std::cerr << "Error1 in UnburyPlayerCorpse query " << errbuf << std::endl; - - safe_delete_array(query); - - return Result; -} - -Corpse* ZoneDatabase::LoadPlayerCorpse(uint32 player_corpse_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - Corpse* NewCorpse = 0; - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, charid, charname, x, y, z, heading, data, timeofdeath, rezzed, WasAtGraveyard FROM player_corpses WHERE id='%u'", player_corpse_id), errbuf, &result)) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if(row && lengths) - { - NewCorpse = Corpse::LoadFromDBData(atoi(row[0]), atoi(row[1]), row[2], (uchar*) row[7], lengths[7], atof(row[3]), atoi(row[4]), atoi(row[5]), atoi(row[6]), row[8],atoi(row[9])==1, atoi(row[10])); - entity_list.AddCorpse(NewCorpse); - } - mysql_free_result(result); - } - else { - std::cerr << "Error in LoadPlayerCorpse query '" << query << "' " << errbuf << std::endl; - std::cerr << "Note that if your missing the 'rezzed' field you can add it with:\nALTER TABLE `player_corpses` ADD `rezzed` TINYINT UNSIGNED DEFAULT \"0\";\n"; - } - - safe_delete_array(query); - - return NewCorpse; -} - -bool ZoneDatabase::LoadPlayerCorpses(uint32 iZoneID, uint16 iInstanceID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 query_length = 0; - - unsigned long* lengths; - - if(!RuleB(Zone, EnableShadowrest)) - query_length = MakeAnyLenString(&query, "SELECT id, charid, charname, x, y, z, heading, data, timeofdeath, rezzed, WasAtGraveyard FROM player_corpses WHERE zoneid='%u' AND instanceid='%u'", iZoneID, iInstanceID); - else - query_length = MakeAnyLenString(&query, "SELECT id, charid, charname, x, y, z, heading, data, timeofdeath, rezzed, 0 FROM player_corpses WHERE zoneid='%u' AND instanceid='%u' AND IsBurried=0", iZoneID, iInstanceID); - - if (RunQuery(query, query_length, errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - lengths = mysql_fetch_lengths(result); - entity_list.AddCorpse(Corpse::LoadFromDBData(atoi(row[0]), atoi(row[1]), row[2], (uchar*) row[7], lengths[7], atof(row[3]), atoi(row[4]), atoi(row[5]), atoi(row[6]), row[8],atoi(row[9])==1, atoi(row[10]))); - } - mysql_free_result(result); - } - else { - std::cerr << "Error in LoadPlayerCorpses query '" << query << "' " << errbuf << std::endl; - std::cerr << "Note that if your missing the 'rezzed' field you can add it with:\nALTER TABLE `player_corpses` ADD `rezzed` TINYINT UNSIGNED DEFAULT \"0\";\n"; - safe_delete_array(query); - return false; - } - - return true; -} - -uint32 ZoneDatabase::GetFirstCorpseID(uint32 char_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 CorpseID = 0; - - MakeAnyLenString(&query, "SELECT id FROM player_corpses WHERE charid='%u' AND IsBurried=0 ORDER BY timeofdeath LIMIT 1", char_id); - if (RunQuery(query, strlen(query), errbuf, &result)) { - if (mysql_num_rows(result)!= 0){ - row = mysql_fetch_row(result); - CorpseID = atoi(row[0]); - mysql_free_result(result); - } - } - else { - std::cerr << "Error in GetFirstCorpseID query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - - safe_delete_array(query); - return CorpseID; -} - -bool ZoneDatabase::BuryPlayerCorpse(uint32 dbid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried = 1 WHERE id=%d", dbid), errbuf)) { - std::cerr << "Error in BuryPlayerCorpse query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - safe_delete_array(query); - return true; -} - -bool ZoneDatabase::BuryAllPlayerCorpses(uint32 charid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried = 1 WHERE charid=%d", charid), errbuf)) { - std::cerr << "Error in BuryPlayerCorpse query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - safe_delete_array(query); - return true; -} - -bool ZoneDatabase::DeletePlayerCorpse(uint32 dbid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses where id=%d", dbid), errbuf)) { - std::cerr << "Error in DeletePlayerCorpse query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - safe_delete_array(query); - return true; -} - -// these functions operate with a material slot, which is from 0 to 8 uint32 Corpse::GetEquipment(uint8 material_slot) const { int invslot; - if(material_slot > EmuConstants::MATERIAL_END) - { + if(material_slot > EmuConstants::MATERIAL_END) { return NO_ITEM; } @@ -1994,14 +1452,12 @@ uint32 Corpse::GetEquipment(uint8 material_slot) const { uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { const Item_Struct *item; - if(material_slot > EmuConstants::MATERIAL_END) - { + if(material_slot > EmuConstants::MATERIAL_END) { return 0; } item = database.GetItem(GetEquipment(material_slot)); - if(item != NO_ITEM) - { + if(item != NO_ITEM) { return item_tint[material_slot].rgb.use_tint ? item_tint[material_slot].color : item->Color; @@ -2010,89 +1466,30 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const { return 0; } -void Corpse::AddLooter(Mob* who) -{ - for (int i=0; iCastToClient()->CharacterID(); +void Corpse::AddLooter(Mob* who) { + for (int i = 0; i < MAX_LOOTERS; i++) { + if (allowed_looters[i] == 0) { + allowed_looters[i] = who->CastToClient()->CharacterID(); break; } } } -void Corpse::LoadPlayerCorpseDecayTime(uint32 dbid){ - if(!dbid) +void Corpse::LoadPlayerCorpseDecayTime(uint32 corpse_db_id){ + if(!corpse_db_id) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) FROM player_corpses WHERE id=%d and not timeofdeath=0", dbid), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - if(atoi(row[0]) > 0 && RuleI(Character, CorpseDecayTimeMS) > (atoi(row[0]) * 1000)) { - corpse_decay_timer.SetTimer(RuleI(Character, CorpseDecayTimeMS) - (atoi(row[0]) * 1000)); - /* - if(RuleI(Character, CorpseResTimeMS) > (atoi(row[0]) * 1000)) { - corpse_res_timer.SetTimer(RuleI(Character, CorpseResTimeMS) - (atoi(row[0]) * 1000)); - } - else { - corpse_res_timer.Disable(); - can_rez = false; - } - */ - } - else { - corpse_decay_timer.SetTimer(2000); - //corpse_res_timer.SetTimer(300000); - } - if(atoi(row[0]) > 0 && RuleI(Zone, GraveyardTimeMS) > (atoi(row[0]) * 1000)) { - corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS) - (atoi(row[0]) * 1000)); - } - else { - corpse_graveyard_timer.SetTimer(3000); - } - } - mysql_free_result(result); + uint32 active_corpse_decay_timer = database.GetCharacterCorpseDecayTimer(corpse_db_id); + if (active_corpse_decay_timer > 0 && RuleI(Character, CorpseDecayTimeMS) > (active_corpse_decay_timer * 1000)) { + corpse_decay_timer.SetTimer(RuleI(Character, CorpseDecayTimeMS) - (active_corpse_decay_timer * 1000)); } - else - safe_delete_array(query); -} - -/* -uint32 Corpse::ServerToCorpseSlot(int16 server_slot) { - // reserved -} -*/ -/* -int16 Corpse::CorpseToServerSlot(uint32 corpse_slot) { - // reserved -} -*/ - -/* -void Corpse::CastRezz(uint16 spellid, Mob* Caster){ - if(Rezzed()){ - if(Caster && Caster->IsClient()) - Caster->Message(13,"This character has already been resurrected."); - return; + else { + corpse_decay_timer.SetTimer(2000); + } + if (active_corpse_decay_timer > 0 && RuleI(Zone, GraveyardTimeMS) > (active_corpse_decay_timer * 1000)) { + corpse_graveyard_timer.SetTimer(RuleI(Zone, GraveyardTimeMS) - (active_corpse_decay_timer * 1000)); + } + else { + corpse_graveyard_timer.SetTimer(3000); } - - APPLAYER* outapp = new APPLAYER(OP_RezzRequest, sizeof(Resurrect_Struct)); - Resurrect_Struct* rezz = (Resurrect_Struct*) outapp->pBuffer; - memcpy(rezz->your_name,this->orgname,30); - memcpy(rezz->corpse_name,this->name,30); - memcpy(rezz->rezzer_name,Caster->GetName(),30); - memcpy(rezz->zone,zone->GetShortName(),15); - rezz->spellid = spellid; - rezz->x = this->x_pos; - rezz->y = this->y_pos; - rezz->z = (float)this->z_pos; - worldserver.RezzPlayer(outapp, rezzexp, OP_RezzRequest); - //DumpPacket(outapp); - safe_delete(outapp); } -*/ diff --git a/zone/corpse.h b/zone/corpse.h index e76b3b4ce..3a3d2bce9 100644 --- a/zone/corpse.h +++ b/zone/corpse.h @@ -26,122 +26,130 @@ class NPC; #define MAX_LOOTERS 72 -class Corpse : public Mob -{ -public: - static void SendEndLootErrorPacket(Client* client); - static void SendLootReqErrorPacket(Client* client, uint8 response = 2); - static Corpse* LoadFromDBData(uint32 in_corpseid, uint32 in_charid, char* in_charname, uchar* in_data, uint32 in_datasize, float in_x, float in_y, float in_z, float in_heading, char* timeofdeath, bool rezzed = false, bool wasAtGraveyard = false); +class Corpse : public Mob { + public: + static void SendEndLootErrorPacket(Client* client); + static void SendLootReqErrorPacket(Client* client, uint8 response = 2); + Corpse(NPC* in_npc, ItemList* in_itemlist, uint32 in_npctypeid, const NPCType** in_npctypedata, uint32 in_decaytime = 600000); Corpse(Client* client, int32 in_rezexp); - Corpse(uint32 in_corpseid, uint32 in_charid, char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture,uint32 in_rezexp, bool wasAtGraveyard = false); - ~Corpse(); + Corpse(uint32 in_corpseid, uint32 in_charid, const char* in_charname, ItemList* in_itemlist, uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_plat, float in_x, float in_y, float in_z, float in_heading, float in_size, uint8 in_gender, uint16 in_race, uint8 in_class, uint8 in_deity, uint8 in_level, uint8 in_texture, uint8 in_helmtexture, uint32 in_rezexp, bool wasAtGraveyard = false); + + ~Corpse(); + static Corpse* LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std::string in_charname, float in_x, float in_y, float in_z, float in_heading, std::string time_of_death, bool rezzed, bool was_at_graveyard); - //abstract virtual function implementations requird by base abstract class - virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill) { return true; } - virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) { return; } - virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = true, bool IsFromSpell = false, - ExtraAttackOptions *opts = nullptr) { return false; } - virtual bool HasRaid() { return false; } - virtual bool HasGroup() { return false; } - virtual Raid* GetRaid() { return 0; } - virtual Group* GetGroup() { return 0; } + /* Corpse: General */ + virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill) { return true; } + virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false) { return; } + virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, + bool IsStrikethrough = true, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) { + return false; + } + virtual bool HasRaid() { return false; } + virtual bool HasGroup() { return false; } + virtual Raid* GetRaid() { return 0; } + virtual Group* GetGroup() { return 0; } + inline uint32 GetCorpseDBID() { return corpse_db_id; } + inline char* GetOwnerName() { return corpse_name; } + bool IsEmpty() const; + bool IsCorpse() const { return true; } + bool IsPlayerCorpse() const { return is_player_corpse; } + bool IsNPCCorpse() const { return !is_player_corpse; } + bool IsBecomeNPCCorpse() const { return become_npc; } + virtual void DepopNPCCorpse(); + virtual void DepopPlayerCorpse(); + bool Process(); + bool Save(); + uint32 GetCharID() { return char_id; } + uint32 SetCharID(uint32 iCharID) { if (IsPlayerCorpse()) { return (char_id = iCharID); } return 0xFFFFFFFF; }; + uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } + uint32 GetRezTime() { if (!corpse_rez_timer.Enabled()) return 0; else return corpse_rez_timer.GetRemainingTime(); } + void SetDecayTimer(uint32 decay_time); + + void Delete(); + void Bury(); + void CalcCorpseName(); + void LoadPlayerCorpseDecayTime(uint32 dbid); - void LoadPlayerCorpseDecayTime(uint32 dbid); - - bool IsCorpse() const { return true; } - bool IsPlayerCorpse() const { return p_PlayerCorpse; } - bool IsNPCCorpse() const { return !p_PlayerCorpse; } - bool IsBecomeNPCCorpse() const { return become_npc; } - bool Process(); - bool Save(); - uint32 GetCharID() { return charid; } - uint32 SetCharID(uint32 iCharID) { if (IsPlayerCorpse()) { return (charid=iCharID); } return 0xFFFFFFFF; }; - uint32 GetDecayTime() { if (!corpse_decay_timer.Enabled()) return 0xFFFFFFFF; else return corpse_decay_timer.GetRemainingTime(); } - uint32 GetResTime() { if (!corpse_res_timer.Enabled()) return 0; else return corpse_res_timer.GetRemainingTime(); } - void CalcCorpseName(); - inline void Lock() { pLocked = true; } - inline void UnLock() { pLocked = false; } - inline bool IsLocked() { return pLocked; } - inline void ResetLooter() { BeingLootedBy = 0xFFFFFFFF; } - inline bool IsBeingLooted() { return (BeingLootedBy != 0xFFFFFFFF); } - inline uint32 GetDBID() { return dbid; } - inline char* GetOwnerName() { return orgname;} - - void SetDecayTimer(uint32 decaytime); - bool IsEmpty() const; - void AddItem(uint32 itemnum, uint16 charges, int16 slot = 0, uint32 aug1=0, uint32 aug2=0, uint32 aug3=0, uint32 aug4=0, uint32 aug5=0); - uint32 GetWornItem(int16 equipSlot) const; - ServerLootItem_Struct* GetItem(uint16 lootslot, ServerLootItem_Struct** bag_item_data = 0); + /* Corpse: Items */ + uint32 GetWornItem(int16 equipSlot) const; + ServerLootItem_Struct* GetItem(uint16 lootslot, ServerLootItem_Struct** bag_item_data = 0); + void SetPlayerKillItemID(int32 pk_item_id) { player_kill_item = pk_item_id; } + int32 GetPlayerKillItem() { return player_kill_item; } void RemoveItem(uint16 lootslot); void RemoveItem(ServerLootItem_Struct* item_data); + void AddItem(uint32 itemnum, uint16 charges, int16 slot = 0, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0); + + /* Corpse: Coin */ void SetCash(uint32 in_copper, uint32 in_silver, uint32 in_gold, uint32 in_platinum); void RemoveCash(); - void QueryLoot(Client* to); - uint32 CountItems(); - void Delete(); - void Bury(); - virtual void Depop(); - virtual void DepopCorpse(); - uint32 GetCopper() { return copper; } uint32 GetSilver() { return silver; } uint32 GetGold() { return gold; } uint32 GetPlatinum() { return platinum; } - void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); - void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app); + /* Corpse: Resurrection */ + bool IsRezzed() { return rez; } + void IsRezzed(bool in_rez) { rez = in_rez; } + void CastRezz(uint16 spellid, Mob* Caster); + void CompleteResurrection(); + + /* Corpse: Loot */ + void QueryLoot(Client* to); void LootItem(Client* client, const EQApplicationPacket* app); void EndLoot(Client* client, const EQApplicationPacket* app); - bool Summon(Client* client, bool spell, bool CheckDistance); - void CastRezz(uint16 spellid, Mob* Caster); - void CompleteRezz(); - void SetPKItem(int32 id) { pkitem = id; } - int32 GetPKItem() { return pkitem; } - bool CanMobLoot(int charid); - void AllowMobLoot(Mob *them, uint8 slot); - void AddLooter(Mob *who); - bool Rezzed() { return rez; } - void Rezzed(bool in_rez) { rez = in_rez; } - void Spawn(); + void MakeLootRequestPackets(Client* client, const EQApplicationPacket* app); + void AllowPlayerLoot(Mob *them, uint8 slot); + void AddLooter(Mob *who); + uint32 CountItems(); + bool CanPlayerLoot(int charid); - char orgname[64]; - uint32 GetEquipment(uint8 material_slot) const; // returns item id - uint32 GetEquipmentColor(uint8 material_slot) const; - inline int GetRezzExp() { return rezzexp; } + inline void Lock() { is_locked = true; } + inline void UnLock() { is_locked = false; } + inline bool IsLocked() { return is_locked; } + inline void ResetLooter() { being_looted_by = 0xFFFFFFFF; } + inline bool IsBeingLooted() { return (being_looted_by != 0xFFFFFFFF); } - // 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 + /* Mob */ + void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho); + bool Summon(Client* client, bool spell, bool CheckDistance); + void Spawn(); + + char corpse_name[64]; + uint32 GetEquipment(uint8 material_slot) const; + uint32 GetEquipmentColor(uint8 material_slot) const; + inline int GetRezExp() { return rez_experience; } protected: std::list MoveItemToCorpse(Client *client, ItemInst *item, int16 equipslot); private: - bool p_PlayerCorpse; bool pIsChanged; - bool pLocked; - int32 pkitem; - uint32 dbid; - uint32 charid; - ItemList itemlist; - uint32 copper; + bool is_player_corpse; /* Determines if Player Corpse or not */ + bool is_corpse_changed; /* Determines if corpse has changed or not */ + bool is_locked; /* Determines if corpse is locked */ + int32 player_kill_item; /* Determines if Player Kill Item */ + uint32 corpse_db_id; /* Corpse Database ID (Player Corpse) */ + uint32 char_id; /* Character ID */ + ItemList itemlist; /* Internal Item list used for corpses */ + uint32 copper; uint32 silver; uint32 gold; uint32 platinum; - bool p_depop; - uint32 BeingLootedBy; - uint32 rezzexp; + bool player_corpse_depop; /* Sets up Corpse::Process to depop the player corpse */ + uint32 being_looted_by; /* Determines what the corpse is being looted by internally for logic */ + uint32 rez_experience; /* Amount of experience that the corpse would rez for */ bool rez; - bool can_rez; + bool can_corpse_be_rezzed; /* Bool declaring whether or not a corpse can be rezzed */ bool become_npc; - int looters[MAX_LOOTERS]; // People allowed to loot the corpse, character id - Timer corpse_decay_timer; - Timer corpse_res_timer; - Timer corpse_delay_timer; + int allowed_looters[MAX_LOOTERS]; /* People allowed to loot the corpse, character id */ + Timer corpse_decay_timer; /* The amount of time in millseconds in which a corpse will take to decay (Depop/Poof) */ + Timer corpse_rez_timer; /* The amount of time in millseconds in which a corpse can be rezzed */ + Timer corpse_delay_timer; Timer corpse_graveyard_timer; - Timer loot_cooldown_timer; + Timer loot_cooldown_timer; /* Delay between loot actions on the corpse entity */ Color_Struct item_tint[9]; + }; #endif diff --git a/zone/doors.cpp b/zone/doors.cpp index ab0b54c8f..4af1d3eae 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -21,11 +21,11 @@ #include #include "masterentity.h" #include "worldserver.h" -#include "StringIDs.h" +#include "string_ids.h" #include "zonedb.h" #include "../common/packet_functions.h" #include "../common/packet_dump.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "guild_mgr.h" #define OPEN_DOOR 0x02 @@ -50,7 +50,7 @@ Doors::Doors(const Door* door) incline = door->incline; opentype = door->opentype; guild_id = door->guild_id; - lockpick = door->lockpick; + lockpick = door->lock_pick; keyitem = door->keyitem; nokeyring = door->nokeyring; trigger_door = door->trigger_door; @@ -253,8 +253,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) strcpy(tmpmsg, "Door is locked by an unknown guild"); } sender->Message(4, tmpmsg); - // safe_delete(outapp); - // /\ possible missing line..all other 'fail' returns seem to have it + safe_delete(outapp); return; } // a key is required or the door is locked but can be picked or both @@ -569,157 +568,137 @@ void Doors::DumpDoor(){ } int32 ZoneDatabase::GetDoorsCount(uint32* oMaxID, const char *zone_name, int16 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - sprintf(query, "SELECT MAX(id), count(*) FROM doors WHERE zone='%s' AND (version=%u OR version=-1)", zone_name, version); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row != nullptr && row[1] != 0) { - int32 ret = atoi(row[1]); - if (oMaxID) { - if (row[0]) - *oMaxID = atoi(row[0]); - else - *oMaxID = 0; - } - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetDoorsCount query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT MAX(id), count(*) FROM doors " + "WHERE zone = '%s' AND (version = %u OR version = -1)", + zone_name, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetDoorsCount query '" << query << "' " << results.ErrorMessage() << std::endl; return -1; - } + } + + if (results.RowCount() != 1) + return -1; + + auto row = results.begin(); + + if (!oMaxID) + return atoi(row[1]); + + if (row[0]) + *oMaxID = atoi(row[0]); + else + *oMaxID = 0; + + return atoi(row[1]); - return -1; } int32 ZoneDatabase::GetDoorsCountPlusOne(const char *zone_name, int16 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 oMaxID = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - sprintf(query, "SELECT MAX(id) FROM doors WHERE zone='%s' AND version=%u", zone_name, version); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row != nullptr && row[1] != 0) { - if (row[0]) - oMaxID = atoi(row[0]) + 1; - else - oMaxID = 0; - mysql_free_result(result); - return oMaxID; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT MAX(id) FROM doors " + "WHERE zone = '%s' AND version = %u", zone_name, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << results.ErrorMessage() << std::endl; return -1; - } + } - return -1; + if (results.RowCount() != 1) + return -1; + + auto row = results.begin(); + + if (!row[0]) + return 0; + + return atoi(row[0]) + 1; } int32 ZoneDatabase::GetDoorsDBCountPlusOne(const char *zone_name, int16 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 oMaxID = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - sprintf(query, "SELECT MAX(doorid) FROM doors WHERE zone='%s' AND (version=%u OR version=-1)", zone_name, version); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row != nullptr && row[1] != 0) { - if (row[0]) - oMaxID = atoi(row[0]) + 1; - else - oMaxID = 0; - mysql_free_result(result); - return oMaxID; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT MAX(doorid) FROM doors " + "WHERE zone = '%s' AND (version = %u OR version = -1)", + zone_name, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetDoorsCountPlusOne query '" << query << "' " << results.ErrorMessage() << std::endl; return -1; } - return -1; + if (results.RowCount() != 1) + return -1; + + auto row = results.begin(); + + if (!row[0]) + return 0; + + return atoi(row[0]) + 1; } bool ZoneDatabase::LoadDoors(int32 iDoorCount, Door *into, const char *zone_name, int16 version) { LogFile->write(EQEMuLog::Status, "Loading Doors from database..."); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + // Door tmpDoor; - MakeAnyLenString(&query, "SELECT id,doorid,zone,name,pos_x,pos_y,pos_z,heading," - "opentype,guild,lockpick,keyitem,nokeyring,triggerdoor,triggertype,dest_zone,dest_instance,dest_x," - "dest_y,dest_z,dest_heading,door_param,invert_state,incline,size,is_ldon_door,client_version_mask " - "FROM doors WHERE zone='%s' AND (version=%u OR version=-1) ORDER BY doorid asc", zone_name, version); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - int32 r; - for(r = 0; (row = mysql_fetch_row(result)); r++) { - if(r >= iDoorCount) { - std::cerr << "Error, Door Count of " << iDoorCount << " exceeded." << std::endl; - break; - } - memset(&into[r], 0, sizeof(Door)); - into[r].db_id = atoi(row[0]); - into[r].door_id = atoi(row[1]); - strn0cpy(into[r].zone_name,row[2],32); - strn0cpy(into[r].door_name,row[3],32); - into[r].pos_x = (float)atof(row[4]); - into[r].pos_y = (float)atof(row[5]); - into[r].pos_z = (float)atof(row[6]); - into[r].heading = (float)atof(row[7]); - into[r].opentype = atoi(row[8]); - into[r].guild_id = atoi(row[9]); - into[r].lockpick = atoi(row[10]); - into[r].keyitem = atoi(row[11]); - into[r].nokeyring = atoi(row[12]); - into[r].trigger_door = atoi(row[13]); - into[r].trigger_type = atoi(row[14]); - strn0cpy(into[r].dest_zone, row[15], 32); - into[r].dest_instance_id = atoi(row[16]); - into[r].dest_x = (float) atof(row[17]); - into[r].dest_y = (float) atof(row[18]); - into[r].dest_z = (float) atof(row[19]); - into[r].dest_heading = (float) atof(row[20]); - into[r].door_param=atoi(row[21]); - into[r].invert_state=atoi(row[22]); - into[r].incline=atoi(row[23]); - into[r].size=atoi(row[24]); - into[r].is_ldon_door=atoi(row[25]); - into[r].client_version_mask = (uint32)strtoul(row[26], nullptr, 10); - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in DBLoadDoors query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT id, doorid, zone, name, pos_x, pos_y, pos_z, heading, " + "opentype, guild, lockpick, keyitem, nokeyring, triggerdoor, triggertype, " + "dest_zone, dest_instance, dest_x, dest_y, dest_z, dest_heading, " + "door_param, invert_state, incline, size, is_ldon_door, client_version_mask " + "FROM doors WHERE zone = '%s' AND (version = %u OR version = -1) " + "ORDER BY doorid asc", zone_name, version); + auto results = QueryDatabase(query); + if (!results.Success()){ + std::cerr << "Error in DBLoadDoors query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } + + int32 rowIndex = 0; + for(auto row = results.begin(); row != results.end(); ++row, ++rowIndex) { + if(rowIndex >= iDoorCount) { + std::cerr << "Error, Door Count of " << iDoorCount << " exceeded." << std::endl; + break; + } + + memset(&into[rowIndex], 0, sizeof(Door)); + + into[rowIndex].db_id = atoi(row[0]); + into[rowIndex].door_id = atoi(row[1]); + + strn0cpy(into[rowIndex].zone_name,row[2],32); + strn0cpy(into[rowIndex].door_name,row[3],32); + + into[rowIndex].pos_x = (float)atof(row[4]); + into[rowIndex].pos_y = (float)atof(row[5]); + into[rowIndex].pos_z = (float)atof(row[6]); + into[rowIndex].heading = (float)atof(row[7]); + into[rowIndex].opentype = atoi(row[8]); + into[rowIndex].guild_id = atoi(row[9]); + into[rowIndex].lock_pick = atoi(row[10]); + into[rowIndex].keyitem = atoi(row[11]); + into[rowIndex].nokeyring = atoi(row[12]); + into[rowIndex].trigger_door = atoi(row[13]); + into[rowIndex].trigger_type = atoi(row[14]); + + strn0cpy(into[rowIndex].dest_zone, row[15], 32); + + into[rowIndex].dest_instance_id = atoi(row[16]); + into[rowIndex].dest_x = (float) atof(row[17]); + into[rowIndex].dest_y = (float) atof(row[18]); + into[rowIndex].dest_z = (float) atof(row[19]); + into[rowIndex].dest_heading = (float) atof(row[20]); + into[rowIndex].door_param=atoi(row[21]); + into[rowIndex].invert_state=atoi(row[22]); + into[rowIndex].incline=atoi(row[23]); + into[rowIndex].size=atoi(row[24]); + into[rowIndex].is_ldon_door=atoi(row[25]); + into[rowIndex].client_version_mask = (uint32)strtoul(row[26], nullptr, 10); + } + return true; } diff --git a/zone/effects.cpp b/zone/effects.cpp index 1b1eeb2ea..0bface8b7 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -24,11 +24,11 @@ #include "../common/packet_functions.h" #include "petitions.h" #include "../common/serverinfo.h" -#include "../common/ZoneNumbers.h" +#include "../common/zone_numbers.h" #include "../common/moremath.h" #include "../common/guilds.h" -#include "StringIDs.h" -#include "NpcAI.h" +#include "string_ids.h" +#include "npc_ai.h" float Client::GetActSpellRange(uint16 spell_id, float range, bool IsBard) { @@ -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; @@ -260,7 +260,7 @@ int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_s if (total_cast_time > 0 && total_cast_time <= 2500) extra_spell_amt = extra_spell_amt*25/100; else if (total_cast_time > 2500 && total_cast_time < 7000) - extra_spell_amt = extra_spell_amt*(0.167*((total_cast_time - 1000)/1000)); + extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000; else extra_spell_amt = extra_spell_amt * total_cast_time / 7000; @@ -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); @@ -408,11 +408,11 @@ int32 Client::GetActSpellCost(uint16 spell_id, int32 cost) break; } - bonus += 0.05 * GetAA(aaAdvancedSpellCastingMastery); + bonus += 0.05f * GetAA(aaAdvancedSpellCastingMastery); if(SuccessChance <= (SpecializeSkill * 0.3 * bonus)) { - PercentManaReduction = 1 + 0.05 * SpecializeSkill; + PercentManaReduction = 1 + 0.05f * SpecializeSkill; switch(GetAA(aaSpellCastingMastery)) { case 1: @@ -451,7 +451,7 @@ int32 Client::GetActSpellCost(uint16 spell_id, int32 cost) // Gift of Mana - reduces spell cost to 1 mana if(focus_redux >= 100) { - uint32 buff_max = GetMaxTotalSlots(); + int buff_max = GetMaxTotalSlots(); for (int buffSlot = 0; buffSlot < buff_max; buffSlot++) { if (buffs[buffSlot].spellid == 0 || buffs[buffSlot].spellid >= SPDAT_RECORDS) continue; @@ -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); } @@ -686,19 +683,20 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { { uint32 reduced_recast = spell.recast_time / 1000; reduced_recast -= CastToClient()->GetFocusEffect(focusReduceRecastTime, spell_id); - if(reduced_recast < 0) + if(reduced_recast <= 0){ reduced_recast = 0; - - CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast); - if(spells[spell_id].EndurTimerIndex < MAX_DISCIPLINE_TIMERS) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_DisciplineTimer, sizeof(DisciplineTimer_Struct)); - DisciplineTimer_Struct *dts = (DisciplineTimer_Struct *)outapp->pBuffer; - dts->TimerID = spells[spell_id].EndurTimerIndex; - dts->Duration = reduced_recast; - QueuePacket(outapp); - safe_delete(outapp); + if (GetPTimers().Enabled((uint32)DiscTimer)) + GetPTimers().Clear(&database, (uint32)DiscTimer); } + + if (reduced_recast > 0) + CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast); + else{ + CastSpell(spell_id, target, DISCIPLINE_SPELL_SLOT); + return true; + } + + SendDisciplineTimer(spells[spell_id].EndurTimerIndex, reduced_recast); } else { @@ -707,6 +705,19 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) { return(true); } +void Client::SendDisciplineTimer(uint32 timer_id, uint32 duration) +{ + if (timer_id < MAX_DISCIPLINE_TIMERS) + { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_DisciplineTimer, sizeof(DisciplineTimer_Struct)); + DisciplineTimer_Struct *dts = (DisciplineTimer_Struct *)outapp->pBuffer; + dts->TimerID = timer_id; + dts->Duration = duration; + QueuePacket(outapp); + safe_delete(outapp); + } +} + void EntityList::AETaunt(Client* taunter, float range) { if (range == 0) @@ -745,7 +756,11 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ bool bad = IsDetrimentalSpell(spell_id); bool isnpc = caster->IsNPC(); - const int MAX_TARGETS_ALLOWED = 4; + int MAX_TARGETS_ALLOWED = 4; + + if (spells[spell_id].aemaxtargets) + MAX_TARGETS_ALLOWED = spells[spell_id].aemaxtargets; + int iCounter = 0; for (auto it = mob_list.begin(); it != mob_list.end(); ++it) { @@ -757,8 +772,20 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ continue; if (curmob == caster && !affect_caster) //watch for caster too continue; - - dist_targ = center->DistNoRoot(*curmob); + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && curmob->IsPetOwnerClient()) + continue; + if (spells[spell_id].targettype == ST_AreaClientOnly && !curmob->IsClient()) + continue; + if (spells[spell_id].targettype == ST_AreaNPCOnly && !curmob->IsNPC()) + continue; + + if (spells[spell_id].targettype == ST_Ring) { + dist_targ = curmob->DistNoRoot(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ()); + } + else if (center) { + dist_targ = center->DistNoRoot(*curmob); + } + if (dist_targ > dist2) //make sure they are in range continue; if (dist_targ < min_range2) //make sure they are in range @@ -780,7 +807,9 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ if (bad) { if (!caster->IsAttackAllowed(curmob, true)) continue; - if (!center->CheckLosFN(curmob)) + if (center && !center->CheckLosFN(curmob)) + continue; + if (!center && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize())) continue; } else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies... // This does not check faction for beneficial AE buffs..only agro and attackable. @@ -800,10 +829,13 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_ caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } } else { - caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); + if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets) + caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); + if (!spells[spell_id].aemaxtargets) + caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust); } - if (!isnpc) //npcs are not target limited... + if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)... iCounter++; } } diff --git a/zone/embparser.cpp b/zone/embparser.cpp index c869104f8..c7a67c388 100644 --- a/zone/embparser.cpp +++ b/zone/embparser.cpp @@ -20,13 +20,13 @@ #include "../common/debug.h" #include "../common/seperator.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "../common/features.h" #include "masterentity.h" #include "embparser.h" #include "questmgr.h" -#include "QGlobals.h" +#include "qglobals.h" #include "zone.h" #include #include @@ -154,7 +154,7 @@ void PerlembParser::ReloadQuests() { } int PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, ItemInst* iteminst, Mob* mob, - uint32 extradata, bool global, std::vector *extra_pointers) + uint32 extradata, bool global, std::vector *extra_pointers) { if(!perl) return 0; @@ -211,32 +211,32 @@ int PerlembParser::EventCommon(QuestEventID event, uint32 objid, const char * da } int PerlembParser::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, false, extra_pointers); } int PerlembParser::EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { return EventCommon(evt, npc->GetNPCTypeID(), data.c_str(), npc, nullptr, init, extra_data, true, extra_pointers); } int PerlembParser::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, false, extra_pointers); } int PerlembParser::EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { return EventCommon(evt, 0, data.c_str(), nullptr, nullptr, client, extra_data, true, extra_pointers); } int PerlembParser::EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { return EventCommon(evt, item->GetID(), nullptr, nullptr, item, client, extra_data, false, extra_pointers); } int PerlembParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { return EventCommon(evt, 0, itoa(spell_id), npc, nullptr, client, extra_data, false, extra_pointers); } @@ -1114,7 +1114,7 @@ void PerlembParser::ExportItemVariables(std::string &package_name, Mob *mob) { #undef HASITEM_ISNULLITEM void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID event, uint32 objid, const char * data, - NPC* npcmob, ItemInst* iteminst, Mob* mob, uint32 extradata, std::vector *extra_pointers) + NPC* npcmob, ItemInst* iteminst, Mob* mob, uint32 extradata, std::vector *extra_pointers) { switch (event) { case EVENT_SAY: { @@ -1130,8 +1130,10 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID case EVENT_TRADE: { if(extra_pointers) { - for(size_t i = 0; i < extra_pointers->size(); ++i) { - ItemInst *inst = reinterpret_cast(extra_pointers->at(i)); + size_t sz = extra_pointers->size(); + for(size_t i = 0; i < sz; ++i) { + ItemInst *inst = EQEmu::any_cast(extra_pointers->at(i)); + std::string var_name = "item"; var_name += std::to_string(static_cast(i + 1)); diff --git a/zone/embparser.h b/zone/embparser.h index ffca78075..1357ae20b 100644 --- a/zone/embparser.h +++ b/zone/embparser.h @@ -20,8 +20,8 @@ #define EQMEU_EMBPARSER_H #ifdef EMBPERL -#include "QuestParserCollection.h" -#include "QuestInterface.h" +#include "quest_parser_collection.h" +#include "quest_interface.h" #include #include #include @@ -45,17 +45,17 @@ public: ~PerlembParser(); virtual int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual bool HasQuestSub(uint32 npcid, QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt); @@ -86,7 +86,7 @@ private: void ExportVarComplex(const char *pkgprefix, const char *varname, const char *value); int EventCommon(QuestEventID event, uint32 objid, const char * data, NPC* npcmob, ItemInst* iteminst, Mob* mob, - uint32 extradata, bool global, std::vector *extra_pointers); + uint32 extradata, bool global, std::vector *extra_pointers); int SendCommands(const char *pkgprefix, const char *event, uint32 npcid, Mob* other, Mob* mob, ItemInst *iteminst); void MapFunctions(); @@ -103,7 +103,7 @@ private: void ExportZoneVariables(std::string &package_name); void ExportItemVariables(std::string &package_name, Mob *mob); void ExportEventVariables(std::string &package_name, QuestEventID event, uint32 objid, const char * data, - NPC* npcmob, ItemInst* iteminst, Mob* mob, uint32 extradata, std::vector *extra_pointers); + NPC* npcmob, ItemInst* iteminst, Mob* mob, uint32 extradata, std::vector *extra_pointers); std::map npc_quest_status_; PerlQuestStatus global_npc_quest_status_; diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index 91b6d1836..0e8b10a8e 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -22,14 +22,16 @@ #ifdef EMBPERL_XS #include "../common/debug.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "embparser.h" #include "questmgr.h" #include "embxs.h" #include "entity.h" #include "zone.h" +#include "queryserv.h" extern Zone* zone; +extern QueryServ* QServ; /* @@ -2857,14 +2859,54 @@ XS(XS__GetInstanceID) { XSRETURN_UV(id); } +XS(XS__GetCharactersInInstance); +XS(XS__GetCharactersInInstance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: GetCharactersInInstance(instance_id)"); + dXSTARG; + + Const_char * RETVAL; + uint16 instance_id = (int)SvUV(ST(0)); + std::list charid_list; + std::string charid_string; + + database.GetCharactersInInstance(instance_id, charid_list); + + if (charid_list.size() > 0) + { + charid_string = itoa(charid_list.size()); + charid_string += " player(s) in instance: "; + auto iter = charid_list.begin(); + while (iter != charid_list.end()) + { + char char_name[64]; + database.GetCharName(*iter, char_name); + charid_string += char_name; + charid_string += "("; + charid_string += itoa(*iter); + charid_string += ")"; + ++iter; + if (iter != charid_list.end()) + charid_string += ", "; + } + RETVAL = charid_string.c_str(); + } + else + RETVAL = "No players in that instance."; + + sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; + XSRETURN(1); +} + XS(XS__AssignToInstance); XS(XS__AssignToInstance) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: AssignToInstance(id)"); + Perl_croak(aTHX_ "Usage: AssignToInstance(instance_id)"); - uint16 id = (int)SvUV(ST(0)); - quest_manager.AssignToInstance(id); + uint16 instance_id = (int)SvUV(ST(0)); + quest_manager.AssignToInstance(instance_id); XSRETURN_EMPTY; } @@ -2873,10 +2915,10 @@ XS(XS__AssignGroupToInstance); XS(XS__AssignGroupToInstance) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: AssignGroupToInstance(id)"); + Perl_croak(aTHX_ "Usage: AssignGroupToInstance(instance_id)"); - uint16 id = (int)SvUV(ST(0)); - quest_manager.AssignGroupToInstance(id); + uint16 instance_id = (int)SvUV(ST(0)); + quest_manager.AssignGroupToInstance(instance_id); XSRETURN_EMPTY; } @@ -2885,10 +2927,34 @@ XS(XS__AssignRaidToInstance); XS(XS__AssignRaidToInstance) { dXSARGS; if (items != 1) - Perl_croak(aTHX_ "Usage: AssignRaidToInstance(id)"); + Perl_croak(aTHX_ "Usage: AssignRaidToInstance(instance_id)"); - uint16 id = (int)SvUV(ST(0)); - quest_manager.AssignRaidToInstance(id); + uint16 instance_id = (int)SvUV(ST(0)); + quest_manager.AssignRaidToInstance(instance_id); + + XSRETURN_EMPTY; +} + +XS(XS__RemoveFromInstance); +XS(XS__RemoveFromInstance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: RemoveFromInstance(instance_id)"); + + uint16 instance_id = (int)SvUV(ST(0)); + quest_manager.RemoveFromInstance(instance_id); + + XSRETURN_EMPTY; +} + +XS(XS__RemoveAllFromInstance); +XS(XS__RemoveAllFromInstance) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: RemoveAllFromInstance(instance_id)"); + + uint16 instance_id = (int)SvUV(ST(0)); + quest_manager.RemoveAllFromInstance(instance_id); XSRETURN_EMPTY; } @@ -3370,6 +3436,71 @@ XS(XS__clear_npctype_cache) XSRETURN_EMPTY; } +XS(XS__qs_send_query); +XS(XS__qs_send_query) +{ + dXSARGS; + if (items != 1){ + Perl_croak(aTHX_ "Usage: qs_send_query(query)"); + } + else{ + // char *Query = (char *)SvPV_nolen(ST(0)); + std::string Query = (std::string)SvPV_nolen(ST(0)); + QServ->SendQuery(Query); + } + XSRETURN_EMPTY; +} + +XS(XS__qs_player_event); +XS(XS__qs_player_event) +{ + dXSARGS; + if (items != 2){ + Perl_croak(aTHX_ "Usage: qs_player_event(char_id, event_desc)"); + } + else{ + int char_id = (int)SvIV(ST(0)); + std::string event_desc = (std::string)SvPV_nolen(ST(1)); + QServ->PlayerLogEvent(Player_Log_Quest, char_id, event_desc); + } + 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 @@ -3561,9 +3692,12 @@ EXTERN_C XS(boot_quest) newXS(strcpy(buf, "CreateInstance"), XS__CreateInstance, file); newXS(strcpy(buf, "DestroyInstance"), XS__DestroyInstance, file); newXS(strcpy(buf, "GetInstanceID"), XS__GetInstanceID, file); + newXS(strcpy(buf, "GetCharactersInInstance"), XS__GetCharactersInInstance, file); newXS(strcpy(buf, "AssignToInstance"), XS__AssignToInstance, file); newXS(strcpy(buf, "AssignGroupToInstance"), XS__AssignGroupToInstance, file); newXS(strcpy(buf, "AssignRaidToInstance"), XS__AssignRaidToInstance, file); + newXS(strcpy(buf, "RemoveFromInstance"), XS__RemoveFromInstance, file); + newXS(strcpy(buf, "RemoveAllFromInstance"), XS__RemoveAllFromInstance, file); newXS(strcpy(buf, "MovePCInstance"), XS__MovePCInstance, file); newXS(strcpy(buf, "FlagInstanceByGroupLeader"), XS__FlagInstanceByGroupLeader, file); newXS(strcpy(buf, "FlagInstanceByRaidLeader"), XS__FlagInstanceByRaidLeader, file); @@ -3591,6 +3725,10 @@ EXTERN_C XS(boot_quest) newXS(strcpy(buf, "enablerecipe"), XS__enablerecipe, file); 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, "crosszonesetentityvariablebynpctypeid"), XS__crosszonesetentityvariablebynpctypeid, file); + newXS(strcpy(buf, "crosszonesignalnpcbynpctypeid"), XS__crosszonesignalnpcbynpctypeid, file); XSRETURN_YES; } diff --git a/zone/entity.cpp b/zone/entity.cpp index 59d4bea80..bf6095a7c 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -31,22 +30,18 @@ #include "../common/unix.h" #endif -#include "net.h" -#include "masterentity.h" -#include "worldserver.h" -#include "../common/guilds.h" -#include "../common/packet_dump.h" -#include "../common/packet_functions.h" -#include "petitions.h" -#include "../common/spdat.h" #include "../common/features.h" -#include "StringIDs.h" -#include "../common/dbasync.h" +#include "../common/guilds.h" +#include "../common/spdat.h" #include "guild_mgr.h" +#include "net.h" +#include "petitions.h" +#include "quest_parser_collection.h" #include "raids.h" -#include "QuestParserCollection.h" #include "remote_call.h" #include "remote_call_subscribe.h" +#include "string_ids.h" +#include "worldserver.h" #ifdef _WINDOWS #define snprintf _snprintf @@ -60,7 +55,6 @@ extern WorldServer worldserver; extern NetConnection net; extern uint32 numclients; extern PetitionList petition_list; -extern DBAsync *dbasync; extern char errorname[32]; extern uint16 adverrornum; @@ -68,12 +62,12 @@ extern uint16 adverrornum; Entity::Entity() { id = 0; - pDBAsyncWorkID = 0; + spawn_timestamp = time(nullptr); } Entity::~Entity() { - dbasync->CancelWork(pDBAsyncWorkID); + } Client *Entity::CastToClient() @@ -496,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(); @@ -520,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); } } } @@ -625,6 +631,7 @@ void EntityList::AddCorpse(Corpse *corpse, uint32 in_id) void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) { npc->SetID(GetFreeID()); + npc->SetMerchantProbability((uint8) MakeRandomInt(0, 99)); parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0); /* Web Interface: NPC Spawn (Pop) */ @@ -668,10 +675,12 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue) void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue) { - if (merc) { + if (merc) + { merc->SetID(GetFreeID()); - if (SendSpawnPacket) { + if (SendSpawnPacket) + { if (dontqueue) { // Send immediately EQApplicationPacket *outapp = new EQApplicationPacket(); @@ -683,12 +692,10 @@ void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue) // Queue the packet NewSpawn_Struct *ns = new NewSpawn_Struct; memset(ns, 0, sizeof(NewSpawn_Struct)); - merc->FillSpawnStruct(ns, merc); + merc->FillSpawnStruct(ns, 0); AddToSpawnQueue(merc->GetID(), &ns); safe_delete(ns); } - - //parse->EventMERC(EVENT_SPAWN, merc, nullptr, "", 0); } merc_list.insert(std::pair(merc->GetID(), merc)); @@ -1358,7 +1365,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()) { @@ -1372,8 +1379,7 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap Mob *TargetsTarget = nullptr; - if (Target) - TargetsTarget = Target->GetTarget(); + TargetsTarget = Target->GetTarget(); bool Send = false; @@ -1385,11 +1391,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)) @@ -1492,7 +1517,7 @@ void EntityList::QueueClientsStatus(Mob *sender, const EQApplicationPacket *app, void EntityList::DuelMessage(Mob *winner, Mob *loser, bool flee) { if (winner->GetLevelCon(winner->GetLevel(), loser->GetLevel()) > 2) { - std::vector args; + std::vector args; args.push_back(winner); args.push_back(loser); @@ -1594,7 +1619,7 @@ Corpse *EntityList::GetCorpseByDBID(uint32 dbid) { auto it = corpse_list.begin(); while (it != corpse_list.end()) { - if (it->second->GetDBID() == dbid) + if (it->second->GetCorpseDBID() == dbid) return it->second; ++it; } @@ -1614,7 +1639,7 @@ Corpse *EntityList::GetCorpseByName(const char *name) Spawn2 *EntityList::GetSpawnByID(uint32 id) { - if (!zone) + if (!zone || !zone->IsLoaded()) return nullptr; LinkedListIterator iterator(zone->spawn2_list); @@ -1648,7 +1673,7 @@ void EntityList::RemoveCorpseByDBID(uint32 dbid) { auto it = corpse_list.begin(); while (it != corpse_list.end()) { - if (it->second->GetDBID() == dbid) { + if (it->second->GetCorpseDBID() == dbid) { safe_delete(it->second); free_ids.push(it->first); it = corpse_list.erase(it); @@ -1665,9 +1690,9 @@ int EntityList::RezzAllCorpsesByCharID(uint32 charid) auto it = corpse_list.begin(); while (it != corpse_list.end()) { if (it->second->GetCharID() == charid) { - RezzExp += it->second->GetRezzExp(); - it->second->Rezzed(true); - it->second->CompleteRezz(); + RezzExp += it->second->GetRezExp(); + it->second->IsRezzed(true); + it->second->CompleteResurrection(); } ++it; } @@ -2654,7 +2679,7 @@ int32 EntityList::DeleteNPCCorpses() auto it = corpse_list.begin(); while (it != corpse_list.end()) { if (it->second->IsNPCCorpse()) { - it->second->Depop(); + it->second->DepopNPCCorpse(); x++; } ++it; @@ -2874,7 +2899,7 @@ void EntityList::ClearFeignAggro(Mob *targ) } if (targ->IsClient()) { - std::vector args; + std::vector args; args.push_back(it->second); int i = parse->EventPlayer(EVENT_FEIGN_DEATH, targ->CastToClient(), "", 0, &args); if (i != 0) { @@ -2934,8 +2959,14 @@ void EntityList::SignalMobsByNPCID(uint32 snpc, int signal_id) } } +bool tracking_compare(const std::pair &a, const std::pair &b) +{ + return a.first->GetSpawnTimeStamp() > b.first->GetSpawnTimeStamp(); +} + bool EntityList::MakeTrackPacket(Client *client) { + std::list > tracking_list; uint32 distance = 0; float MobDistance; @@ -2950,60 +2981,43 @@ bool EntityList::MakeTrackPacket(Client *client) if (distance < 300) distance = 300; - uint32 spe= 0; - bool ret = false; - - spe = mob_list.size() + 50; - - uchar *buffer1 = new uchar[sizeof(Track_Struct)]; - Track_Struct *track_ent = (Track_Struct*) buffer1; - - uchar *buffer2 = new uchar[sizeof(Track_Struct)*spe]; - Tracking_Struct *track_array = (Tracking_Struct*) buffer2; - memset(track_array, 0, sizeof(Track_Struct)*spe); - - uint32 array_counter = 0; - Group *g = client->GetGroup(); - auto it = mob_list.begin(); - while (it != mob_list.end()) { - if (it->second && ((MobDistance = it->second->DistNoZ(*client)) <= distance)) { - if ((it->second != client) && it->second->IsTrackable()) { - memset(track_ent, 0, sizeof(Track_Struct)); - Mob *cur_entity = it->second; - track_ent->entityid = cur_entity->GetID(); - track_ent->distance = MobDistance; - track_ent->level = cur_entity->GetLevel(); - track_ent->NPC = !cur_entity->IsClient(); - if (g && cur_entity->IsClient() && g->IsGroupMember(cur_entity->CastToMob())) - track_ent->GroupMember = 1; - else - track_ent->GroupMember = 0; - strn0cpy(track_ent->name, cur_entity->GetName(), sizeof(track_ent->name)); - memcpy(&track_array->Entrys[array_counter], track_ent, sizeof(Track_Struct)); - array_counter++; - } - } + for (auto it = mob_list.cbegin(); it != mob_list.cend(); ++it) { + if (!it->second || it->second == client || !it->second->IsTrackable() || + it->second->IsInvisible(client)) + continue; - ++it; + MobDistance = it->second->DistNoZ(*client); + if (MobDistance > distance) + continue; + + tracking_list.push_back(std::make_pair(it->second, MobDistance)); } - if (array_counter <= spe) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Track,sizeof(Track_Struct)*(array_counter)); - memcpy(outapp->pBuffer, track_array,sizeof(Track_Struct)*(array_counter)); - outapp->priority = 6; - client->QueuePacket(outapp); - safe_delete(outapp); - ret = true; - } else { - LogFile->write(EQEMuLog::Status, "ERROR: Unable to transmit a Tracking_Struct packet. Mobs in zone = %i. Mobs in packet = %i", array_counter, spe); + tracking_list.sort(tracking_compare); + EQApplicationPacket *outapp = new EQApplicationPacket(OP_Track, sizeof(Track_Struct) * tracking_list.size()); + Tracking_Struct *outtrack = (Tracking_Struct *)outapp->pBuffer; + outapp->priority = 6; + + int index = 0; + for (auto it = tracking_list.cbegin(); it != tracking_list.cend(); ++it, ++index) { + Mob *cur_entity = it->first; + outtrack->Entrys[index].entityid = cur_entity->GetID(); + outtrack->Entrys[index].distance = it->second; + outtrack->Entrys[index].level = cur_entity->GetLevel(); + outtrack->Entrys[index].NPC = !cur_entity->IsClient(); + if (g && cur_entity->IsClient() && g->IsGroupMember(cur_entity->CastToMob())) + outtrack->Entrys[index].GroupMember = 1; + else + outtrack->Entrys[index].GroupMember = 0; + strn0cpy(outtrack->Entrys[index].name, cur_entity->GetName(), sizeof(outtrack->Entrys[index].name)); } - safe_delete_array(buffer1); - safe_delete_array(buffer2); + client->QueuePacket(outapp); + safe_delete(outapp); - return ret; + return true; } void EntityList::MessageGroup(Mob *sender, bool skipclose, uint32 type, const char *message, ...) @@ -3269,9 +3283,10 @@ void EntityList::ProcessMove(Client *c, float x, float y, float z) for (auto iter = events.begin(); iter != events.end(); ++iter) { quest_proximity_event& evt = (*iter); if (evt.npc) { - parse->EventNPC(evt.event_id, evt.npc, evt.client, "", 0); + std::vector args; + parse->EventNPC(evt.event_id, evt.npc, evt.client, "", 0, &args); } else { - std::vector args; + std::vector args; args.push_back(&evt.area_id); args.push_back(&evt.area_type); parse->EventPlayer(evt.event_id, evt.client, "", 0, &args); @@ -3325,7 +3340,7 @@ void EntityList::ProcessMove(NPC *n, float x, float y, float z) for (auto iter = events.begin(); iter != events.end(); ++iter) { quest_proximity_event& evt = (*iter); - std::vector args; + std::vector args; args.push_back(&evt.area_id); args.push_back(&evt.area_type); parse->EventNPC(evt.event_id, evt.npc, evt.client, "", 0, &args); @@ -3632,6 +3647,43 @@ void EntityList::DestroyTempPets(Mob *owner) } } +int16 EntityList::CountTempPets(Mob *owner) +{ + int16 count = 0; + auto it = npc_list.begin(); + while (it != npc_list.end()) { + NPC* n = it->second; + if (n->GetSwarmInfo()) { + if (n->GetSwarmInfo()->owner_id == owner->GetID()) { + count++; + } + } + ++it; + } + + owner->SetTempPetCount(count); + + return count; +} + +void EntityList::AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy) +{ + if (!other || !owner) + return; + + auto it = npc_list.begin(); + while (it != npc_list.end()) { + NPC* n = it->second; + if (n->GetSwarmInfo()) { + if (n->GetSwarmInfo()->owner_id == owner->GetID()) { + if (!n->GetSpecialAbility(IMMUNE_AGGRO)) + n->hate_list.Add(other, 0, 0, bFrenzy); + } + } + ++it; + } +} + bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk) { @@ -4137,15 +4189,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; } } @@ -4197,6 +4244,20 @@ void EntityList::SignalAllClients(uint32 data) } } +uint16 EntityList::GetClientCount(){ + uint16 ClientCount = 0; + std::list client_list; + entity_list.GetClientList(client_list); + std::list::iterator iter = client_list.begin(); + while (iter != client_list.end()) { + Client *entry = (*iter); + entry->GetCleanName(); + ClientCount++; + iter++; + } + return ClientCount; +} + void EntityList::GetMobList(std::list &m_list) { m_list.clear(); @@ -4602,42 +4663,49 @@ Client *EntityList::FindCorpseDragger(uint16 CorpseID) return nullptr; } -Mob *EntityList::GetTargetForVirus(Mob *spreader) +Mob *EntityList::GetTargetForVirus(Mob *spreader, int range) { int max_spread_range = RuleI(Spells, VirusSpreadDistance); + if (range) + max_spread_range = range; + std::vector TargetsInRange; auto it = mob_list.begin(); while (it != mob_list.end()) { + Mob *cur = it->second; // Make sure the target is in range, has los and is not the mob doing the spreading - if ((it->second->GetID() != spreader->GetID()) && - (it->second->CalculateDistance(spreader->GetX(), spreader->GetY(), + if ((cur->GetID() != spreader->GetID()) && + (cur->CalculateDistance(spreader->GetX(), spreader->GetY(), spreader->GetZ()) <= max_spread_range) && - (spreader->CheckLosFN(it->second))) { + (spreader->CheckLosFN(cur))) { // If the spreader is an npc it can only spread to other npc controlled mobs - if (spreader->IsNPC() && !spreader->IsPet() && it->second->IsNPC()) { - TargetsInRange.push_back(it->second); + if (spreader->IsNPC() && !spreader->IsPet() && !spreader->CastToNPC()->GetSwarmOwner() && cur->IsNPC()) { + TargetsInRange.push_back(cur); } // If the spreader is an npc controlled pet it can spread to any other npc or an npc controlled pet else if (spreader->IsNPC() && spreader->IsPet() && spreader->GetOwner()->IsNPC()) { - if (it->second->IsNPC() && !it->second->IsPet()) { - TargetsInRange.push_back(it->second); - } else if (it->second->IsNPC() && it->second->IsPet() && it->second->GetOwner()->IsNPC()) { - TargetsInRange.push_back(it->second); + if (cur->IsNPC() && !cur->IsPet()) { + TargetsInRange.push_back(cur); + } else if (cur->IsNPC() && cur->IsPet() && cur->GetOwner()->IsNPC()) { + TargetsInRange.push_back(cur); + } + else if (cur->IsNPC() && cur->CastToNPC()->GetSwarmOwner() && cur->GetOwner()->IsNPC()) { + TargetsInRange.push_back(cur); } } // if the spreader is anything else(bot, pet, etc) then it should spread to everything but non client controlled npcs - else if (!spreader->IsNPC() && !it->second->IsNPC()) { - TargetsInRange.push_back(it->second); + else if (!spreader->IsNPC() && !cur->IsNPC()) { + TargetsInRange.push_back(cur); } // if its a pet we need to determine appropriate targets(pet to client, pet to pet, pet to bot, etc) - else if (spreader->IsNPC() && spreader->IsPet() && !spreader->GetOwner()->IsNPC()) { - if (!it->second->IsNPC()) { - TargetsInRange.push_back(it->second); + else if (spreader->IsNPC() && (spreader->IsPet() || spreader->CastToNPC()->GetSwarmOwner()) && !spreader->GetOwner()->IsNPC()) { + if (!cur->IsNPC()) { + TargetsInRange.push_back(cur); } - else if (it->second->IsNPC() && it->second->IsPet() && !it->second->GetOwner()->IsNPC()) { - TargetsInRange.push_back(it->second); + else if (cur->IsNPC() && (cur->IsPet() || cur->CastToNPC()->GetSwarmOwner()) && !cur->GetOwner()->IsNPC()) { + TargetsInRange.push_back(cur); } } } diff --git a/zone/entity.h b/zone/entity.h index c1ba0936c..486f1d49f 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -28,8 +28,7 @@ #include "zonedb.h" #include "zonedump.h" -#include "zonedbasync.h" -#include "QGlobals.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 { @@ -98,9 +97,9 @@ public: const Beacon *CastToBeacon() const; inline const uint16& GetID() const { return id; } + inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; } 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 @@ -114,6 +113,7 @@ protected: uint32 pDBAsyncWorkID; private: uint16 id; + time_t spawn_timestamp; }; class EntityList @@ -136,7 +136,7 @@ public: Mob *GetMob(const char* name); Mob *GetMobByNpcTypeID(uint32 get_id); bool IsMobSpawnedByNpcTypeID(uint32 get_id); - Mob *GetTargetForVirus(Mob* spreader); + Mob *GetTargetForVirus(Mob* spreader, int range); inline NPC *GetNPCByID(uint16 id) { return npc_list.count(id) ? npc_list.at(id) : nullptr; } NPC *GetNPCByNPCTypeID(uint32 npc_id); @@ -251,6 +251,8 @@ public: void RemoveAllLocalities(); void RemoveAllRaids(); void DestroyTempPets(Mob *owner); + int16 CountTempPets(Mob *owner); + void AddTempPetsToHateList(Mob *owner, Mob* other, bool bFrenzy = false); Entity *GetEntityMob(uint16 id); Entity *GetEntityMerc(uint16 id); Entity *GetEntityDoor(uint16 id); @@ -299,7 +301,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); @@ -398,6 +400,7 @@ public: void UpdateFindableNPCState(NPC *n, bool Remove); void HideCorpses(Client *c, uint8 CurrentMode, uint8 NewMode); + uint16 GetClientCount(); void GetMobList(std::list &m_list); void GetNPCList(std::list &n_list); void GetMercList(std::list &n_list); diff --git a/zone/exp.cpp b/zone/exp.cpp index 5e8eb49f8..0e01a4c0e 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -18,10 +18,13 @@ #include "../common/debug.h" #include "../common/features.h" #include "masterentity.h" -#include "StringIDs.h" -#include "../common/StringUtil.h" +#include "string_ids.h" +#include "../common/string_util.h" #include "../common/rulesys.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" +#include "queryserv.h" + +extern QueryServ* QServ; static uint32 MaxBankedGroupLeadershipPoints(int Level) @@ -48,6 +51,8 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level) void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { + this->EVENT_ITEM_ScriptStopReturn(); + uint32 add_exp = in_add_exp; if(!resexp && (XPRate != 0)) @@ -128,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); + } + } } } @@ -203,7 +238,6 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { return; // Must be invalid class/race } - if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) { if (isrezzexp) this->Message_StringID(MT_Experience, REZ_REGAIN); @@ -279,6 +313,14 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { //Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA); char val1[20]={0}; Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)"); //You have gained an ability point! You now have %1 ability point%2. + + /* QS: PlayerLogAARate */ + if (RuleB(QueryServ, PlayerLogAARate)){ + int add_points = (m_pp.aapoints - last_unspentAA); + std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", this->CharacterID(), add_points, add_points); + QServ->SendQuery(query.c_str()); + } + //Message(15, "You now have %d skill points available to spend.", m_pp.aapoints); } @@ -290,12 +332,10 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { if(check_level > maxlevel) { check_level = maxlevel; - if(RuleB(Character, KeepLevelOverMax)) - { + if(RuleB(Character, KeepLevelOverMax)) { set_exp = GetEXPForLevel(GetLevel()+1); } - else - { + else { set_exp = GetEXPForLevel(maxlevel); } } @@ -305,8 +345,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { if(MaxLevel){ if(GetLevel() >= MaxLevel){ uint32 expneeded = GetEXPForLevel(MaxLevel); - if(set_exp > expneeded) - { + if(set_exp > expneeded) { set_exp = expneeded; } } @@ -318,11 +357,11 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { if (GetLevel() == check_level-1){ Message_StringID(MT_Experience, GAIN_LEVEL,ConvertArray(check_level,val1)); SendLevelAppearance(); - //Message(15, "You have gained a level! Welcome to level %i!", check_level); + /* Message(15, "You have gained a level! Welcome to level %i!", check_level); */ } if (GetLevel() == check_level){ Message_StringID(MT_Experience, LOSE_LEVEL,ConvertArray(check_level,val1)); - //Message(15, "You lost a level! You are now level %i!", check_level); + /* Message(15, "You lost a level! You are now level %i!", check_level); */ } else Message(15, "Welcome to level %i!", check_level); @@ -343,8 +382,7 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) { //If were at max level then stop gaining experience if we make it to the cap if(GetLevel() == maxlevel - 1){ uint32 expneeded = GetEXPForLevel(maxlevel); - if(set_exp > expneeded) - { + if(set_exp > expneeded) { set_exp = expneeded; } } @@ -396,15 +434,13 @@ void Client::SetLevel(uint8 set_level, bool command) level = set_level; - if(IsRaidGrouped()) - { + if(IsRaidGrouped()) { Raid *r = this->GetRaid(); if(r){ r->UpdateLevel(GetName(), set_level); } } - if(set_level > m_pp.level2) - { + if(set_level > m_pp.level2) { if(m_pp.level2 == 0) m_pp.points += 5; else @@ -414,6 +450,18 @@ void Client::SetLevel(uint8 set_level, bool command) } if(set_level > m_pp.level) { parse->EventPlayer(EVENT_LEVEL_UP, this, "", 0); + /* QS: PlayerLogLevels */ + if (RuleB(QueryServ, PlayerLogLevels)){ + std::string event_desc = StringFormat("Leveled UP :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc); + } + } + else if (set_level < m_pp.level){ + /* QS: PlayerLogLevels */ + if (RuleB(QueryServ, PlayerLogLevels)){ + std::string event_desc = StringFormat("Leveled DOWN :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc); + } } m_pp.level = set_level; @@ -423,34 +471,34 @@ void Client::SetLevel(uint8 set_level, bool command) lu->exp = 0; } else { - float tmpxp = (float) ( (float) m_pp.exp - GetEXPForLevel( GetLevel() )) / - ( (float) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel())); + float tmpxp = (float) ( (float) m_pp.exp - GetEXPForLevel( GetLevel() )) / ( (float) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel())); lu->exp = (uint32)(330.0f * tmpxp); } QueuePacket(outapp); safe_delete(outapp); this->SendAppearancePacket(AT_WhoLevel, set_level); // who level change - LogFile->write(EQEMuLog::Normal,"Setting Level for %s to %i", GetName(), set_level); + LogFile->write(EQEMuLog::Normal,"Setting Level for %s to %i", GetName(), set_level); CalcBonuses(); - if(!RuleB(Character, HealOnLevel)) - { + + if(!RuleB(Character, HealOnLevel)) { int mhp = CalcMaxHP(); if(GetHP() > mhp) SetHP(mhp); } - else - { + else { SetHP(CalcMaxHP()); // Why not, lets give them a free heal } - DoTributeUpdate(); + DoTributeUpdate(); SendHPUpdate(); SetMana(CalcMaxMana()); UpdateWho(); - if(GetMerc()) - UpdateMercLevel(); + + if(GetMerc()) + UpdateMercLevel(); + Save(); } @@ -499,23 +547,13 @@ uint32 Client::GetEXPForLevel(uint16 check_level) uint32 finalxp = uint32(base * mod); finalxp = mod_client_xp_for_level(finalxp, check_level); - return(finalxp); + return finalxp; } -void Client::AddLevelBasedExp(uint8 exp_percentage, uint8 max_level) { - - if (exp_percentage > 100) - { - exp_percentage = 100; - } - - if (!max_level || GetLevel() < max_level) - { - max_level = GetLevel(); - } - - uint32 newexp = GetEXP() + ((GetEXPForLevel(max_level + 1) - GetEXPForLevel(max_level)) * exp_percentage / 100); - +void Client::AddLevelBasedExp(uint8 exp_percentage, uint8 max_level) { + if (exp_percentage > 100) { exp_percentage = 100; } + if (!max_level || GetLevel() < max_level) { max_level = GetLevel(); } + uint32 newexp = GetEXP() + ((GetEXPForLevel(max_level + 1) - GetEXPForLevel(max_level)) * exp_percentage / 100); SetEXP(newexp, GetAAXP()); } @@ -657,28 +695,25 @@ void Client::SendLeadershipEXPUpdate() { } uint32 Client::GetCharMaxLevelFromQGlobal() { + QGlobalCache *char_c = nullptr; + char_c = this->GetQGlobals(); - QGlobalCache *char_c = nullptr; - char_c = this->GetQGlobals(); + std::list globalMap; + uint32 ntype = 0; - std::list globalMap; - uint32 ntype = 0; + if(char_c) { + QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, this->CharacterID(), zone->GetZoneID()); + } - if(char_c) - { - QGlobalCache::Combine(globalMap, char_c->GetBucket(), ntype, this->CharacterID(), zone->GetZoneID()); - } + std::list::iterator iter = globalMap.begin(); + uint32 gcount = 0; + while(iter != globalMap.end()) { + if((*iter).name.compare("CharMaxLevel") == 0){ + return atoi((*iter).value.c_str()); + } + ++iter; + ++gcount; + } - std::list::iterator iter = globalMap.begin(); - uint32 gcount = 0; - while(iter != globalMap.end()) - { - if((*iter).name.compare("CharMaxLevel") == 0){ - return atoi((*iter).value.c_str()); - } - ++iter; - ++gcount; - } - - return false; // Default is false + return false; } diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 10c66b730..47fc53a96 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -23,7 +23,7 @@ #include #include "../common/rulesys.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "map.h" #include "zone.h" #include "pathing.h" @@ -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/forage.cpp b/zone/forage.cpp index 4f8d17c0c..cbb6550db 100644 --- a/zone/forage.cpp +++ b/zone/forage.cpp @@ -15,82 +15,35 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "../common/debug.h" -#include -#include -#include - -#ifdef _WINDOWS -#define snprintf _snprintf -#endif - -#include "forage.h" -#include "entity.h" -#include "masterentity.h" -#include "npc.h" -#include "water_map.h" -#include "titles.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" #include "../common/rulesys.h" +#include "../common/string_util.h" +#include "entity.h" +#include "forage.h" +#include "npc.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "titles.h" +#include "water_map.h" #include "zonedb.h" + +#include + #ifdef _WINDOWS #define snprintf _snprintf #endif -#include "QuestParserCollection.h" +struct NPCType; //max number of items which can be in the foraging table //for a given zone. #define FORAGE_ITEM_LIMIT 50 -/* - -The fishing and foraging need some work... -foraging currently gives each item an equal chance of dropping -fishing gives items which come in last from the select a very -very low chance of dropping. - - -Schema: -CREATE TABLE forage ( - id int(11) NOT NULL auto_increment, - zoneid int(4) NOT NULL default '0', - Itemid int(11) NOT NULL default '0', - level smallint(6) NOT NULL default '0', - chance smallint(6) NOT NULL default '0', - PRIMARY KEY (id) -) TYPE=MyISAM; - -old table upgrade: -alter table forage add chance smallint(6) NOT NULL default '0'; -update forage set chance=100; - - -CREATE TABLE fishing ( - id int(11) NOT NULL auto_increment, - zoneid int(4) NOT NULL default '0', - Itemid int(11) NOT NULL default '0', - skill_level smallint(6) NOT NULL default '0', - chance smallint(6) NOT NULL default '0', - npc_id int NOT NULL default 0, - npc_chance int NOT NULL default 0, - PRIMARY KEY (id) -) TYPE=MyISAM; - - -*/ - -// This allows EqEmu to have zone specific foraging - BoB uint32 ZoneDatabase::GetZoneForage(uint32 ZoneID, uint8 skill) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint8 index = 0; uint32 item[FORAGE_ITEM_LIMIT]; uint32 chance[FORAGE_ITEM_LIMIT]; uint32 ret; @@ -100,31 +53,32 @@ uint32 ZoneDatabase::GetZoneForage(uint32 ZoneID, uint8 skill) { } uint32 chancepool = 0; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT itemid,chance FROM forage WHERE zoneid= '%i' and level <= '%i' LIMIT %i", ZoneID, skill, FORAGE_ITEM_LIMIT), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result)) && (index < FORAGE_ITEM_LIMIT)) { - item[index] = atoi(row[0]); - chance[index] = atoi(row[1])+chancepool; -LogFile->write(EQEMuLog::Error, "Possible Forage: %d with a %d chance", item[index], chance[index]); - chancepool = chance[index]; - index++; - } - - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in Forage query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT itemid, chance FROM " + "forage WHERE zoneid = '%i' and level <= '%i' " + "LIMIT %i", ZoneID, skill, FORAGE_ITEM_LIMIT); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Forage query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return 0; } + uint8 index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + if (index >= FORAGE_ITEM_LIMIT) + break; + + item[index] = atoi(row[0]); + chance[index] = atoi(row[1]) + chancepool; + LogFile->write(EQEMuLog::Error, "Possible Forage: %d with a %d chance", item[index], chance[index]); + chancepool = chance[index]; + } + + if(chancepool == 0 || index < 1) - return(0); + return 0; if(index == 1) { - return(item[0]); + return item[0]; } ret = 0; @@ -143,12 +97,6 @@ LogFile->write(EQEMuLog::Error, "Possible Forage: %d with a %d chance", item[ind uint32 ZoneDatabase::GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id, uint8 &npc_chance) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - uint8 index = 0; uint32 item[50]; uint32 chance[50]; uint32 npc_ids[50]; @@ -161,44 +109,44 @@ uint32 ZoneDatabase::GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id, chance[c]=0; } - if (RunQuery(query, MakeAnyLenString(&query, "SELECT itemid,chance,npc_id,npc_chance FROM fishing WHERE (zoneid= '%i' || zoneid = 0) and skill_level <= '%i'",ZoneID, skill ), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))&&(index<50)) { - item[index] = atoi(row[0]); - chance[index] = atoi(row[1])+chancepool; - chancepool = chance[index]; - - npc_ids[index] = atoi(row[2]); - npc_chances[index] = atoi(row[3]); - index++; - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in Fishing query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT itemid, chance, npc_id, npc_chance " + "FROM fishing WHERE (zoneid = '%i' || zoneid = 0) AND skill_level <= '%i'", + ZoneID, skill); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in Fishing query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; - } + } + + uint8 index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + if (index >= 50) + break; + + item[index] = atoi(row[0]); + chance[index] = atoi(row[1])+chancepool; + chancepool = chance[index]; + + npc_ids[index] = atoi(row[2]); + npc_chances[index] = atoi(row[3]); + } npc_id = 0; npc_chance = 0; - if (index>0) { - uint32 random = MakeRandomInt(1, chancepool); - for (int i = 0; i < index; i++) - { - if (random <= chance[i]) - { - ret = item[i]; - npc_id = npc_ids[i]; - npc_chance = npc_chances[i]; - break; - } - } - } else { - ret = 0; - } + if (index <= 0) + return 0; + + uint32 random = MakeRandomInt(1, chancepool); + for (int i = 0; i < index; i++) + { + if (random > chance[i]) + continue; + + ret = item[i]; + npc_id = npc_ids[i]; + npc_chance = npc_chances[i]; + break; + } return ret; } @@ -245,7 +193,7 @@ bool Client::CanFish() { dest.y = RodY; dest.z = z_pos+10; - RodZ = zone->zonemap->FindBestZ(dest, nullptr) - 1; + RodZ = zone->zonemap->FindBestZ(dest, nullptr) + 4; bool in_lava = zone->watermap->InLava(RodX, RodY, RodZ); bool in_water = zone->watermap->InWater(RodX, RodY, RodZ) || zone->watermap->InVWater(RodX, RodY, RodZ); //Message(0, "Rod is at %4.3f, %4.3f, %4.3f, InWater says %d, InLava says %d", RodX, RodY, RodZ, in_water, in_lava); @@ -365,11 +313,13 @@ void Client::GoFish() safe_delete(inst); inst = m_inv.GetItem(MainCursor); } - } - std::vector args; - args.push_back(inst); - parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst != nullptr ? inst->GetItem()->ID : 0, &args); + if(inst) { + std::vector args; + args.push_back(inst); + parse->EventPlayer(EVENT_FISH_SUCCESS, this, "", inst->GetID(), &args); + } + } } else { @@ -479,11 +429,13 @@ void Client::ForageItem(bool guarantee) { safe_delete(inst); inst = m_inv.GetItem(MainCursor); } - } - std::vector args; - args.push_back(inst); - parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst ? inst->GetItem()->ID : 0, &args); + if(inst) { + std::vector args; + args.push_back(inst); + parse->EventPlayer(EVENT_FORAGE_SUCCESS, this, "", inst->GetID(), &args); + } + } int ChanceSecondForage = aabonuses.ForageAdditionalItems + itembonuses.ForageAdditionalItems + spellbonuses.ForageAdditionalItems; if(!guarantee && MakeRandomInt(0,99) < ChanceSecondForage) { diff --git a/zone/groups.cpp b/zone/groups.cpp index d948eb476..3180e51f1 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -17,10 +17,10 @@ */ #include "../common/debug.h" #include "masterentity.h" -#include "NpcAI.h" +#include "npc_ai.h" #include "../common/packet_functions.h" #include "../common/packet_dump.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "worldserver.h" extern EntityList entity_list; extern WorldServer worldserver; @@ -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->Message(2, msg.c_str()); + Client *c = members[i]->CastToClient(); + //I could not get MoneyOnCorpse to work, so we use this + c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); + c->Message(2, msg.c_str()); } } } -bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 CharacterID) +bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 CharacterID, bool ismerc) { bool InZone = true; - bool ismerc = false; // This method should either be passed a Mob*, if the new member is in this zone, or a nullptr Mob* // and the name and CharacterID of the new member, if they are out of zone. - // if(!newmember && !NewMemberName) - return false; + { + return false; + } if(GroupCount() >= MAX_GROUP_MEMBERS) //Sanity check for merging groups together. + { return false; + } if(!newmember) + { InZone = false; + } else { NewMemberName = newmember->GetCleanName(); if(newmember->IsClient()) + { CharacterID = newmember->CastToClient()->CharacterID(); + } if(newmember->IsMerc()) { Client* owner = newmember->CastToMerc()->GetMercOwner(); @@ -231,18 +238,20 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte { CharacterID = owner->CastToClient()->CharacterID(); NewMemberName = newmember->GetName(); - ismerc = true; } + ismerc = true; } } - uint32 i = 0; - // See if they are already in the group - // + uint32 i = 0; for (i = 0; i < MAX_GROUP_MEMBERS; ++i) + { if(!strcasecmp(membername[i], NewMemberName)) + { return false; + } + } // Put them in the group for (i = 0; i < MAX_GROUP_MEMBERS; ++i) @@ -250,17 +259,20 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte if (membername[i][0] == '\0') { if(InZone) + { members[i] = newmember; - + } + strcpy(membername[i], NewMemberName); + MemberRoles[i] = 0; break; } } + // Is this even possible based on the above loops? Remove? if (i == MAX_GROUP_MEMBERS) + { return false; - - strcpy(membername[i], NewMemberName); - MemberRoles[i] = 0; + } int x=1; @@ -269,11 +281,12 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; strcpy(gj->membername, NewMemberName); gj->action = groupActJoin; - gj->leader_aas = LeaderAbilities; - for (i = 0;i < MAX_GROUP_MEMBERS; i++) { - if (members[i] != nullptr && members[i] != newmember) { + for (i = 0;i < MAX_GROUP_MEMBERS; i++) + { + if (members[i] != nullptr && members[i] != newmember) + { //fill in group join & send it if(members[i]->IsMerc()) { @@ -283,18 +296,23 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte { strcpy(gj->yourname, members[i]->GetCleanName()); } - if(members[i]->IsClient()) { + if(members[i]->IsClient()) + { members[i]->CastToClient()->QueuePacket(outapp); - //put new member into existing person's list + //put new member into existing group members' list(s) strcpy(members[i]->CastToClient()->GetPP().groupMembers[this->GroupCount()-1], NewMemberName); } - //put this existing person into the new member's list - if(InZone && newmember->IsClient()) { + //put existing group member(s) into the new member's list + if(InZone && newmember->IsClient()) + { if(IsLeader(members[i])) + { strcpy(newmember->CastToClient()->GetPP().groupMembers[0], members[i]->GetCleanName()); - else { + } + else + { strcpy(newmember->CastToClient()->GetPP().groupMembers[x], members[i]->GetCleanName()); x++; } @@ -336,7 +354,9 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte #endif //BOTS } else + { database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc); + } safe_delete(outapp); @@ -468,6 +488,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; } @@ -501,6 +526,33 @@ void Group::MemberZoned(Mob* removemob) { if(removemob->IsClient() && HasRole(removemob, RolePuller)) SetGroupPullerTarget(0); + + if (removemob->IsClient() && removemob == mentoree) + mentoree = nullptr; +} + +void Group::SendGroupJoinOOZ(Mob* NewMember) { + + if (NewMember == nullptr) + { + return; + } + + if (!NewMember->HasGroup()) + { + return; + } + + //send updates to clients out of zone... + ServerPacket* pack = new ServerPacket(ServerOP_GroupJoin, sizeof(ServerGroupJoin_Struct)); + ServerGroupJoin_Struct* gj = (ServerGroupJoin_Struct*)pack->pBuffer; + gj->gid = GetID(); + gj->zoneid = zone->GetZoneID(); + gj->instance_id = zone->GetInstanceID(); + strcpy(gj->member_name, NewMember->GetName()); + worldserver.SendPacket(pack); + safe_delete(pack); + } bool Group::DelMemberOOZ(const char *Name) { @@ -529,6 +581,8 @@ bool Group::DelMemberOOZ(const char *Name) { } ClearAllNPCMarks(); } + if (Name == mentoree_name) + ClearGroupMentor(); return true; } } @@ -538,12 +592,15 @@ bool Group::DelMemberOOZ(const char *Name) { bool Group::DelMember(Mob* oldmember,bool ignoresender) { - if (oldmember == nullptr){ + if (oldmember == nullptr) + { return false; } - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == oldmember) { + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if (members[i] == oldmember) + { members[i] = nullptr; membername[i][0] = '\0'; memset(membername[i],0,64); @@ -552,15 +609,40 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) } } + /* This may seem pointless but the case above does not cover the following situation: + * Group has Leader a, member b, member c + * b and c are out of zone + * a disconnects/quits + * b or c zone back in and disconnects/quits + * a is still "leader" from GetLeader()'s perspective and will crash the zone when we DelMember(b) + * Ultimately we should think up a better solution to this. + */ + if(oldmember == GetLeader()) + { + SetLeader(nullptr); + } + //handle leader quitting group gracefully - if (oldmember == GetLeader() && GroupCount() >= 2) { - for(uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++) { - if(members[nl]) { - ChangeLeader(members[nl]); - break; + if (oldmember == GetLeader() && GroupCount() >= 2) + { + for(uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++) + { + if(members[nl]) + { + if (members[nl]->IsClient()) + { + ChangeLeader(members[nl]); + break; + } } } } + + if (GetLeader() == nullptr) + { + DisbandGroup(); + return true; + } ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; @@ -596,7 +678,8 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) #endif //BOTS } - if (!ignoresender) { + if (!ignoresender) + { strcpy(gu->yourname,oldmember->GetCleanName()); strcpy(gu->membername,oldmember->GetCleanName()); gu->action = groupActLeave; @@ -606,7 +689,18 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) } if(oldmember->IsClient()) - database.SetGroupID(oldmember->GetCleanName(), 0, oldmember->CastToClient()->CharacterID()); + { + database.SetGroupID(oldmember->GetCleanName(), 0, oldmember->CastToClient()->CharacterID(), false); + } + + if(oldmember->IsMerc()) + { + Client* owner = oldmember->CastToMerc()->GetMercOwner(); + if(owner) + { + database.SetGroupID(oldmember->GetName(), 0, owner->CharacterID(), true); + } + } oldmember->SetGrouped(false); disbandcheck = true; @@ -631,6 +725,9 @@ bool Group::DelMember(Mob* oldmember,bool ignoresender) UnDelegatePuller(oldmember->GetName()); } + if (oldmember->GetName() == mentoree_name) + ClearGroupMentor(); + if(oldmember->IsClient()) SendMarkedNPCsToMember(oldmember->CastToClient(), true); @@ -667,7 +764,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { if(members[z] == caster) { caster->SpellOnTarget(spell_id, caster); #ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + if(spells[spell_id].targettype != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) caster->SpellOnTarget(spell_id, caster->GetPet()); #endif } @@ -678,7 +775,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { members[z]->CalcSpellPowerDistanceMod(spell_id, distance); caster->SpellOnTarget(spell_id, members[z]); #ifdef GROUP_BUFF_PETS - if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) + if(spells[spell_id].targettype != ST_GroupNoPets && members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) caster->SpellOnTarget(spell_id, members[z]->GetPet()); #endif } else @@ -793,20 +890,33 @@ void Group::DisbandGroup() { Client *Leader = nullptr; uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == nullptr) { + for (i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if (members[i] == nullptr) + { continue; } - if (members[i]->IsClient()) { + if (members[i]->IsClient()) + { if(IsLeader(members[i])) + { Leader = members[i]->CastToClient(); + } strcpy(gu->yourname, members[i]->GetName()); - database.SetGroupID(members[i]->GetName(), 0, members[i]->CastToClient()->CharacterID()); + database.SetGroupID(members[i]->GetName(), 0, members[i]->CastToClient()->CharacterID(), false); members[i]->CastToClient()->QueuePacket(outapp); SendMarkedNPCsToMember(members[i]->CastToClient(), true); - + } + + if (members[i]->IsMerc()) + { + Client* owner = members[i]->CastToMerc()->GetMercOwner(); + if(owner) + { + database.SetGroupID(members[i]->GetName(), 0, owner->CharacterID(), true); + } } members[i]->SetGrouped(false); @@ -826,9 +936,12 @@ void Group::DisbandGroup() { entity_list.RemoveGroup(GetID()); if(GetID() != 0) + { database.ClearGroup(GetID()); + } - if(Leader && (Leader->IsLFP())) { + if(Leader && (Leader->IsLFP())) + { Leader->UpdateLFP(); } @@ -910,8 +1023,12 @@ uint8 Group::GroupCount() { uint8 MemberCount = 0; for(uint8 i = 0; i < MAX_GROUP_MEMBERS; ++i) + { if(membername[i][0]) + { ++MemberCount; + } + } return MemberCount; } @@ -958,31 +1075,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() { @@ -1046,27 +1160,49 @@ void Group::GroupMessage_StringID(Mob* sender, uint32 type, uint32 string_id, co void Client::LeaveGroup() { Group *g = GetGroup(); - if(g) { - if(g->GroupCount() < 3) + if(g) + { + int32 MemberCount = g->GroupCount(); + // Account for both client and merc leaving the group + if (GetMerc() && GetMerc()->HasGroup() && GetMerc()->GetGroup() == g) + { + MemberCount -= 1; + } + + if(MemberCount < 3) + { g->DisbandGroup(); + } else + { g->DelMember(this); - } else { + if (GetMerc() && GetMerc()->HasGroup() && GetMerc()->GetGroup() == g) + { + GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); + } + } + } + else + { //force things a little - database.SetGroupID(GetName(), 0, CharacterID()); + database.SetGroupID(GetName(), 0, CharacterID(), false); + if (GetMerc()) + { + database.SetGroupID(GetMerc()->GetName(), 0, CharacterID(), true); + } } isgrouped = false; } -void Group::HealGroup(uint32 heal_amt, Mob* caster, int32 range) +void Group::HealGroup(uint32 heal_amt, Mob* caster, float range) { if (!caster) return; if (!range) range = 200; - + float distance; float range2 = range*range; @@ -1097,16 +1233,16 @@ void Group::HealGroup(uint32 heal_amt, Mob* caster, int32 range) } -void Group::BalanceHP(int32 penalty, int32 range, Mob* caster, int32 limit) +void Group::BalanceHP(int32 penalty, float range, Mob* caster, int32 limit) { if (!caster) return; if (!range) - range = 200; - + range = 200; + int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; - + float distance; float range2 = range*range; @@ -1147,22 +1283,22 @@ void Group::BalanceHP(int32 penalty, int32 range, Mob* caster, int32 limit) } } -void Group::BalanceMana(int32 penalty, int32 range, Mob* caster, int32 limit) +void Group::BalanceMana(int32 penalty, float range, Mob* caster, int32 limit) { if (!caster) 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){ @@ -1330,15 +1466,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()); } } @@ -1379,15 +1512,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); } } @@ -1428,15 +1559,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); } } @@ -1582,15 +1711,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) { @@ -1636,15 +1761,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) { @@ -1668,15 +1788,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) { @@ -1748,6 +1864,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. @@ -1811,16 +1952,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) @@ -1897,37 +2033,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..a0b17229b 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -51,7 +51,7 @@ public: Group(uint32 gid); ~Group(); - bool AddMember(Mob* newmember, const char* NewMemberName = nullptr, uint32 CharacterID = 0); + bool AddMember(Mob* newmember, const char* NewMemberName = nullptr, uint32 CharacterID = 0, bool ismerc = false); void AddMember(const char* NewMemberName); void SendUpdate(uint32 type,Mob* member); void SendLeadershipAAUpdate(); @@ -63,6 +63,7 @@ public: bool IsGroupMember(const char *Name); bool Process(); bool IsGroup() { return true; } + void SendGroupJoinOOZ(Mob* NewMember); void CastGroupSpell(Mob* caster,uint16 spellid); void GroupBardPulse(Mob* caster,uint16 spellid); void SplitExp(uint32 exp, Mob* other); @@ -86,9 +87,9 @@ public: uint16 GetAvgLevel(); bool LearnMembers(); void VerifyGroup(); - void BalanceHP(int32 penalty, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); - void BalanceMana(int32 penalty, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); - void HealGroup(uint32 heal_amt, Mob* caster, int32 range = 0); + void BalanceHP(int32 penalty, float range = 0, Mob* caster = nullptr, int32 limit = 0); + void BalanceMana(int32 penalty, float range = 0, Mob* caster = nullptr, int32 limit = 0); + void HealGroup(uint32 heal_amt, Mob* caster, float range = 0); inline void SetGroupAAs(GroupLeadershipAA_Struct *From) { memcpy(&LeaderAbilities, From, sizeof(GroupLeadershipAA_Struct)); } inline void GetGroupAAs(GroupLeadershipAA_Struct *Into) { memcpy(Into, &LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); } void UpdateGroupAAs(); @@ -132,6 +133,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 +157,9 @@ private: uint16 PullerTargetID; uint16 MarkedNPCs[MAX_MARKED_NPCS]; + std::string mentoree_name; + Client *mentoree; + int mentor_percent; }; #endif diff --git a/zone/guild.cpp b/zone/guild.cpp index 21e350cdf..58b0f006e 100644 --- a/zone/guild.cpp +++ b/zone/guild.cpp @@ -15,23 +15,13 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "../common/debug.h" -#include "masterentity.h" -#include "worldserver.h" -#include "net.h" + #include "../common/database.h" -#include "../common/spdat.h" -#include "../common/packet_dump.h" -#include "../common/packet_functions.h" -#include "petitions.h" -#include "../common/serverinfo.h" -#include "../common/ZoneNumbers.h" -#include "../common/moremath.h" #include "../common/guilds.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" + #include "guild_mgr.h" -#include "StringIDs.h" -#include "NpcAI.h" +#include "worldserver.h" extern WorldServer worldserver; @@ -160,9 +150,17 @@ void Client::SendGuildSpawnAppearance() { uint8 rank = guild_mgr.GetDisplayedRank(GuildID(), GuildRank(), CharacterID()); mlog(GUILDS__OUT_PACKETS, "Sending spawn appearance for guild %d at rank %d", GuildID(), rank); SendAppearancePacket(AT_GuildID, GuildID()); + 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_GuildRank, rank); } - UpdateWho(); } @@ -409,57 +407,36 @@ void Client::GuildChangeRank(const char* name, uint32 guild_id, uint32 oldrank, }*/ -bool ZoneDatabase::CheckGuildDoor(uint8 doorid,uint16 guild_id,const char* zone) { - MYSQL_ROW row; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - if (!RunQuery(query, MakeAnyLenString(&query, - "SELECT guild FROM doors where doorid=%i AND zone='%s'", - doorid-128, zone), errbuf, &result)) - { - LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query, errbuf); - safe_delete_array(query); - return false; - } else { - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if (atoi(row[0]) == guild_id) - { - mysql_free_result(result); - return true; - } - else - { - mysql_free_result(result); - return false; - } +bool ZoneDatabase::CheckGuildDoor(uint8 doorid, uint16 guild_id, const char* zone) { - // code below will never be reached - mysql_free_result(result); - return false; - } + std::string query = StringFormat("SELECT guild FROM doors WHERE doorid = %i AND zone = '%s'", + doorid-128, zone); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in CheckGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - return false; + + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + return atoi(row[0]) == guild_id; } bool ZoneDatabase::SetGuildDoor(uint8 doorid,uint16 guild_id, const char* zone) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; + if (doorid > 127) doorid = doorid - 128; - if (!RunQuery(query, MakeAnyLenString(&query, - "UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')", - guild_id, doorid, zone), errbuf, 0,&affected_rows)) - { - LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query, errbuf); - safe_delete_array(query); + + std::string query = StringFormat("UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')", + guild_id, doorid, zone); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in SetGuildDoor query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - return(affected_rows > 0); + return (results.RowsAffected() > 0); } diff --git a/zone/guild_mgr.cpp b/zone/guild_mgr.cpp index f6f57fe51..bad060aef 100644 --- a/zone/guild_mgr.cpp +++ b/zone/guild_mgr.cpp @@ -20,63 +20,10 @@ #include "zonedb.h" #include "worldserver.h" #include "../common/servertalk.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #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; @@ -374,6 +321,10 @@ void ZoneGuildManager::ProcessWorldPacket(ServerPacket *pack) { else if(c != nullptr && s->guild_id != GUILD_NONE) { //char is in zone, and has changed into a new guild, send MOTD. c->SendGuildMOTD(); + if(c->GetClientVersion() >= EQClientRoF) + { + c->SendGuildRanks(); + } } @@ -644,104 +595,73 @@ GuildBankManager::~GuildBankManager() } } -bool GuildBankManager::Load(uint32 GuildID) +bool GuildBankManager::Load(uint32 guildID) { - const char *LoadQuery = "SELECT `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `whofor` from `guild_bank` " - "WHERE `guildid` = %i"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* query = 0; - - MYSQL_RES *result; - - MYSQL_ROW row; - - if(database.RunQuery(query, MakeAnyLenString(&query, LoadQuery, GuildID), errbuf, &result)) - { - GuildBank *Bank = new GuildBank; - - Bank->GuildID = GuildID; - - for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i) - Bank->Items.MainArea[i].ItemID = 0; - - for(int i = 0; i < GUILD_BANK_DEPOSIT_AREA_SIZE; ++i) - Bank->Items.DepositArea[i].ItemID = 0; - - char Donator[64], WhoFor[64]; - - while((row = mysql_fetch_row(result))) - { - int Area = atoi(row[0]); - - int Slot = atoi(row[1]); - - int ItemID = atoi(row[2]); - - int Qty = atoi(row[3]); - - if(row[4]) - strn0cpy(Donator, row[4], sizeof(Donator)); - else - Donator[0] = '\0'; - - int Permissions = atoi(row[5]); - - if(row[6]) - strn0cpy(WhoFor, row[6], sizeof(WhoFor)); - else - WhoFor[0] = '\0'; - - if(Area == GuildBankMainArea) - { - if((Slot >= 0) && (Slot < GUILD_BANK_MAIN_AREA_SIZE)) - { - Bank->Items.MainArea[Slot].ItemID = ItemID; - - Bank->Items.MainArea[Slot].Quantity = Qty; - - strn0cpy(Bank->Items.MainArea[Slot].Donator, Donator, sizeof(Donator)); - - Bank->Items.MainArea[Slot].Permissions = Permissions; - - strn0cpy(Bank->Items.MainArea[Slot].WhoFor, WhoFor, sizeof(WhoFor)); - } - } - else - { - if((Slot >= 0 ) && (Slot < GUILD_BANK_DEPOSIT_AREA_SIZE)) - { - Bank->Items.DepositArea[Slot].ItemID = ItemID; - - Bank->Items.DepositArea[Slot].Quantity = Qty; - - strn0cpy(Bank->Items.DepositArea[Slot].Donator, Donator, sizeof(Donator)); - - Bank->Items.DepositArea[Slot].Permissions = Permissions; - - strn0cpy(Bank->Items.DepositArea[Slot].WhoFor, WhoFor, sizeof(WhoFor)); - } - } - - } - mysql_free_result(result); - - safe_delete_array(query); - - Banks.push_back(Bank); - } - else - { - _log(GUILDS__BANK_ERROR, "Error Loading guild bank: %s, %s", query, errbuf); - - safe_delete_array(query); + std::string query = StringFormat("SELECT `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `whofor` " + "FROM `guild_bank` WHERE `guildid` = %i", guildID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + _log(GUILDS__BANK_ERROR, "Error Loading guild bank: %s, %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - return true; + GuildBank *bank = new GuildBank; + bank->GuildID = guildID; + + for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i) + bank->Items.MainArea[i].ItemID = 0; + + for(int i = 0; i < GUILD_BANK_DEPOSIT_AREA_SIZE; ++i) + bank->Items.DepositArea[i].ItemID = 0; + + char donator[64], whoFor[64]; + + for (auto row = results.begin(); row != results.end(); ++row) + { + int area = atoi(row[0]); + int slot = atoi(row[1]); + int itemID = atoi(row[2]); + int qty = atoi(row[3]); + + if(row[4]) + strn0cpy(donator, row[4], sizeof(donator)); + else + donator[0] = '\0'; + + int permissions = atoi(row[5]); + + if(row[6]) + strn0cpy(whoFor, row[6], sizeof(whoFor)); + else + whoFor[0] = '\0'; + + if(slot < 0) + continue; + + GuildBankItem *itemSection = nullptr; + + if (area == GuildBankMainArea && slot < GUILD_BANK_MAIN_AREA_SIZE) + itemSection = bank->Items.MainArea; + else if (area != GuildBankMainArea && slot < GUILD_BANK_DEPOSIT_AREA_SIZE) + itemSection = bank->Items.DepositArea; + else + continue; + + itemSection[slot].ItemID = itemID; + itemSection[slot].Quantity = qty; + + strn0cpy(itemSection[slot].Donator, donator, sizeof(donator)); + + itemSection[slot].Permissions = permissions; + + strn0cpy(itemSection[slot].WhoFor, whoFor, sizeof(whoFor)); + } + + Banks.push_back(bank); + + return true; } bool GuildBankManager::IsLoaded(uint32 GuildID) @@ -930,24 +850,16 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32 return false; } - const char *Query="INSERT INTO `guild_bank` (`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) " - "VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* query = 0; - - if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor), errbuf)) - { - _log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query, errbuf); - - safe_delete_array(query); - + std::string query = StringFormat("INSERT INTO `guild_bank` " + "(`guildid`, `area`, `slot`, `itemid`, `qty`, `donator`, `permissions`, `WhoFor`) " + "VALUES (%i, %i, %i, %i, %i, '%s', %i, '%s')", + GuildID, Area, Slot, ItemID, QtyOrCharges, Donator, Permissions, WhoFor); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + _log(GUILDS__BANK_ERROR, "Insert Error: %s : %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - const Item_Struct *Item = database.GetItem(ItemID); GuildBankItemUpdate_Struct gbius; @@ -973,156 +885,127 @@ bool GuildBankManager::AddItem(uint32 GuildID, uint8 Area, uint32 ItemID, int32 return true; } -int GuildBankManager::Promote(uint32 GuildID, int SlotID) +int GuildBankManager::Promote(uint32 guildID, int slotID) { - if((SlotID < 0) || (SlotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1))) + if((slotID < 0) || (slotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1))) return -1; - std::list::iterator Iterator = GetGuildBank(GuildID); + auto iter = GetGuildBank(guildID); - if(Iterator == Banks.end()) - { + if(iter == Banks.end()) return -1; - } - if((*Iterator)->Items.DepositArea[SlotID].ItemID == 0) - { + if((*iter)->Items.DepositArea[slotID].ItemID == 0) return -1; - } - int MainSlot = -1; + int mainSlot = -1; for(int i = 0; i < GUILD_BANK_MAIN_AREA_SIZE; ++i) - if((*Iterator)->Items.MainArea[i].ItemID == 0) - { - MainSlot = i; - + if((*iter)->Items.MainArea[i].ItemID == 0) { + mainSlot = i; break; } - if(MainSlot == -1) + if(mainSlot == -1) return -1; + (*iter)->Items.MainArea[mainSlot].ItemID = (*iter)->Items.DepositArea[slotID].ItemID; + (*iter)->Items.MainArea[mainSlot].Quantity = (*iter)->Items.DepositArea[slotID].Quantity; + (*iter)->Items.MainArea[mainSlot].Permissions = (*iter)->Items.DepositArea[slotID].Permissions; - (*Iterator)->Items.MainArea[MainSlot].ItemID = (*Iterator)->Items.DepositArea[SlotID].ItemID; - - (*Iterator)->Items.MainArea[MainSlot].Quantity = (*Iterator)->Items.DepositArea[SlotID].Quantity; - - strn0cpy((*Iterator)->Items.MainArea[MainSlot].Donator, (*Iterator)->Items.DepositArea[SlotID].Donator, sizeof((*Iterator)->Items.MainArea[MainSlot].Donator)); - (*Iterator)->Items.MainArea[MainSlot].Permissions = (*Iterator)->Items.DepositArea[SlotID].Permissions; - - strn0cpy((*Iterator)->Items.MainArea[MainSlot].WhoFor, (*Iterator)->Items.DepositArea[SlotID].WhoFor, sizeof((*Iterator)->Items.MainArea[MainSlot].WhoFor)); - - const char *Query="UPDATE `guild_bank` SET `area` = 1, `slot` = %i WHERE `guildid` = %i AND `area` = 0 AND `slot` = %i LIMIT 1"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* query = 0; - - if(!database.RunQuery(query, MakeAnyLenString(&query, Query, MainSlot, GuildID, SlotID), errbuf)) - { - _log(GUILDS__BANK_ERROR, "error promoting item: %s : %s", query, errbuf); - - safe_delete_array(query); + strn0cpy((*iter)->Items.MainArea[mainSlot].Donator, (*iter)->Items.DepositArea[slotID].Donator, sizeof((*iter)->Items.MainArea[mainSlot].Donator)); + strn0cpy((*iter)->Items.MainArea[mainSlot].WhoFor, (*iter)->Items.DepositArea[slotID].WhoFor, sizeof((*iter)->Items.MainArea[mainSlot].WhoFor)); + std::string query = StringFormat("UPDATE `guild_bank` SET `area` = 1, `slot` = %i " + "WHERE `guildid` = %i AND `area` = 0 AND `slot` = %i " + "LIMIT 1", mainSlot, guildID, slotID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + _log(GUILDS__BANK_ERROR, "error promoting item: %s : %s", query.c_str(), results.ErrorMessage().c_str()); return -1; } - safe_delete_array(query); + (*iter)->Items.DepositArea[slotID].ItemID = 0; - (*Iterator)->Items.DepositArea[SlotID].ItemID = 0; - - const Item_Struct *Item = database.GetItem((*Iterator)->Items.MainArea[MainSlot].ItemID); + const Item_Struct *Item = database.GetItem((*iter)->Items.MainArea[mainSlot].ItemID); GuildBankItemUpdate_Struct gbius; if(!Item->Stackable) - gbius.Init(GuildBankItemUpdate, 1, MainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, 0, 0, 0); + gbius.Init(GuildBankItemUpdate, 1, mainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, 0, 0, 0); else { - if((*Iterator)->Items.MainArea[MainSlot].Quantity == Item->StackSize) - gbius.Init(GuildBankItemUpdate, 1, MainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, - (*Iterator)->Items.MainArea[MainSlot].Quantity, 0, 0, 0); + if((*iter)->Items.MainArea[mainSlot].Quantity == Item->StackSize) + gbius.Init(GuildBankItemUpdate, 1, mainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*iter)->Items.MainArea[mainSlot].Quantity, 0, 0, 0); else - gbius.Init(GuildBankItemUpdate, 1, MainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, - (*Iterator)->Items.MainArea[MainSlot].Quantity, 0, 1, 0); + gbius.Init(GuildBankItemUpdate, 1, mainSlot, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*iter)->Items.MainArea[mainSlot].Quantity, 0, 1, 0); } strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName)); - entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID); - gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankDepositArea, 0, 0, 0, 0, 0, 0, 0); + gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankDepositArea, 0, 0, 0, 0, 0, 0, 0); - entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID); - return MainSlot; + return mainSlot; } -void GuildBankManager::SetPermissions(uint32 GuildID, uint16 SlotID, uint32 Permissions, const char *MemberName) +void GuildBankManager::SetPermissions(uint32 guildID, uint16 slotID, uint32 permissions, const char *memberName) { - if((SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1))) + if((slotID > (GUILD_BANK_MAIN_AREA_SIZE - 1))) return; - std::list::iterator Iterator = GetGuildBank(GuildID); + auto iter = GetGuildBank(guildID); - if(Iterator == Banks.end()) + if(iter == Banks.end()) + return; + + if((*iter)->Items.MainArea[slotID].ItemID == 0) + return; + + std::string query = StringFormat("UPDATE `guild_bank` SET `permissions` = %i, `whofor` = '%s' " + "WHERE `guildid` = %i AND `area` = 1 AND `slot` = %i LIMIT 1", + permissions, memberName, guildID, slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + _log(GUILDS__BANK_ERROR, "error changing permissions: %s : %s", query.c_str(), results.ErrorMessage().c_str()); return; } - if((*Iterator)->Items.MainArea[SlotID].ItemID == 0) - { - return; - } + (*iter)->Items.MainArea[slotID].Permissions = permissions; - const char *Query="UPDATE `guild_bank` SET `permissions` = %i, `whofor` = '%s' WHERE `guildid` = %i AND `area` = 1 AND `slot` = %i LIMIT 1"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* query = 0; - - if(!database.RunQuery(query, MakeAnyLenString(&query, Query, Permissions, MemberName, GuildID, SlotID), errbuf)) - { - _log(GUILDS__BANK_ERROR, "error changing permissions: %s : %s", query, errbuf); - - safe_delete_array(query); - - return; - } - - safe_delete_array(query); - - (*Iterator)->Items.MainArea[SlotID].Permissions = Permissions; - - if(Permissions == GuildBankSingleMember) - strn0cpy((*Iterator)->Items.MainArea[SlotID].WhoFor, MemberName, sizeof((*Iterator)->Items.MainArea[SlotID].WhoFor)); + if(permissions == GuildBankSingleMember) + strn0cpy((*iter)->Items.MainArea[slotID].WhoFor, memberName, sizeof((*iter)->Items.MainArea[slotID].WhoFor)); else - (*Iterator)->Items.MainArea[SlotID].WhoFor[0] = '\0'; + (*iter)->Items.MainArea[slotID].WhoFor[0] = '\0'; - - const Item_Struct *Item = database.GetItem((*Iterator)->Items.MainArea[SlotID].ItemID); + const Item_Struct *Item = database.GetItem((*iter)->Items.MainArea[slotID].ItemID); GuildBankItemUpdate_Struct gbius; if(!Item->Stackable) - gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, (*Iterator)->Items.MainArea[SlotID].Permissions, 0, 0); + gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankMainArea, 1, Item->ID, Item->Icon, 1, (*iter)->Items.MainArea[slotID].Permissions, 0, 0); else { - if((*Iterator)->Items.MainArea[SlotID].Quantity == Item->StackSize) - gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankMainArea, 1, Item->ID, Item->Icon, - (*Iterator)->Items.MainArea[SlotID].Quantity, (*Iterator)->Items.MainArea[SlotID].Permissions, 0, 0); + if((*iter)->Items.MainArea[slotID].Quantity == Item->StackSize) + gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*iter)->Items.MainArea[slotID].Quantity, (*iter)->Items.MainArea[slotID].Permissions, 0, 0); else - gbius.Init(GuildBankItemUpdate, 1, SlotID, GuildBankMainArea, 1, Item->ID, Item->Icon, - (*Iterator)->Items.MainArea[SlotID].Quantity, (*Iterator)->Items.MainArea[SlotID].Permissions, 1, 0); + gbius.Init(GuildBankItemUpdate, 1, slotID, GuildBankMainArea, 1, Item->ID, Item->Icon, + (*iter)->Items.MainArea[slotID].Quantity, (*iter)->Items.MainArea[slotID].Permissions, 1, 0); } strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName)); - strn0cpy(gbius.WhoFor, (*Iterator)->Items.MainArea[SlotID].WhoFor, sizeof(gbius.WhoFor)); + strn0cpy(gbius.WhoFor, (*iter)->Items.MainArea[slotID].WhoFor, sizeof(gbius.WhoFor)); - entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID); } ItemInst* GuildBankManager::GetItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity) @@ -1208,90 +1091,73 @@ std::list::iterator GuildBankManager::GetGuildBank(uint32 GuildID) return Iterator; } -bool GuildBankManager::DeleteItem(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity) +bool GuildBankManager::DeleteItem(uint32 guildID, uint16 area, uint16 slotID, uint32 quantity) { - std::list::iterator Iterator = GetGuildBank(GuildID); + auto iter = GetGuildBank(guildID); - if(Iterator == Banks.end()) + if(iter == Banks.end()) return false; - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* query = 0; - GuildBankItem* BankArea = nullptr; - if(Area == GuildBankMainArea) + if(area == GuildBankMainArea) { - if(SlotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) + if(slotID > (GUILD_BANK_MAIN_AREA_SIZE - 1)) return false; - BankArea = &(*Iterator)->Items.MainArea[0]; - } - else - { - if(SlotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1)) + BankArea = &(*iter)->Items.MainArea[0]; + } else { + if(slotID > (GUILD_BANK_DEPOSIT_AREA_SIZE - 1)) return false; - BankArea = &(*Iterator)->Items.DepositArea[0]; + BankArea = &(*iter)->Items.DepositArea[0]; } + bool deleted = true; - bool Deleted = true; - - const Item_Struct *Item = database.GetItem(BankArea[SlotID].ItemID); - - if(!Item->Stackable || (Quantity >= BankArea[SlotID].Quantity)) - { - const char *Query = "DELETE from `guild_bank` where `guildid` = %i AND `area` = %i AND `slot` = %i LIMIT 1"; - - if(!database.RunQuery(query, MakeAnyLenString(&query, Query, GuildID, Area, SlotID), errbuf)) - { - _log(GUILDS__BANK_ERROR, "Delete item failed. %s : %s", query, errbuf); - - safe_delete_array(query); + const Item_Struct *Item = database.GetItem(BankArea[slotID].ItemID); + if(!Item->Stackable || (quantity >= BankArea[slotID].Quantity)) { + std::string query = StringFormat("DELETE FROM `guild_bank` WHERE `guildid` = %i " + "AND `area` = %i AND `slot` = %i LIMIT 1", + guildID, area, slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + _log(GUILDS__BANK_ERROR, "Delete item failed. %s : %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); - - BankArea[SlotID].ItemID = 0; - } - else - { - const char *Query = "UPDATE `guild_bank` SET `qty` = %i where `guildid` = %i AND `area` = %i AND `slot` = %i LIMIT 1"; - - if(!database.RunQuery(query, MakeAnyLenString(&query, Query, BankArea[SlotID].Quantity - Quantity, - GuildID, Area, SlotID), errbuf)) - { - _log(GUILDS__BANK_ERROR, "Update item failed. %s : %s", query, errbuf); - - safe_delete_array(query); + BankArea[slotID].ItemID = 0; + } else { + std::string query = StringFormat("UPDATE `guild_bank` SET `qty` = %i WHERE `guildid` = %i " + "AND `area` = %i AND `slot` = %i LIMIT 1", + BankArea[slotID].Quantity - quantity, guildID, area, slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + _log(GUILDS__BANK_ERROR, "Update item failed. %s : %s", query.c_str(), results.ErrorMessage().c_str()); return false; } - safe_delete_array(query); + BankArea[slotID].Quantity -= quantity; - BankArea[SlotID].Quantity -= Quantity; - - Deleted = false; + deleted = false; } + GuildBankItemUpdate_Struct gbius; - if(!Deleted) + if(!deleted) { - gbius.Init(GuildBankItemUpdate, 1, SlotID, Area, 1, Item->ID, Item->Icon, BankArea[SlotID].Quantity, BankArea[SlotID].Permissions, 1, 0); + gbius.Init(GuildBankItemUpdate, 1, slotID, area, 1, Item->ID, Item->Icon, BankArea[slotID].Quantity, BankArea[slotID].Permissions, 1, 0); strn0cpy(gbius.ItemName, Item->Name, sizeof(gbius.ItemName)); - strn0cpy(gbius.WhoFor, BankArea[SlotID].WhoFor, sizeof(gbius.WhoFor)); + strn0cpy(gbius.WhoFor, BankArea[slotID].WhoFor, sizeof(gbius.WhoFor)); } else - gbius.Init(GuildBankItemUpdate, 1, SlotID, Area, 0, 0, 0, 0, 0, 0, 0); + gbius.Init(GuildBankItemUpdate, 1, slotID, area, 0, 0, 0, 0, 0, 0, 0); - entity_list.QueueClientsGuildBankItemUpdate(&gbius, GuildID); + entity_list.QueueClientsGuildBankItemUpdate(&gbius, guildID); return true; @@ -1422,26 +1288,20 @@ bool GuildBankManager::SplitStack(uint32 GuildID, uint16 SlotID, uint32 Quantity return true; } -void GuildBankManager::UpdateItemQuantity(uint32 GuildID, uint16 Area, uint16 SlotID, uint32 Quantity) +void GuildBankManager::UpdateItemQuantity(uint32 guildID, uint16 area, uint16 slotID, uint32 quantity) { // Helper method for MergeStacks. Assuming all passed parameters are valid. // - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* query = 0; - - const char *Query = "UPDATE `guild_bank` SET `qty` = %i where `guildid` = %i AND `area` = %i AND `slot` = %i LIMIT 1"; - - if(!database.RunQuery(query, MakeAnyLenString(&query, Query, Quantity, GuildID, Area, SlotID), errbuf)) - { - _log(GUILDS__BANK_ERROR, "Update item quantity failed. %s : %s", query, errbuf); - - safe_delete_array(query); - + std::string query = StringFormat("UPDATE `guild_bank` SET `qty` = %i " + "WHERE `guildid` = %i AND `area` = %i " + "AND `slot` = %i LIMIT 1", + quantity, guildID, area, slotID); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + _log(GUILDS__BANK_ERROR, "Update item quantity failed. %s : %s", query.c_str(), results.ErrorMessage().c_str()); return; } - safe_delete_array(query); } bool GuildBankManager::AllowedToWithdraw(uint32 GuildID, uint16 Area, uint16 SlotID, const char *Name) diff --git a/zone/hate_list.cpp b/zone/hate_list.cpp index 2d7bd3ea6..4c4290232 100644 --- a/zone/hate_list.cpp +++ b/zone/hate_list.cpp @@ -23,9 +23,9 @@ #include #include "masterentity.h" #include "../common/rulesys.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "hate_list.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "zone.h" #include "water_map.h" @@ -40,7 +40,7 @@ HateList::~HateList() { } -// neotokyo: added for frenzy support +// added for frenzy support // checks if target still is in frenzy mode void HateList::CheckFrenzyHate() { @@ -163,14 +163,14 @@ Mob* HateList::GetClosest(Mob *hater) { ++iterator; } - if (close == 0 && hater->IsNPC() || close->DivineAura()) + if ((!close && hater->IsNPC()) || (close && close->DivineAura())) close = hater->CastToNPC()->GetHateTop(); return close; } -// neotokyo: a few comments added, rearranged code for readability +// a few comments added, rearranged code for readability void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAddIfNotExist) { if(!ent) @@ -311,7 +311,7 @@ Mob *HateList::GetTop(Mob *center) } } - if (cur->ent->Sanctuary()) { + if (cur->ent->Sanctuary()) { if(hate == -1) { top = cur->ent; @@ -403,6 +403,13 @@ Mob *HateList::GetTop(Mob *center) } } + if (!isTopClientType) { + if (top->GetSpecialAbility(ALLOW_TO_TANK)){ + isTopClientType = true; + topClientTypeInRange = top; + } + } + if(!isTopClientType) return topClientTypeInRange ? topClientTypeInRange : nullptr; @@ -557,12 +564,15 @@ int HateList::AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOption return ret; } -void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) +void HateList::SpellCast(Mob *caster, uint32 spell_id, float range, Mob* ae_center) { if(!caster) - { return; - } + + Mob* center = caster; + + if (ae_center) + center = ae_center; //this is slower than just iterating through the list but avoids //crashes when people kick the bucket in the middle of this call @@ -578,7 +588,7 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range) tHateEntry *h = (*iterator); if(range > 0) { - dist_targ = caster->DistNoRoot(*h->ent); + dist_targ = center->DistNoRoot(*h->ent); if(dist_targ <= range && dist_targ >= min_range2) { id_list.push_back(h->ent->GetID()); diff --git a/zone/hate_list.h b/zone/hate_list.h index a3e6c6a88..4ea8d0bd6 100644 --- a/zone/hate_list.h +++ b/zone/hate_list.h @@ -63,7 +63,7 @@ public: int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts); - void SpellCast(Mob *caster, uint32 spell_id, float range); + void SpellCast(Mob *caster, uint32 spell_id, float range, Mob *ae_center = nullptr); bool IsEmpty(); void PrintToClient(Client *c); diff --git a/zone/horse.cpp b/zone/horse.cpp index 04c826df0..df7c7e7c1 100644 --- a/zone/horse.cpp +++ b/zone/horse.cpp @@ -18,9 +18,9 @@ #include "../common/debug.h" #include "masterentity.h" -#include "../common/Item.h" +#include "../common/item.h" #include "../common/linked_list.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include #include #include "worldserver.h" @@ -67,70 +67,51 @@ const NPCType *Horse::GetHorseType(uint16 spell_id) { const NPCType *Horse::BuildHorseType(uint16 spell_id) { - const char* FileName = spells[spell_id].teleport_zone; + const char* fileName = spells[spell_id].teleport_zone; - char mount_color = 0; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT race,gender,texture,mountspeed FROM horses WHERE filename='%s'", FileName), errbuf, &result)) { - - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - - row = mysql_fetch_row(result); - - NPCType* npc_type = new NPCType; - memset(npc_type, 0, sizeof(NPCType)); - strcpy(npc_type->name,"Unclaimed_Mount"); //this should never get used - - strcpy(npc_type->special_abilities, "19,1^20,1^24,1"); - npc_type->cur_hp = 1; - npc_type->max_hp = 1; - npc_type->race = atoi(row[0]); - npc_type->gender = atoi(row[1]); // Drogmor's are female horses. Yuck. - npc_type->class_ = 1; - npc_type->deity= 1; - npc_type->level = 1; - npc_type->npc_id = 0; - npc_type->loottable_id = 0; - npc_type->texture = atoi(row[2]); - npc_type->helmtexture = atoi(row[2]); - npc_type->runspeed = atof(row[3]); - - mount_color = atoi(row[2]); - - npc_type->light = 0; - npc_type->STR = 75; - npc_type->STA = 75; - npc_type->DEX = 75; - npc_type->AGI = 75; - npc_type->INT = 75; - npc_type->WIS = 75; - npc_type->CHA = 75; - - horses_auto_delete.Insert(npc_type); - - mysql_free_result(result); - return(npc_type); - } - else { - LogFile->write(EQEMuLog::Error, "No Database entry for mount: %s, check the horses table", FileName); - //Message(13, "Unable to find data for mount %s", FileName); - safe_delete_array(query); - } - mysql_free_result(result); - return nullptr; - } - else { - LogFile->write(EQEMuLog::Error, "Error in Mount query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT race, gender, texture, mountspeed FROM horses WHERE filename = '%s'", fileName); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Mount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return nullptr; } + if (results.RowCount() != 1) { + LogFile->write(EQEMuLog::Error, "No Database entry for mount: %s, check the horses table", fileName); + return nullptr; + } + + auto row = results.begin(); + + NPCType* npc_type = new NPCType; + memset(npc_type, 0, sizeof(NPCType)); + strcpy(npc_type->name,"Unclaimed_Mount"); //this should never get used + + strcpy(npc_type->special_abilities, "19,1^20,1^24,1"); + npc_type->cur_hp = 1; + npc_type->max_hp = 1; + npc_type->race = atoi(row[0]); + npc_type->gender = atoi(row[1]); // Drogmor's are female horses. Yuck. + npc_type->class_ = 1; + npc_type->deity= 1; + npc_type->level = 1; + npc_type->npc_id = 0; + npc_type->loottable_id = 0; + npc_type->texture = atoi(row[2]); // mount color + npc_type->helmtexture = atoi(row[2]); // mount color + npc_type->runspeed = atof(row[3]); + + npc_type->light = 0; + npc_type->STR = 75; + npc_type->STA = 75; + npc_type->DEX = 75; + npc_type->AGI = 75; + npc_type->INT = 75; + npc_type->WIS = 75; + npc_type->CHA = 75; + horses_auto_delete.Insert(npc_type); + + return npc_type; } void Client::SummonHorse(uint16 spell_id) { diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 9a815c6ca..6b7a0b297 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -15,24 +15,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "../common/debug.h" -#include "masterentity.h" -#include "worldserver.h" -#include "net.h" -#include "zonedb.h" -#include "../common/spdat.h" -#include "../common/packet_dump.h" -#include "../common/packet_functions.h" -#include "petitions.h" -#include "../common/serverinfo.h" -#include "../common/ZoneNumbers.h" -#include "../common/moremath.h" -#include "../common/guilds.h" #include "../common/logsys.h" -#include "../common/StringUtil.h" -#include "StringIDs.h" -#include "NpcAI.h" -#include "QuestParserCollection.h" +#include "../common/string_util.h" +#include "quest_parser_collection.h" +#include "worldserver.h" +#include "zonedb.h" + extern WorldServer worldserver; // @merth: this needs to be touched up @@ -199,7 +189,9 @@ bool Client::CheckLoreConflict(const Item_Struct* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != INVALID_INDEX); } -bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot) { +bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5, bool attuned, uint16 to_slot, uint32 ornament_icon, uint32 ornament_idfile) { + this->EVENT_ITEM_ScriptStopReturn(); + // TODO: update calling methods and script apis to handle a failure return const Item_Struct* item = database.GetItem(item_id); @@ -555,6 +547,11 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, // attune item if(attuned && inst->GetItem()->Attuneable) inst->SetInstNoDrop(true); + + if(ornament_icon > 0 && ornament_idfile > 0) { + inst->SetOrnamentIcon(ornament_icon); + inst->SetOrnamentationIDFile(ornament_idfile); + } // check to see if item is usable in requested slot if(enforceusable && (((to_slot >= MainCharm) && (to_slot <= MainAmmo)) || (to_slot == MainPowerSource))) { @@ -813,25 +810,24 @@ bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update) return database.SaveCursor(CharacterID(), s, e); } -bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update) -{ +bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update) { mlog(INVENTORY__SLOTS, "Putting item %s (%d) into slot %d", inst.GetItem()->Name, inst.GetItem()->ID, slot_id); + if (slot_id == MainCursor) - { - return PushItemOnCursor(inst,client_update); - } + return PushItemOnCursor(inst, client_update); else m_inv.PutItem(slot_id, inst); - if (client_update) { - SendItemPacket(slot_id, &inst, (slot_id == MainCursor) ? ItemPacketSummonItem : ItemPacketTrade); - } + if (client_update) + SendItemPacket(slot_id, &inst, ((slot_id == MainCursor) ? ItemPacketSummonItem : ItemPacketTrade)); if (slot_id == MainCursor) { - std::list::const_iterator s=m_inv.cursor_begin(),e=m_inv.cursor_end(); + std::list::const_iterator s = m_inv.cursor_begin(), e = m_inv.cursor_end(); return database.SaveCursor(this->CharacterID(), s, e); - } else + } + else { return database.SaveInventory(this->CharacterID(), &inst, slot_id); + } CalcBonuses(); } @@ -857,7 +853,7 @@ void Client::PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootI { if(bag_item_data[i] == nullptr) continue; - const ItemInst *bagitem = database.CreateItem(bag_item_data[i]->item_id, bag_item_data[i]->charges, bag_item_data[i]->aug1, bag_item_data[i]->aug2, bag_item_data[i]->aug3, bag_item_data[i]->aug4, bag_item_data[i]->aug5); + const ItemInst *bagitem = database.CreateItem(bag_item_data[i]->item_id, bag_item_data[i]->charges, bag_item_data[i]->aug_1, bag_item_data[i]->aug_2, bag_item_data[i]->aug_3, bag_item_data[i]->aug_4, bag_item_data[i]->aug_5); interior_slot = Inventory::CalcSlotId(slot_id, i); mlog(INVENTORY__SLOTS, "Putting bag loot item %s (%d) into slot %d (bag slot %d)", inst.GetItem()->Name, inst.GetItem()->ID, interior_slot, i); PutLootInInventory(interior_slot, *bagitem); @@ -1479,6 +1475,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { } world_inst->SetCharges(world_charges); + m_tradeskill_object->PutItem(world_idx, world_inst); m_tradeskill_object->Save(); if (src_charges == 0) { @@ -1530,7 +1527,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) { // Also sends trade information to other client of trade session if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit - trade->AddEntity(src_slot_id, dst_slot_id); + trade->AddEntity(dst_slot_id, move_in->number_in_stack); return true; } else { @@ -1869,7 +1866,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; @@ -1890,7 +1889,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) { @@ -1994,11 +1993,10 @@ bool Client::DecreaseByID(uint32 type, uint8 amt) { } void Client::RemoveNoRent(bool client_update) { + int16 slot_id = 0; - int16 slot_id; - - // personal - for(slot_id = MAIN_BEGIN; slot_id < EmuConstants::MAP_POSSESSIONS_SIZE; slot_id++) { + // equipment + for(slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; slot_id++) { const ItemInst* inst = m_inv[slot_id]; if(inst && !inst->GetItem()->NoRent) { mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); @@ -2006,11 +2004,22 @@ void Client::RemoveNoRent(bool client_update) { } } + // general + for (slot_id = EmuConstants::GENERAL_BEGIN; slot_id <= EmuConstants::GENERAL_END; slot_id++) { + const ItemInst* inst = m_inv[slot_id]; + if (inst && !inst->GetItem()->NoRent) { + mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + DeleteItemInInventory(slot_id, 0, client_update); + } + } + // power source - const ItemInst* inst = m_inv[MainPowerSource]; - if(inst && !inst->GetItem()->NoRent) { - mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, MainPowerSource); - DeleteItemInInventory(MainPowerSource, 0, (GetClientVersion() >= EQClientSoF) ? client_update : false); // Ti slot non-existent + if (m_inv[MainPowerSource]) { + const ItemInst* inst = m_inv[MainPowerSource]; + if (inst && !inst->GetItem()->NoRent) { + mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from slot %i", inst->GetItem()->Name, MainPowerSource); + DeleteItemInInventory(MainPowerSource, 0, (GetClientVersion() >= EQClientSoF) ? client_update : false); // Ti slot non-existent + } } // containers @@ -2057,15 +2066,42 @@ void Client::RemoveNoRent(bool client_update) { DeleteItemInInventory(slot_id, 0, false); // Can't delete from client Shared Bank Container slots } } + + // cursor & limbo + if (!m_inv.CursorEmpty()) { + std::list local; + ItemInst* inst = nullptr; + + while (!m_inv.CursorEmpty()) { + inst = m_inv.PopItem(MainCursor); + if (inst) + local.push_back(inst); + } + + std::list::iterator iter = local.begin(); + while (iter != local.end()) { + inst = *iter; + if (!inst->GetItem()->NoRent) + mlog(INVENTORY__SLOTS, "NoRent Timer Lapse: Deleting %s from `Limbo`", inst->GetItem()->Name); + else + m_inv.PushCursor(**iter); + + safe_delete(*iter); + iter = local.erase(iter); + } + + std::list::const_iterator s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + database.SaveCursor(this->CharacterID(), s, e); + local.clear(); + } } // Two new methods to alleviate perpetual login desyncs void Client::RemoveDuplicateLore(bool client_update) { - // Split-charge stacking may be added at some point -U - int16 slot_id; + int16 slot_id = 0; - // personal - for(slot_id = MAIN_BEGIN; slot_id < EmuConstants::MAP_POSSESSIONS_SIZE; slot_id++) { + // equipment + for(slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; slot_id++) { ItemInst* inst = m_inv.PopItem(slot_id); if(inst) { if(CheckLoreConflict(inst->GetItem())) { @@ -2079,17 +2115,34 @@ void Client::RemoveDuplicateLore(bool client_update) { } } + // general + for (slot_id = EmuConstants::GENERAL_BEGIN; slot_id <= EmuConstants::GENERAL_END; slot_id++) { + ItemInst* inst = m_inv.PopItem(slot_id); + if (inst) { + if (CheckLoreConflict(inst->GetItem())) { + mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + database.SaveInventory(character_id, nullptr, slot_id); + } + else { + m_inv.PutItem(slot_id, *inst); + } + safe_delete(inst); + } + } + // power source - ItemInst* inst = m_inv.PopItem(MainPowerSource); - if(inst) { - if(CheckLoreConflict(inst->GetItem())) { - mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); - database.SaveInventory(character_id, nullptr, MainPowerSource); + if (m_inv[MainPowerSource]) { + ItemInst* inst = m_inv.PopItem(MainPowerSource); + if (inst) { + if (CheckLoreConflict(inst->GetItem())) { + mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from slot %i", inst->GetItem()->Name, slot_id); + database.SaveInventory(character_id, nullptr, MainPowerSource); + } + else { + m_inv.PutItem(MainPowerSource, *inst); + } + safe_delete(inst); } - else { - m_inv.PutItem(MainPowerSource, *inst); - } - safe_delete(inst); } // containers @@ -2138,11 +2191,56 @@ void Client::RemoveDuplicateLore(bool client_update) { } // Shared Bank and Shared Bank Containers are not checked due to their allowing duplicate lore items -U + + // cursor & limbo + if (!m_inv.CursorEmpty()) { + std::list local; + ItemInst* inst = nullptr; + + while (!m_inv.CursorEmpty()) { + inst = m_inv.PopItem(MainCursor); + if (inst) + local.push_back(inst); + } + + std::list::iterator iter = local.begin(); + while (iter != local.end()) { + inst = *iter; + if (CheckLoreConflict(inst->GetItem())) { + mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from `Limbo`", inst->GetItem()->Name); + safe_delete(*iter); + iter = local.erase(iter); + } + else { + ++iter; + } + } + + iter = local.begin(); + while (iter != local.end()) { + inst = *iter; + if (!inst->GetItem()->LoreFlag || + ((inst->GetItem()->LoreGroup == -1) && (m_inv.HasItem(inst->GetID(), 0, invWhereCursor) == INVALID_INDEX)) || + (inst->GetItem()->LoreGroup && ~inst->GetItem()->LoreGroup && (m_inv.HasItemByLoreGroup(inst->GetItem()->LoreGroup, invWhereCursor) == INVALID_INDEX))) { + + m_inv.PushCursor(**iter); + } + else { + mlog(INVENTORY__ERROR, "Lore Duplication Error: Deleting %s from `Limbo`", inst->GetItem()->Name); + } + + safe_delete(*iter); + iter = local.erase(iter); + } + + std::list::const_iterator s = m_inv.cursor_begin(), e = m_inv.cursor_end(); + database.SaveCursor(this->CharacterID(), s, e); + local.clear(); + } } void Client::MoveSlotNotAllowed(bool client_update) { - - int16 slot_id; + int16 slot_id = 0; // equipment for(slot_id = EmuConstants::EQUIPMENT_BEGIN; slot_id <= EmuConstants::EQUIPMENT_END; slot_id++) { @@ -2310,10 +2408,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++) { @@ -2324,6 +2420,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); @@ -2331,21 +2428,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) { @@ -2634,6 +2727,211 @@ bool Client::MoveItemToInventory(ItemInst *ItemToReturn, bool UpdateClient) { return true; } +bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool allowtrip, bool& error, bool autolog) +{ + if (!requester) + return false; + + std::map instmap; + + // build reference map + for (int16 index = MAIN_BEGIN; index < EmuConstants::MAP_POSSESSIONS_SIZE; ++index) + if (m_inv[index]) + instmap[index] = m_inv[index]; + for (int16 index = EmuConstants::TRIBUTE_BEGIN; index <= EmuConstants::TRIBUTE_END; ++index) + if (m_inv[index]) + instmap[index] = m_inv[index]; + for (int16 index = EmuConstants::BANK_BEGIN; index <= EmuConstants::BANK_END; ++index) + if (m_inv[index]) + instmap[index] = m_inv[index]; + for (int16 index = EmuConstants::SHARED_BANK_BEGIN; index <= EmuConstants::SHARED_BANK_END; ++index) + if (m_inv[index]) + instmap[index] = m_inv[index]; + for (int16 index = EmuConstants::TRADE_BEGIN; index <= EmuConstants::TRADE_END; ++index) + if (m_inv[index]) + instmap[index] = m_inv[index]; + + if (Object* tsobject = GetTradeskillObject()) + for (int16 index = MAIN_BEGIN; index < EmuConstants::MAP_WORLD_SIZE; ++index) + if (tsobject->GetItem(index)) + instmap[EmuConstants::WORLD_BEGIN + index] = tsobject->GetItem(index); + + int limbo = 0; + for (iter_queue cursor_itr = m_inv.cursor_begin(); cursor_itr != m_inv.cursor_end(); ++cursor_itr, ++limbo) { + if (cursor_itr == m_inv.cursor_begin()) // m_inv.cursor_begin() is referenced as MainCursor in MapPossessions above + continue; + + instmap[8000 + limbo] = *cursor_itr; + } + + if (m_inv[MainPowerSource]) + instmap[MainPowerSource] = m_inv[MainPowerSource]; + + // call InterrogateInventory_ for error check + for (std::map::iterator instmap_itr = instmap.begin(); (instmap_itr != instmap.end()) && (!error); ++instmap_itr) + InterrogateInventory_(true, requester, instmap_itr->first, INVALID_INDEX, instmap_itr->second, nullptr, log, silent, error, 0); + + if (autolog && error && (!log)) + log = true; + + if (log) + _log(INVENTORY__ERROR, "Client::InterrogateInventory() called for %s by %s with an error state of %s", GetName(), requester->GetName(), (error ? "TRUE" : "FALSE")); + if (!silent) + requester->Message(1, "--- Inventory Interrogation Report for %s (requested by: %s, error state: %s) ---", GetName(), requester->GetName(), (error ? "TRUE" : "FALSE")); + + // call InterrogateInventory_ for report + for (std::map::iterator instmap_itr = instmap.begin(); (instmap_itr != instmap.end()); ++instmap_itr) + InterrogateInventory_(false, requester, instmap_itr->first, INVALID_INDEX, instmap_itr->second, nullptr, log, silent, error, 0); + + if (error) { + Message(13, "An error has been discovered in your inventory!"); + Message(13, "Do not log out, zone or re-arrange items until this"); + Message(13, "issue has been resolved or item loss may occur!"); + + if (allowtrip) + TripInterrogateInvState(); + } + + if (log) { + _log(INVENTORY__ERROR, "Target interrogate inventory flag: %s", (GetInterrogateInvState() ? "TRUE" : "FALSE")); + _log(INVENTORY__ERROR, "Client::InterrogateInventory() -- End"); + } + if (!silent) { + requester->Message(1, "Target interrogation flag: %s", (GetInterrogateInvState() ? "TRUE" : "FALSE")); + requester->Message(1, "--- End of Interrogation Report ---"); + } + + instmap.clear(); + + return true; +} + +void Client::InterrogateInventory_(bool errorcheck, Client* requester, int16 head, int16 index, const ItemInst* inst, const ItemInst* parent, bool log, bool silent, bool &error, int depth) +{ + if (depth >= 10) { + _log(INVENTORY__ERROR, "Client::InterrogateInventory_() - Recursion count has exceeded the maximum allowable (You have a REALLY BIG PROBLEM!!)"); + return; + } + + if (errorcheck) { + if (InterrogateInventory_error(head, index, inst, parent, depth)) { + error = true; + } + else { + if (inst) + for (int16 sub = SUB_BEGIN; (sub < EmuConstants::ITEM_CONTAINER_SIZE) && (!error); ++sub) // treat any ItemInst as having the max internal slots available + if (inst->GetItem(sub)) + InterrogateInventory_(true, requester, head, sub, inst->GetItem(sub), inst, log, silent, error, depth + 1); + } + } + else { + bool localerror = InterrogateInventory_error(head, index, inst, parent, depth); + std::string i; + std::string p; + std::string e; + + if (inst) { i = StringFormat("%s (class: %u | augtype: %u)", inst->GetItem()->Name, inst->GetItem()->ItemClass, inst->GetItem()->AugType); } + else { i = "NONE"; } + if (parent) { p = StringFormat("%s (class: %u | augtype: %u), index: %i", parent->GetItem()->Name, parent->GetItem()->ItemClass, parent->GetItem()->AugType, index); } + else { p = "NONE"; } + if (localerror) { e = " [ERROR]"; } + else { e = ""; } + + if (log) + _log(INVENTORY__ERROR, "Head: %i, Depth: %i, Instance: %s, Parent: %s%s", + head, depth, i.c_str(), p.c_str(), e.c_str()); + if (!silent) + requester->Message(1, "%i:%i - inst: %s - parent: %s%s", + head, depth, i.c_str(), p.c_str(), e.c_str()); + + if (inst) + for (int16 sub = SUB_BEGIN; (sub < EmuConstants::ITEM_CONTAINER_SIZE); ++sub) + if (inst->GetItem(sub)) + InterrogateInventory_(false, requester, head, sub, inst->GetItem(sub), inst, log, silent, error, depth + 1); + } + + return; +} + +bool Client::InterrogateInventory_error(int16 head, int16 index, const ItemInst* inst, const ItemInst* parent, int depth) +{ + // very basic error checking - can be elaborated upon if more in-depth testing is needed... + + if ( + (head >= EmuConstants::EQUIPMENT_BEGIN && head <= EmuConstants::EQUIPMENT_END) || + (head >= EmuConstants::TRIBUTE_BEGIN && head <= EmuConstants::TRIBUTE_END) || + (head >= EmuConstants::WORLD_BEGIN && head <= EmuConstants::WORLD_END) || + (head >= 8000 && head <= 8101) || + (head == MainPowerSource)) { + switch (depth) + { + case 0: // requirement: inst is extant + if (!inst) + return true; + break; + case 1: // requirement: parent is common and inst is augment + if ((!parent) || (!inst)) + return true; + if (!parent->IsType(ItemClassCommon)) + return true; + if (index >= EmuConstants::ITEM_COMMON_SIZE) + return true; + break; + default: // requirement: none (something bad happened...) + return true; + } + } + else if ( + (head >= EmuConstants::GENERAL_BEGIN && head <= EmuConstants::GENERAL_END) || + (head == MainCursor) || + (head >= EmuConstants::BANK_BEGIN && head <= EmuConstants::BANK_END) || + (head >= EmuConstants::SHARED_BANK_BEGIN && head <= EmuConstants::SHARED_BANK_END) || + (head >= EmuConstants::TRADE_BEGIN && head <= EmuConstants::TRADE_END)) { + switch (depth) + { + case 0: // requirement: inst is extant + if (!inst) + return true; + break; + case 1: // requirement: parent is common and inst is augment ..or.. parent is container and inst is extant + if ((!parent) || (!inst)) + return true; + if (parent->IsType(ItemClassContainer)) + break; + if (parent->IsType(ItemClassBook)) + return true; + if (parent->IsType(ItemClassCommon)) { + if (!(inst->GetItem()->AugType > 0)) + return true; + if (index >= EmuConstants::ITEM_COMMON_SIZE) + return true; + } + break; + case 2: // requirement: parent is common and inst is augment + if ((!parent) || (!inst)) + return true; + if (parent->IsType(ItemClassContainer)) + return true; + if (parent->IsType(ItemClassBook)) + return true; + if (parent->IsType(ItemClassCommon)) { + if (!(inst->GetItem()->AugType > 0)) + return true; + if (index >= EmuConstants::ITEM_COMMON_SIZE) + return true; + } + break; + default: // requirement: none (something bad happened again...) + return true; + } + } + else { + return true; + } + + return false; +} + void Inventory::SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, std::string value) { ItemInst *inst = GetItem(slot_id); if(inst) { diff --git a/zone/loottables.cpp b/zone/loottables.cpp index 41de7c6eb..121dad8c5 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -22,7 +22,8 @@ #include "npc.h" #include "masterentity.h" #include "zonedb.h" -#include "../common/MiscFunctions.h" +#include "../common/loottable.h" +#include "../common/misc_functions.h" #ifdef _WINDOWS #define snprintf _snprintf #endif @@ -195,13 +196,13 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge item->item_id = item2->ID; item->charges = charges; - item->aug1 = 0; - item->aug2 = 0; - item->aug3 = 0; - item->aug4 = 0; - item->aug5 = 0; - item->minlevel = minlevel; - item->maxlevel = maxlevel; + item->aug_1 = 0; + item->aug_2 = 0; + item->aug_3 = 0; + item->aug_4 = 0; + item->aug_5 = 0; + item->min_level = minlevel; + item->max_level = maxlevel; if (equipit) { uint8 eslot = 0xFF; char newid[20]; @@ -339,7 +340,7 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge if (found) { CalcBonuses(); // This is less than ideal for bulk adding of items } - item->equipSlot = item2->Slots; + item->equip_slot = item2->Slots; } if(itemlist != nullptr) diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index a1af1b499..22e38b21d 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -235,19 +235,24 @@ void Lua_Client::SetBindPoint(int to_zone) { self->SetBindPoint(to_zone); } -void Lua_Client::SetBindPoint(int to_zone, float new_x) { +void Lua_Client::SetBindPoint(int to_zone, int to_instance) { Lua_Safe_Call_Void(); - self->SetBindPoint(to_zone, new_x); + self->SetBindPoint(to_zone, to_instance); } -void Lua_Client::SetBindPoint(int to_zone, float new_x, float new_y) { +void Lua_Client::SetBindPoint(int to_zone, int to_instance, float new_x) { Lua_Safe_Call_Void(); - self->SetBindPoint(to_zone, new_x, new_y); + self->SetBindPoint(to_zone, to_instance, new_x); } -void Lua_Client::SetBindPoint(int to_zone, float new_x, float new_y, float new_z) { +void Lua_Client::SetBindPoint(int to_zone, int to_instance, float new_x, float new_y) { Lua_Safe_Call_Void(); - self->SetBindPoint(to_zone, new_x, new_y, new_z); + self->SetBindPoint(to_zone, to_instance, new_x, new_y); +} + +void Lua_Client::SetBindPoint(int to_zone, int to_instance, float new_x, float new_y, float new_z) { + Lua_Safe_Call_Void(); + self->SetBindPoint(to_zone, to_instance, new_x, new_y, new_z); } float Lua_Client::GetBindX() { @@ -1134,7 +1139,7 @@ void Lua_Client::Signal(uint32 id) { void Lua_Client::AddAlternateCurrencyValue(uint32 currency, int amount) { Lua_Safe_Call_Void(); - self->AddAlternateCurrencyValue(currency, amount); + self->AddAlternateCurrencyValue(currency, amount, 1); } void Lua_Client::SendWebLink(const char *site) { @@ -1239,6 +1244,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(); @@ -1292,9 +1302,10 @@ luabind::scope lua_register_client() { .def("SetEXP", (void(Lua_Client::*)(uint32,uint32,bool))&Lua_Client::SetEXP) .def("SetBindPoint", (void(Lua_Client::*)(void))&Lua_Client::SetBindPoint) .def("SetBindPoint", (void(Lua_Client::*)(int))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,float))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,float,float))&Lua_Client::SetBindPoint) - .def("SetBindPoint", (void(Lua_Client::*)(int,float,float,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float))&Lua_Client::SetBindPoint) + .def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float, float))&Lua_Client::SetBindPoint) .def("GetBindX", (float(Lua_Client::*)(void))&Lua_Client::GetBindX) .def("GetBindX", (float(Lua_Client::*)(int))&Lua_Client::GetBindX) .def("GetBindY", (float(Lua_Client::*)(void))&Lua_Client::GetBindY) @@ -1492,6 +1503,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..e2b0a6614 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -73,9 +73,10 @@ public: void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp); void SetBindPoint(); void SetBindPoint(int to_zone); - void SetBindPoint(int to_zone, float new_x); - void SetBindPoint(int to_zone, float new_x, float new_y); - void SetBindPoint(int to_zone, float new_x, float new_y, float new_z); + void SetBindPoint(int to_zone, int to_instance); + void SetBindPoint(int to_zone, int to_instance, float new_x); + void SetBindPoint(int to_zone, int to_instance, float new_x, float new_y); + void SetBindPoint(int to_zone, int to_instance, float new_x, float new_y, float new_z); float GetBindX(); float GetBindX(int index); float GetBindY(); @@ -275,6 +276,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_corpse.cpp b/zone/lua_corpse.cpp index 6498458ae..789555a54 100644 --- a/zone/lua_corpse.cpp +++ b/zone/lua_corpse.cpp @@ -39,12 +39,12 @@ void Lua_Corpse::ResetLooter() { uint32 Lua_Corpse::GetDBID() { Lua_Safe_Call_Int(); - return self->GetDBID(); + return self->GetCorpseDBID(); } bool Lua_Corpse::IsRezzed() { Lua_Safe_Call_Bool(); - return self->Rezzed(); + return self->IsRezzed(); } const char* Lua_Corpse::GetOwnerName() { @@ -114,12 +114,12 @@ void Lua_Corpse::SetDecayTimer(uint32 decaytime) { bool Lua_Corpse::CanMobLoot(int charid) { Lua_Safe_Call_Bool(); - return self->CanMobLoot(charid); + return self->CanPlayerLoot(charid); } void Lua_Corpse::AllowMobLoot(Lua_Mob them, uint8 slot) { Lua_Safe_Call_Void(); - self->AllowMobLoot(them, slot); + self->AllowPlayerLoot(them, slot); } bool Lua_Corpse::Summon(Lua_Client client, bool spell, bool checkdistance) { diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index 32bd9a89d..eace84a69 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -416,6 +416,11 @@ void Lua_EntityList::SignalAllClients(int signal) { self->SignalAllClients(signal); } +void Lua_EntityList::ChannelMessage(Lua_Mob from, int channel_num, int language, const char *message) { + Lua_Safe_Call_Void(); + self->ChannelMessage(from, channel_num, language, message); +} + luabind::scope lua_register_entity_list() { return luabind::class_("EntityList") .def(luabind::constructor<>()) @@ -479,7 +484,8 @@ luabind::scope lua_register_entity_list() { .def("GetObjectList", (Lua_Object_List(Lua_EntityList::*)(void))&Lua_EntityList::GetObjectList) .def("GetDoorsList", (Lua_Doors_List(Lua_EntityList::*)(void))&Lua_EntityList::GetDoorsList) .def("GetSpawnList", (Lua_Spawn_List(Lua_EntityList::*)(void))&Lua_EntityList::GetSpawnList) - .def("SignalAllClients", (void(Lua_EntityList::*)(int))&Lua_EntityList::SignalAllClients); + .def("SignalAllClients", (void(Lua_EntityList::*)(int))&Lua_EntityList::SignalAllClients) + .def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob,int,int,const char*))&Lua_EntityList::ChannelMessage); } luabind::scope lua_register_mob_list() { diff --git a/zone/lua_entity_list.h b/zone/lua_entity_list.h index dc9c1a5ed..823499f24 100644 --- a/zone/lua_entity_list.h +++ b/zone/lua_entity_list.h @@ -106,6 +106,7 @@ public: Lua_Doors_List GetDoorsList(); Lua_Spawn_List GetSpawnList(); void SignalAllClients(int signal); + void ChannelMessage(Lua_Mob from, int channel_num, int language, const char *message); }; #endif diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 508112f4a..09b0dcf53 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -13,9 +13,9 @@ #include "lua_client.h" #include "lua_npc.h" #include "lua_entity_list.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "questmgr.h" -#include "QGlobals.h" +#include "qglobals.h" #include "../common/timer.h" struct Events { }; @@ -32,18 +32,94 @@ struct lua_registered_event { }; extern std::map> lua_encounter_events_registered; +extern std::map lua_encounters_loaded; extern void MapOpcodes(); extern void ClearMappedOpcode(EmuOpcode op); +void unregister_event(std::string package_name, std::string name, int evt); + void load_encounter(std::string name) { + if(lua_encounters_loaded.count(name) > 0) + return; + + lua_encounters_loaded[name] = true; parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0); } +void load_encounter_with_data(std::string name, std::string info_str) { + if(lua_encounters_loaded.count(name) > 0) + return; + + lua_encounters_loaded[name] = true; + std::vector info_ptrs; + info_ptrs.push_back(&info_str); + parse->EventEncounter(EVENT_ENCOUNTER_LOAD, name, 0, &info_ptrs); +} + void unload_encounter(std::string name) { + if(lua_encounters_loaded.count(name) == 0) + return; + + auto liter = lua_encounter_events_registered.begin(); + while(liter != lua_encounter_events_registered.end()) { + std::list &elist = liter->second; + auto iter = elist.begin(); + while(iter != elist.end()) { + if((*iter).encounter_name.compare(name) == 0) { + iter = elist.erase(iter); + } else { + ++iter; + } + } + + if(elist.size() == 0) { + lua_encounter_events_registered.erase(liter++); + } else { + ++liter; + } + } + + lua_encounters_loaded.erase(name); parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0); } +void unload_encounter_with_data(std::string name, std::string info_str) { + if(lua_encounters_loaded.count(name) == 0) + return; + + auto liter = lua_encounter_events_registered.begin(); + while(liter != lua_encounter_events_registered.end()) { + std::list &elist = liter->second; + auto iter = elist.begin(); + while(iter != elist.end()) { + if((*iter).encounter_name.compare(name) == 0) { + iter = elist.erase(iter); + } + else { + ++iter; + } + } + + if(elist.size() == 0) { + lua_encounter_events_registered.erase(liter++); + } + else { + ++liter; + } + } + + lua_encounters_loaded.erase(name); + std::vector info_ptrs; + info_ptrs.push_back(&info_str); + parse->EventEncounter(EVENT_ENCOUNTER_UNLOAD, name, 0, &info_ptrs); +} + void register_event(std::string package_name, std::string name, int evt, luabind::adl::object func) { + if(lua_encounters_loaded.count(name) == 0) + return; + + unregister_event(package_name, name, evt); + lua_registered_event e; e.encounter_name = name; e.lua_reference = func; @@ -55,18 +131,8 @@ void register_event(std::string package_name, std::string name, int evt, luabind elist.push_back(e); lua_encounter_events_registered[package_name] = elist; } else { - std::list elist = liter->second; - auto iter = elist.begin(); - while(iter != elist.end()) { - if(iter->event_id == evt && iter->encounter_name.compare(name) == 0) { - //already registered this event for this encounter - return; - } - ++iter; - } - + std::list &elist = liter->second; elist.push_back(e); - lua_encounter_events_registered[package_name] = elist; } } @@ -78,7 +144,9 @@ void unregister_event(std::string package_name, std::string name, int evt) { while(iter != elist.end()) { if(iter->event_id == evt && iter->encounter_name.compare(name) == 0) { iter = elist.erase(iter); + break; } + ++iter; } lua_encounter_events_registered[package_name] = elist; } @@ -93,6 +161,11 @@ void register_npc_event(std::string name, int evt, int npc_id, luabind::adl::obj } } +void register_npc_event(int evt, int npc_id, luabind::adl::object func) { + std::string name = quest_manager.GetEncounter(); + register_npc_event(name, evt, npc_id, func); +} + void unregister_npc_event(std::string name, int evt, int npc_id) { std::stringstream package_name; package_name << "npc_" << npc_id; @@ -100,16 +173,31 @@ void unregister_npc_event(std::string name, int evt, int npc_id) { unregister_event(package_name.str(), name, evt); } +void unregister_npc_event(int evt, int npc_id) { + std::string name = quest_manager.GetEncounter(); + unregister_npc_event(name, evt, npc_id); +} + void register_player_event(std::string name, int evt, luabind::adl::object func) { if(luabind::type(func) == LUA_TFUNCTION) { register_event("player", name, evt, func); } } +void register_player_event(int evt, luabind::adl::object func) { + std::string name = quest_manager.GetEncounter(); + register_player_event(name, evt, func); +} + void unregister_player_event(std::string name, int evt) { unregister_event("player", name, evt); } +void unregister_player_event(int evt) { + std::string name = quest_manager.GetEncounter(); + unregister_player_event(name, evt); +} + void register_item_event(std::string name, int evt, int item_id, luabind::adl::object func) { std::string package_name = "item_"; package_name += std::to_string(static_cast(item_id)); @@ -119,6 +207,11 @@ void register_item_event(std::string name, int evt, int item_id, luabind::adl::o } } +void register_item_event(int evt, int item_id, luabind::adl::object func) { + std::string name = quest_manager.GetEncounter(); + register_item_event(name, evt, item_id, func); +} + void unregister_item_event(std::string name, int evt, int item_id) { std::string package_name = "item_"; package_name += std::to_string(static_cast(item_id)); @@ -126,6 +219,11 @@ void unregister_item_event(std::string name, int evt, int item_id) { unregister_event(package_name, name, evt); } +void unregister_item_event(int evt, int item_id) { + std::string name = quest_manager.GetEncounter(); + unregister_item_event(name, evt, item_id); +} + void register_spell_event(std::string name, int evt, int spell_id, luabind::adl::object func) { if(luabind::type(func) == LUA_TFUNCTION) { std::stringstream package_name; @@ -135,6 +233,11 @@ void register_spell_event(std::string name, int evt, int spell_id, luabind::adl: } } +void register_spell_event(int evt, int spell_id, luabind::adl::object func) { + std::string name = quest_manager.GetEncounter(); + register_spell_event(name, evt, spell_id, func); +} + void unregister_spell_event(std::string name, int evt, int spell_id) { std::stringstream package_name; package_name << "spell_" << spell_id; @@ -142,6 +245,11 @@ void unregister_spell_event(std::string name, int evt, int spell_id) { unregister_event(package_name.str(), name, evt); } +void unregister_spell_event(int evt, int spell_id) { + std::string name = quest_manager.GetEncounter(); + unregister_spell_event(name, evt, spell_id); +} + Lua_Mob lua_spawn2(int npc_type, int grid, int unused, double x, double y, double z, double heading) { return Lua_Mob(quest_manager.spawn2(npc_type, grid, unused, static_cast(x), static_cast(y), static_cast(z), static_cast(heading))); @@ -1109,6 +1217,11 @@ Lua_ItemInst lua_get_quest_item() { return quest_manager.GetQuestItem(); } +std::string lua_get_encounter() { + return quest_manager.GetEncounter(); +} + + void lua_map_opcodes() { MapOpcodes(); } @@ -1136,19 +1249,175 @@ double lua_clock() { return static_cast(t) / 1000.0; } +#define LuaCreateNPCParse(name, c_type, default_value) do { \ + cur = table[#name]; \ + if(luabind::type(cur) != LUA_TNIL) { \ + try { \ + npc_type->name = luabind::object_cast(cur); \ + } \ + catch(luabind::cast_failed) { \ + npc_type->size = default_value; \ + } \ + } \ + else { \ + npc_type->size = default_value; \ + } \ +} while(0) + +#define LuaCreateNPCParseString(name, str_length, default_value) do { \ + cur = table[#name]; \ + if(luabind::type(cur) != LUA_TNIL) { \ + try { \ + std::string tmp = luabind::object_cast(cur); \ + strncpy(npc_type->name, tmp.c_str(), str_length); \ + } \ + catch(luabind::cast_failed) { \ + strncpy(npc_type->name, default_value, str_length); \ + } \ + } \ + else { \ + strncpy(npc_type->name, default_value, str_length); \ + } \ +} while(0) + +void lua_create_npc(luabind::adl::object table, float x, float y, float z, float heading) { + if(luabind::type(table) != LUA_TTABLE) { + return; + } + + NPCType* npc_type = new NPCType; + memset(npc_type, 0, sizeof(NPCType)); + + + luabind::adl::index_proxy cur = table["name"]; + LuaCreateNPCParseString(name, 64, "_"); + LuaCreateNPCParseString(lastname, 64, ""); + LuaCreateNPCParse(cur_hp, int32, 30); + LuaCreateNPCParse(max_hp, int32, 30); + LuaCreateNPCParse(size, float, 6.0f); + LuaCreateNPCParse(runspeed, float, 1.25f); + LuaCreateNPCParse(gender, uint8, 0); + LuaCreateNPCParse(race, uint16, 1); + LuaCreateNPCParse(class_, uint8, WARRIOR); + LuaCreateNPCParse(bodytype, uint8, 0); + LuaCreateNPCParse(deity, uint8, 0); + LuaCreateNPCParse(level, uint8, 1); + LuaCreateNPCParse(npc_id, uint32, 1); + LuaCreateNPCParse(texture, uint8, 0); + LuaCreateNPCParse(helmtexture, uint8, 0); + LuaCreateNPCParse(loottable_id, uint32, 0); + LuaCreateNPCParse(npc_spells_id, uint32, 0); + LuaCreateNPCParse(npc_spells_effects_id, uint32, 0); + LuaCreateNPCParse(npc_faction_id, int32, 0); + LuaCreateNPCParse(merchanttype, uint32, 0); + LuaCreateNPCParse(alt_currency_type, uint32, 0); + LuaCreateNPCParse(adventure_template, uint32, 0); + LuaCreateNPCParse(trap_template, uint32, 0); + LuaCreateNPCParse(light, uint8, 0); + LuaCreateNPCParse(AC, uint32, 0); + LuaCreateNPCParse(Mana, uint32, 0); + LuaCreateNPCParse(ATK, uint32, 0); + LuaCreateNPCParse(STR, uint32, 75); + LuaCreateNPCParse(STA, uint32, 75); + LuaCreateNPCParse(DEX, uint32, 75); + LuaCreateNPCParse(AGI, uint32, 75); + LuaCreateNPCParse(INT, uint32, 75); + LuaCreateNPCParse(WIS, uint32, 75); + LuaCreateNPCParse(CHA, uint32, 75); + LuaCreateNPCParse(MR, int32, 25); + LuaCreateNPCParse(FR, int32, 25); + LuaCreateNPCParse(CR, int32, 25); + LuaCreateNPCParse(PR, int32, 25); + LuaCreateNPCParse(DR, int32, 25); + LuaCreateNPCParse(Corrup, int32, 25); + LuaCreateNPCParse(PhR, int32, 0); + LuaCreateNPCParse(haircolor, uint8, 0); + LuaCreateNPCParse(beardcolor, uint8, 0); + LuaCreateNPCParse(eyecolor1, uint8, 0); + LuaCreateNPCParse(eyecolor2, uint8, 0); + LuaCreateNPCParse(hairstyle, uint8, 0); + LuaCreateNPCParse(luclinface, uint8, 0); + LuaCreateNPCParse(beard, uint8, 0); + LuaCreateNPCParse(drakkin_heritage, uint32, 0); + LuaCreateNPCParse(drakkin_tattoo, uint32, 0); + LuaCreateNPCParse(drakkin_details, uint32, 0); + LuaCreateNPCParse(armor_tint[0], uint32, 0); + LuaCreateNPCParse(armor_tint[1], uint32, 0); + LuaCreateNPCParse(armor_tint[2], uint32, 0); + LuaCreateNPCParse(armor_tint[3], uint32, 0); + LuaCreateNPCParse(armor_tint[4], uint32, 0); + LuaCreateNPCParse(armor_tint[5], uint32, 0); + LuaCreateNPCParse(armor_tint[6], uint32, 0); + LuaCreateNPCParse(armor_tint[7], uint32, 0); + LuaCreateNPCParse(armor_tint[8], uint32, 0); + LuaCreateNPCParse(min_dmg, uint32, 2); + LuaCreateNPCParse(max_dmg, uint32, 4); + LuaCreateNPCParse(attack_count, int16, 0); + LuaCreateNPCParseString(special_abilities, 512, ""); + LuaCreateNPCParse(d_meele_texture1, uint16, 0); + LuaCreateNPCParse(d_meele_texture2, uint16, 0); + LuaCreateNPCParseString(ammo_idfile, 32, ""); + LuaCreateNPCParse(prim_melee_type, uint8, 0); + LuaCreateNPCParse(sec_melee_type, uint8, 0); + LuaCreateNPCParse(ranged_type, uint8, 0); + LuaCreateNPCParse(hp_regen, int32, 1); + LuaCreateNPCParse(mana_regen, int32, 1); + LuaCreateNPCParse(aggroradius, int32, 0); + LuaCreateNPCParse(assistradius, int32, 0); + LuaCreateNPCParse(see_invis, uint8, 0); + LuaCreateNPCParse(see_invis_undead, bool, false); + LuaCreateNPCParse(see_hide, bool, false); + LuaCreateNPCParse(see_improved_hide, bool, false); + LuaCreateNPCParse(qglobal, bool, false); + LuaCreateNPCParse(npc_aggro, bool, false); + LuaCreateNPCParse(spawn_limit, uint8, false); + LuaCreateNPCParse(mount_color, uint8, false); + LuaCreateNPCParse(attack_speed, float, 0); + LuaCreateNPCParse(attack_delay, uint8, 30); + LuaCreateNPCParse(accuracy_rating, int, 0); + LuaCreateNPCParse(avoidance_rating, int, 0); + LuaCreateNPCParse(findable, bool, false); + LuaCreateNPCParse(trackable, bool, false); + LuaCreateNPCParse(slow_mitigation, int16, 0); + LuaCreateNPCParse(maxlevel, uint8, 0); + LuaCreateNPCParse(scalerate, uint32, 0); + LuaCreateNPCParse(private_corpse, bool, false); + LuaCreateNPCParse(unique_spawn_by_name, bool, false); + LuaCreateNPCParse(underwater, bool, false); + LuaCreateNPCParse(emoteid, uint32, 0); + LuaCreateNPCParse(spellscale, float, 0); + LuaCreateNPCParse(healscale, float, 0); + LuaCreateNPCParse(no_target_hotkey, bool, false); + LuaCreateNPCParse(raid_target, bool, false); + LuaCreateNPCParse(probability, uint8, 0); + + NPC* npc = new NPC(npc_type, nullptr, x, y, z, heading, FlyMode3); + npc->GiveNPCTypeData(npc_type); + entity_list.AddNPC(npc); +} luabind::scope lua_register_general() { return luabind::namespace_("eq") [ luabind::def("load_encounter", &load_encounter), luabind::def("unload_encounter", &unload_encounter), - luabind::def("register_npc_event", ®ister_npc_event), - luabind::def("unregister_npc_event", &unregister_npc_event), - luabind::def("register_player_event", ®ister_player_event), - luabind::def("unregister_player_event", &unregister_player_event), - luabind::def("register_item_event", ®ister_item_event), - luabind::def("unregister_item_event", &unregister_item_event), - luabind::def("register_spell_event", ®ister_spell_event), - luabind::def("unregister_spell_event", &unregister_spell_event), + luabind::def("load_encounter_with_data", &load_encounter_with_data), + luabind::def("unload_encounter_with_data", &unload_encounter_with_data), + luabind::def("register_npc_event", (void(*)(std::string, int, int, luabind::adl::object))®ister_npc_event), + luabind::def("register_npc_event", (void(*)(int, int, luabind::adl::object))®ister_npc_event), + luabind::def("unregister_npc_event", (void(*)(std::string, int, int))&unregister_npc_event), + luabind::def("unregister_npc_event", (void(*)(int, int))&unregister_npc_event), + luabind::def("register_player_event", (void(*)(std::string, int, luabind::adl::object))®ister_player_event), + luabind::def("register_player_event", (void(*)(int, luabind::adl::object))®ister_player_event), + luabind::def("unregister_player_event", (void(*)(std::string, int))&unregister_player_event), + luabind::def("unregister_player_event", (void(*)(int))&unregister_player_event), + luabind::def("register_item_event", (void(*)(std::string, int, int, luabind::adl::object))®ister_item_event), + luabind::def("register_item_event", (void(*)(int, int, luabind::adl::object))®ister_item_event), + luabind::def("unregister_item_event", (void(*)(std::string, int, int))&unregister_item_event), + luabind::def("unregister_item_event", (void(*)(int, int))&unregister_item_event), + luabind::def("register_spell_event", (void(*)(std::string, int, int, luabind::adl::object func))®ister_spell_event), + luabind::def("register_spell_event", (void(*)(int, int, luabind::adl::object func))®ister_spell_event), + luabind::def("unregister_spell_event", (void(*)(std::string, int, int))&unregister_spell_event), + luabind::def("unregister_spell_event", (void(*)(int, int))&unregister_spell_event), luabind::def("spawn2", (Lua_Mob(*)(int,int,int,double,double,double,double))&lua_spawn2), luabind::def("unique_spawn", (Lua_Mob(*)(int,int,int,double,double,double))&lua_unique_spawn), luabind::def("unique_spawn", (Lua_Mob(*)(int,int,int,double,double,double,double))&lua_unique_spawn), @@ -1306,12 +1575,14 @@ luabind::scope lua_register_general() { luabind::def("get_initiator", &lua_get_initiator), luabind::def("get_owner", &lua_get_owner), luabind::def("get_quest_item", &lua_get_quest_item), + luabind::def("get_encounter", &lua_get_encounter), luabind::def("map_opcodes", &lua_map_opcodes), luabind::def("clear_opcode", &lua_clear_opcode), luabind::def("enable_recipe", &lua_enable_recipe), luabind::def("disable_recipe", &lua_disable_recipe), luabind::def("clear_npctype_cache", &lua_clear_npctype_cache), - luabind::def("clock", &lua_clock) + luabind::def("clock", &lua_clock), + luabind::def("create_npc", &lua_create_npc) ]; } @@ -1451,11 +1722,7 @@ luabind::scope lua_register_slot() { luabind::value("General6", static_cast(MainGeneral6)), luabind::value("General7", static_cast(MainGeneral7)), luabind::value("General8", static_cast(MainGeneral8)), - //luabind::value("General9", static_cast(MainGeneral9)), - //luabind::value("General10", static_cast(MainGeneral10)), luabind::value("Cursor", static_cast(MainCursor)), - //luabind::value("EquipmentBegin", static_cast(EmuConstants::EQUIPMENT_BEGIN)), - //luabind::value("EquipmentEnd", static_cast(EmuConstants::EQUIPMENT_END)), luabind::value("PersonalBegin", static_cast(EmuConstants::GENERAL_BEGIN)), // deprecated luabind::value("GeneralBegin", static_cast(EmuConstants::GENERAL_BEGIN)), luabind::value("PersonalEnd", static_cast(EmuConstants::GENERAL_END)), // deprecated @@ -1483,7 +1750,6 @@ luabind::scope lua_register_material() { luabind::value("Secondary", static_cast(MaterialSecondary)), luabind::value("Max", static_cast(_MaterialCount)), // deprecated luabind::value("Count", static_cast(_MaterialCount)), - //luabind::value("TintCount", static_cast(_MaterialCount - 2)), luabind::value("Invalid", static_cast(_MaterialInvalid)) ]; } @@ -1498,8 +1764,7 @@ luabind::scope lua_register_client_version() { luabind::value("SoF", static_cast(EQClientSoF)), luabind::value("SoD", static_cast(EQClientSoD)), luabind::value("Underfoot", static_cast(EQClientUnderfoot)), - luabind::value("RoF", static_cast(EQClientRoF))//, - //luabind::value("RoF2", static_cast(EQClientRoF2)) + luabind::value("RoF", static_cast(EQClientRoF)) ]; } diff --git a/zone/lua_npc.cpp b/zone/lua_npc.cpp index 41524af15..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(); } @@ -442,6 +452,15 @@ void Lua_NPC::MerchantCloseShop() { self->MerchantCloseShop(); } +void Lua_NPC::SetMerchantProbability(uint8 amt) { + Lua_Safe_Call_Void(); + self->SetMerchantProbability(amt); +} + +uint8 Lua_NPC::GetMerchantProbability() { + Lua_Safe_Call_Int(); + return self->GetMerchantProbability(); +} luabind::scope lua_register_npc() { return luabind::class_("NPC") @@ -526,13 +545,17 @@ 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) .def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount) .def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore) .def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop) - .def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop); + .def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop) + .def("SetMerchantProbability", (void(Lua_NPC::*)(void))&Lua_NPC::SetMerchantProbability) + .def("GetMerchantProbability", (uint8(Lua_NPC::*)(void))&Lua_NPC::GetMerchantProbability); } #endif diff --git a/zone/lua_npc.h b/zone/lua_npc.h index 8c34ed17d..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(); @@ -114,6 +116,8 @@ public: int GetScore(); void MerchantOpenShop(); void MerchantCloseShop(); + void SetMerchantProbability(uint8 amt); + uint8 GetMerchantProbability(); }; #endif 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_parser.cpp b/zone/lua_parser.cpp index 9f082993c..b05e7619f 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -126,6 +126,7 @@ struct lua_registered_event { }; std::map> lua_encounter_events_registered; +std::map lua_encounters_loaded; LuaParser::LuaParser() { for(int i = 0; i < _LargestEventID; ++i) { @@ -220,7 +221,7 @@ LuaParser::~LuaParser() { } int LuaParser::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -239,7 +240,7 @@ int LuaParser::EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, } int LuaParser::EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -257,7 +258,7 @@ int LuaParser::EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string } int LuaParser::_EventNPC(std::string package_name, QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers, luabind::adl::object *l_func) { + std::vector *extra_pointers, luabind::adl::object *l_func) { const char *sub_name = LuaEvents[evt]; int start = lua_gettop(L); @@ -316,7 +317,7 @@ int LuaParser::_EventNPC(std::string package_name, QuestEventID evt, NPC* npc, M } int LuaParser::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -334,7 +335,7 @@ int LuaParser::EventPlayer(QuestEventID evt, Client *client, std::string data, u } int LuaParser::EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -352,7 +353,7 @@ int LuaParser::EventGlobalPlayer(QuestEventID evt, Client *client, std::string d } int LuaParser::_EventPlayer(std::string package_name, QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers, luabind::adl::object *l_func) { + std::vector *extra_pointers, luabind::adl::object *l_func) { const char *sub_name = LuaEvents[evt]; int start = lua_gettop(L); @@ -409,7 +410,7 @@ int LuaParser::_EventPlayer(std::string package_name, QuestEventID evt, Client * } int LuaParser::EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -429,7 +430,7 @@ int LuaParser::EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob * } int LuaParser::_EventItem(std::string package_name, QuestEventID evt, Client *client, ItemInst *item, Mob *mob, - std::string data, uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func) { + std::string data, uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func) { const char *sub_name = LuaEvents[evt]; int start = lua_gettop(L); @@ -492,7 +493,7 @@ int LuaParser::_EventItem(std::string package_name, QuestEventID evt, Client *cl } int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -508,7 +509,7 @@ int LuaParser::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spe } int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers, luabind::adl::object *l_func) { + std::vector *extra_pointers, luabind::adl::object *l_func) { const char *sub_name = LuaEvents[evt]; int start = lua_gettop(L); @@ -572,7 +573,7 @@ int LuaParser::_EventSpell(std::string package_name, QuestEventID evt, NPC* npc, return 0; } -int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, std::vector *extra_pointers) { +int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -588,7 +589,7 @@ int LuaParser::EventEncounter(QuestEventID evt, std::string encounter_name, uint } int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { const char *sub_name = LuaEvents[evt]; int start = lua_gettop(L); @@ -601,7 +602,13 @@ int LuaParser::_EventEncounter(std::string package_name, QuestEventID evt, std:: lua_pushstring(L, encounter_name.c_str()); lua_setfield(L, -2, "name"); - quest_manager.StartQuest(nullptr, nullptr, nullptr); + if(extra_pointers) { + std::string *str = EQEmu::any_cast(extra_pointers->at(0)); + lua_pushstring(L, str->c_str()); + lua_setfield(L, -2, "data"); + } + + quest_manager.StartQuest(nullptr, nullptr, nullptr, encounter_name); if(lua_pcall(L, 1, 1, 0)) { std::string error = lua_tostring(L, -1); AddError(error); @@ -770,6 +777,7 @@ void LuaParser::ReloadQuests() { loaded_.clear(); errors_.clear(); lua_encounter_events_registered.clear(); + lua_encounters_loaded.clear(); if(L) { lua_close(L); @@ -839,7 +847,7 @@ void LuaParser::ReloadQuests() { if(f) { fclose(f); - if(luaL_dofile(L, "quests/global/script_init.lua")) { + if(luaL_dofile(L, path.c_str())) { std::string error = lua_tostring(L, -1); AddError(error); } @@ -849,7 +857,9 @@ void LuaParser::ReloadQuests() { if(zone) { std::string zone_script = "quests/"; zone_script += zone->GetShortName(); - zone_script += "/script_init.lua"; + zone_script += "/script_init_v"; + zone_script += std::to_string(zone->GetInstanceVersion()); + zone_script += ".lua"; f = fopen(zone_script.c_str(), "r"); if(f) { fclose(f); @@ -858,6 +868,21 @@ void LuaParser::ReloadQuests() { std::string error = lua_tostring(L, -1); AddError(error); } + + return; + } + + zone_script = "quests/"; + zone_script += zone->GetShortName(); + zone_script += "/script_init.lua"; + f = fopen(zone_script.c_str(), "r"); + if(f) { + fclose(f); + + if(luaL_dofile(L, zone_script.c_str())) { + std::string error = lua_tostring(L, -1); + AddError(error); + } } } } @@ -972,7 +997,7 @@ void LuaParser::MapFunctions(lua_State *L) { } int LuaParser::DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -1018,7 +1043,7 @@ int LuaParser::DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::stri } int LuaParser::DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -1047,7 +1072,7 @@ int LuaParser::DispatchEventPlayer(QuestEventID evt, Client *client, std::string } int LuaParser::DispatchEventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; @@ -1093,7 +1118,7 @@ int LuaParser::DispatchEventItem(QuestEventID evt, Client *client, ItemInst *ite } int LuaParser::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { evt = ConvertLuaEvent(evt); if(evt >= _LargestEventID) { return 0; diff --git a/zone/lua_parser.h b/zone/lua_parser.h index b30e3d3b7..13cebe0fc 100644 --- a/zone/lua_parser.h +++ b/zone/lua_parser.h @@ -2,8 +2,8 @@ #define _EQE_LUA_PARSER_H #ifdef LUA_EQEMU -#include "QuestParserCollection.h" -#include "QuestInterface.h" +#include "quest_parser_collection.h" +#include "quest_interface.h" #include #include #include @@ -28,19 +28,19 @@ public: ~LuaParser(); virtual int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual bool HasQuestSub(uint32 npc_id, QuestEventID evt); virtual bool HasGlobalQuestSub(QuestEventID evt); @@ -65,25 +65,25 @@ public: virtual uint32 GetIdentifier() { return 0xb0712acc; } virtual int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int DispatchEventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); private: int _EventNPC(std::string package_name, QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); + std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventPlayer(std::string package_name, QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); + std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventItem(std::string package_name, QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, - uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); + uint32 extra_data, std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventSpell(std::string package_name, QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); + std::vector *extra_pointers, luabind::adl::object *l_func = nullptr); int _EventEncounter(std::string package_name, QuestEventID evt, std::string encounter_name, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void LoadScript(std::string filename, std::string package_name); bool HasFunction(std::string function, std::string package_name); diff --git a/zone/lua_parser_events.cpp b/zone/lua_parser_events.cpp index 8064106dc..89f08a81f 100644 --- a/zone/lua_parser_events.cpp +++ b/zone/lua_parser_events.cpp @@ -5,12 +5,12 @@ #include #include -#include "QuestParserCollection.h" -#include "QuestInterface.h" +#include "quest_parser_collection.h" +#include "quest_interface.h" #include "masterentity.h" #include "../common/seperator.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "lua_item.h" #include "lua_iteminst.h" #include "lua_entity.h" @@ -27,7 +27,7 @@ //NPC void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { npc->DoQuestPause(init); Lua_Client l_client(reinterpret_cast(init)); @@ -43,7 +43,7 @@ void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *in } void handle_npc_event_trade(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Client l_client(reinterpret_cast(init)); luabind::adl::object l_client_o = luabind::adl::object(L, l_client); l_client_o.push(L); @@ -54,9 +54,12 @@ void handle_npc_event_trade(QuestInterface *parse, lua_State* L, NPC* npc, Mob * ident << npc->GetNPCTypeID(); if(extra_pointers) { - for(size_t i = 0; i < extra_pointers->size(); ++i) { + size_t sz = extra_pointers->size(); + for(size_t i = 0; i < sz; ++i) { std::string prefix = "item" + std::to_string(static_cast(i + 1)); - Lua_ItemInst l_inst = reinterpret_cast(extra_pointers->at(i)); + ItemInst *inst = EQEmu::any_cast(extra_pointers->at(i)); + + Lua_ItemInst l_inst = inst; luabind::adl::object l_inst_o = luabind::adl::object(L, l_inst); l_inst_o.push(L); @@ -79,7 +82,7 @@ void handle_npc_event_trade(QuestInterface *parse, lua_State* L, NPC* npc, Mob * } void handle_npc_event_hp(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(extra_data == 1) { lua_pushinteger(L, -1); lua_setfield(L, -2, "hp_event"); @@ -96,7 +99,7 @@ void handle_npc_event_hp(QuestInterface *parse, lua_State* L, NPC* npc, Mob *ini } void handle_npc_single_mob(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Mob l_mob(init); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); l_mob_o.push(L); @@ -104,7 +107,7 @@ void handle_npc_single_mob(QuestInterface *parse, lua_State* L, NPC* npc, Mob *i } void handle_npc_single_client(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Client l_client(reinterpret_cast(init)); luabind::adl::object l_client_o = luabind::adl::object(L, l_client); l_client_o.push(L); @@ -112,7 +115,7 @@ void handle_npc_single_client(QuestInterface *parse, lua_State* L, NPC* npc, Mob } void handle_npc_single_npc(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_NPC l_npc(reinterpret_cast(init)); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); l_npc_o.push(L); @@ -120,7 +123,7 @@ void handle_npc_single_npc(QuestInterface *parse, lua_State* L, NPC* npc, Mob *i } void handle_npc_task_accepted(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Client l_client(reinterpret_cast(init)); luabind::adl::object l_client_o = luabind::adl::object(L, l_client); l_client_o.push(L); @@ -131,7 +134,7 @@ void handle_npc_task_accepted(QuestInterface *parse, lua_State* L, NPC* npc, Mob } void handle_npc_popup(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Mob l_mob(init); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); l_mob_o.push(L); @@ -142,7 +145,7 @@ void handle_npc_popup(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, } void handle_npc_waypoint(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Mob l_mob(init); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); l_mob_o.push(L); @@ -153,7 +156,7 @@ void handle_npc_waypoint(QuestInterface *parse, lua_State* L, NPC* npc, Mob *ini } void handle_npc_hate(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Mob l_mob(init); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); l_mob_o.push(L); @@ -165,19 +168,19 @@ void handle_npc_hate(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, s void handle_npc_signal(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "signal"); } void handle_npc_timer(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushstring(L, data.c_str()); lua_setfield(L, -2, "timer"); } void handle_npc_death(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Mob l_mob(init); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); l_mob_o.push(L); @@ -205,7 +208,7 @@ void handle_npc_death(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, } void handle_npc_cast(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { int spell_id = std::stoi(data); if(IsValidSpell(spell_id)) { Lua_Spell l_spell(&spells[spell_id]); @@ -221,21 +224,21 @@ void handle_npc_cast(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, s } void handle_npc_area(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(0))); lua_setfield(L, -2, "area_id"); - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(1))); + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(1))); lua_setfield(L, -2, "area_type"); } void handle_npc_null(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { } //Player void handle_player_say(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushstring(L, data.c_str()); lua_setfield(L, -2, "message"); @@ -244,7 +247,7 @@ void handle_player_say(QuestInterface *parse, lua_State* L, Client* client, std: } void handle_player_death(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Seperator sep(data.c_str()); Mob *o = entity_list.GetMobID(std::stoi(sep.arg[0])); @@ -274,13 +277,13 @@ void handle_player_death(QuestInterface *parse, lua_State* L, Client* client, st } void handle_player_timer(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushstring(L, data.c_str()); lua_setfield(L, -2, "timer"); } void handle_player_discover_item(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { const Item_Struct *item = database.GetItem(extra_data); if(item) { Lua_Item l_item(item); @@ -296,51 +299,51 @@ void handle_player_discover_item(QuestInterface *parse, lua_State* L, Client* cl } void handle_player_fish_forage_success(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_ItemInst l_item(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_ItemInst l_item(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_item_o = luabind::adl::object(L, l_item); l_item_o.push(L); lua_setfield(L, -2, "item"); } void handle_player_click_object(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_Object l_object(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_Object l_object(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_object_o = luabind::adl::object(L, l_object); l_object_o.push(L); lua_setfield(L, -2, "object"); } void handle_player_click_door(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_Door l_door(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_Door l_door(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_door_o = luabind::adl::object(L, l_door); l_door_o.push(L); lua_setfield(L, -2, "door"); } void handle_player_signal(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "signal"); } void handle_player_popup_response(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "popup_id"); } void handle_player_pick_up(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_ItemInst l_item(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_ItemInst l_item(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_item_o = luabind::adl::object(L, l_item); l_item_o.push(L); lua_setfield(L, -2, "item"); } void handle_player_cast(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { int spell_id = std::stoi(data); if(IsValidSpell(spell_id)) { Lua_Spell l_spell(&spells[spell_id]); @@ -356,48 +359,48 @@ void handle_player_cast(QuestInterface *parse, lua_State* L, Client* client, std } void handle_player_task_fail(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "task_id"); } void handle_player_zone(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "zone_id"); } void handle_player_duel_win(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_Client l_client(reinterpret_cast(extra_pointers->at(1))); + std::vector *extra_pointers) { + Lua_Client l_client(EQEmu::any_cast(extra_pointers->at(1))); luabind::adl::object l_client_o = luabind::adl::object(L, l_client); l_client_o.push(L); lua_setfield(L, -2, "other"); } void handle_player_duel_loss(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_Client l_client(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_Client l_client(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_client_o = luabind::adl::object(L, l_client); l_client_o.push(L); lua_setfield(L, -2, "other"); } void handle_player_loot(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_ItemInst l_item(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_ItemInst l_item(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_item_o = luabind::adl::object(L, l_item); l_item_o.push(L); lua_setfield(L, -2, "item"); - Lua_Corpse l_corpse(reinterpret_cast(extra_pointers->at(1))); + Lua_Corpse l_corpse(EQEmu::any_cast(extra_pointers->at(1))); luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse); l_corpse_o.push(L); lua_setfield(L, -2, "corpse"); } void handle_player_task_stage_complete(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Seperator sep(data.c_str()); lua_pushinteger(L, std::stoi(sep.arg[0])); lua_setfield(L, -2, "task_id"); @@ -407,7 +410,7 @@ void handle_player_task_stage_complete(QuestInterface *parse, lua_State* L, Clie } void handle_player_task_update(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Seperator sep(data.c_str()); lua_pushinteger(L, std::stoi(sep.arg[0])); lua_setfield(L, -2, "count"); @@ -420,7 +423,7 @@ void handle_player_task_update(QuestInterface *parse, lua_State* L, Client* clie } void handle_player_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Seperator sep(data.c_str(), ' ', 10, 100, true); std::string command(sep.arg[0] + 1); lua_pushstring(L, command.c_str()); @@ -439,7 +442,7 @@ void handle_player_command(QuestInterface *parse, lua_State* L, Client* client, } void handle_player_combine(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, extra_data); lua_setfield(L, -2, "recipe_id"); @@ -448,24 +451,24 @@ void handle_player_combine(QuestInterface *parse, lua_State* L, Client* client, } void handle_player_feign(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_NPC l_npc(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_NPC l_npc(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); l_npc_o.push(L); lua_setfield(L, -2, "other"); } void handle_player_area(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(0))); lua_setfield(L, -2, "area_id"); - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(1))); + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(1))); lua_setfield(L, -2, "area_type"); } void handle_player_respawn(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, std::stoi(data)); lua_setfield(L, -2, "option"); @@ -474,8 +477,8 @@ void handle_player_respawn(QuestInterface *parse, lua_State* L, Client* client, } void handle_player_packet(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_Packet l_packet(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_Packet l_packet(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_packet_o = luabind::adl::object(L, l_packet); l_packet_o.push(L); lua_setfield(L, -2, "packet"); @@ -485,24 +488,24 @@ void handle_player_packet(QuestInterface *parse, lua_State* L, Client* client, s } void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { } //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, extra_data); lua_setfield(L, -2, "slot_id"); } void handle_item_timer(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushstring(L, data.c_str()); lua_setfield(L, -2, "timer"); } void handle_item_proc(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { Lua_Mob l_mob(mob); luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob); @@ -523,7 +526,7 @@ void handle_item_proc(QuestInterface *parse, lua_State* L, Client* client, ItemI } void handle_item_loot(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(mob && mob->IsCorpse()) { Lua_Corpse l_corpse(mob->CastToCorpse()); luabind::adl::object l_corpse_o = luabind::adl::object(L, l_corpse); @@ -538,14 +541,14 @@ void handle_item_loot(QuestInterface *parse, lua_State* L, Client* client, ItemI } void handle_item_equip(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { lua_pushinteger(L, extra_data); lua_setfield(L, -2, "slot_id"); } void handle_item_augment(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_ItemInst l_item(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_ItemInst l_item(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_item_o = luabind::adl::object(L, l_item); l_item_o.push(L); lua_setfield(L, -2, "aug"); @@ -555,8 +558,8 @@ void handle_item_augment(QuestInterface *parse, lua_State* L, Client* client, It } void handle_item_augment_insert(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_ItemInst l_item(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_ItemInst l_item(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_item_o = luabind::adl::object(L, l_item); l_item_o.push(L); lua_setfield(L, -2, "item"); @@ -566,8 +569,8 @@ void handle_item_augment_insert(QuestInterface *parse, lua_State* L, Client* cli } void handle_item_augment_remove(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { - Lua_ItemInst l_item(reinterpret_cast(extra_pointers->at(0))); + std::vector *extra_pointers) { + Lua_ItemInst l_item(EQEmu::any_cast(extra_pointers->at(0))); luabind::adl::object l_item_o = luabind::adl::object(L, l_item); l_item_o.push(L); lua_setfield(L, -2, "item"); @@ -575,17 +578,17 @@ void handle_item_augment_remove(QuestInterface *parse, lua_State* L, Client* cli lua_pushinteger(L, extra_data); lua_setfield(L, -2, "slot_id"); - lua_pushboolean(L, *reinterpret_cast(extra_pointers->at(1))); + lua_pushboolean(L, *EQEmu::any_cast(extra_pointers->at(1))); lua_setfield(L, -2, "destroyed"); } void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { } //Spell void handle_spell_effect(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(npc) { Lua_Mob l_npc(npc); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); @@ -602,7 +605,7 @@ void handle_spell_effect(QuestInterface *parse, lua_State* L, NPC* npc, Client* lua_setfield(L, -2, "target"); - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(0))); + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(0))); lua_setfield(L, -2, "buff_slot"); lua_pushinteger(L, extra_data); @@ -610,7 +613,7 @@ void handle_spell_effect(QuestInterface *parse, lua_State* L, NPC* npc, Client* } void handle_spell_tic(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(npc) { Lua_Mob l_npc(npc); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); @@ -627,13 +630,13 @@ void handle_spell_tic(QuestInterface *parse, lua_State* L, NPC* npc, Client* cli lua_setfield(L, -2, "target"); - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(0))); + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(0))); lua_setfield(L, -2, "tics_remaining"); - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(1))); + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(1))); lua_setfield(L, -2, "caster_level"); - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(2))); + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(2))); lua_setfield(L, -2, "buff_slot"); lua_pushinteger(L, extra_data); @@ -641,7 +644,7 @@ void handle_spell_tic(QuestInterface *parse, lua_State* L, NPC* npc, Client* cli } void handle_spell_fade(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(npc) { Lua_Mob l_npc(npc); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); @@ -661,12 +664,12 @@ void handle_spell_fade(QuestInterface *parse, lua_State* L, NPC* npc, Client* cl lua_pushinteger(L, extra_data); lua_setfield(L, -2, "buff_slot"); - lua_pushinteger(L, *reinterpret_cast(extra_pointers->at(0))); + lua_pushinteger(L, *EQEmu::any_cast(extra_pointers->at(0))); lua_setfield(L, -2, "caster_id"); } void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(npc) { Lua_Mob l_npc(npc); luabind::adl::object l_npc_o = luabind::adl::object(L, l_npc); @@ -685,7 +688,7 @@ void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Cl } void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { } #endif diff --git a/zone/lua_parser_events.h b/zone/lua_parser_events.h index 8272e30c3..32a59e0da 100644 --- a/zone/lua_parser_events.h +++ b/zone/lua_parser_events.h @@ -2,128 +2,128 @@ #define _EQE_LUA_PARSER_EVENTS_H #ifdef LUA_EQEMU -typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std::string, uint32, std::vector*); -typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); -typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, ItemInst*, Mob*, std::string, uint32, std::vector*); -typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector*); +typedef void(*NPCArgumentHandler)(QuestInterface*, lua_State*, NPC*, Mob*, std::string, uint32, std::vector*); +typedef void(*PlayerArgumentHandler)(QuestInterface*, lua_State*, Client*, std::string, uint32, std::vector*); +typedef void(*ItemArgumentHandler)(QuestInterface*, lua_State*, Client*, ItemInst*, Mob*, std::string, uint32, std::vector*); +typedef void(*SpellArgumentHandler)(QuestInterface*, lua_State*, NPC*, Client*, uint32, uint32, std::vector*); //NPC void handle_npc_event_say(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_event_trade(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_event_hp(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_single_mob(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_single_client(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_single_npc(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_task_accepted(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_popup(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_waypoint(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_hate(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_signal(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_timer(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_death(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_cast(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_area(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_npc_null(QuestInterface *parse, lua_State* L, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); //Player void handle_player_say(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_death(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_timer(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_discover_item(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_fish_forage_success(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_click_object(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_click_door(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_signal(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_popup_response(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_pick_up(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_cast(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_task_fail(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_zone(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_duel_win(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_duel_loss(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_loot(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_task_stage_complete(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_task_update(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_command(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_combine(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_feign(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_area(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_respawn(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_packet(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_player_null(QuestInterface *parse, lua_State* L, Client* client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); //Item void handle_item_click(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_timer(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_proc(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_loot(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_equip(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_augment(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_augment_insert(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_augment_remove(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_item_null(QuestInterface *parse, lua_State* L, Client* client, ItemInst* item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); //Spell void handle_spell_effect(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_spell_tic(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_spell_fade(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_translocate_finish(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); void handle_spell_null(QuestInterface *parse, lua_State* L, NPC* npc, Client* client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); #endif #endif 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/map.cpp b/zone/map.cpp index 42c295e0d..a023cc125 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -1,7 +1,7 @@ #include "../common/debug.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "map.h" -#include "RaycastMesh.h" +#include "raycast_mesh.h" #include "zone.h" #include #include diff --git a/zone/merc.cpp b/zone/merc.cpp index ff4581489..c94616eab 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1,23 +1,23 @@ #include "merc.h" #include "masterentity.h" -#include "NpcAI.h" +#include "npc_ai.h" #include "../common/packet_dump.h" #include "../common/eq_packet_structs.h" #include "../common/eq_constants.h" #include "../common/skills.h" #include "../common/spdat.h" #include "zone.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "string_ids.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "../common/rulesys.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "water_map.h" extern volatile bool ZoneLoaded; Merc::Merc(const NPCType* d, float x, float y, float z, float heading) - : NPC(d, 0, x, y, z, heading, 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000) +: NPC(d, 0, x, y, z, heading, 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000) { base_hp = d->max_hp; base_mana = d->Mana; @@ -62,7 +62,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) skills[r] = database.GetSkillCap(GetClass(),(SkillUseTypes)r,GetLevel()); } - GetMercSize(); + size = d->size; CalcBonuses(); SetHP(GetMaxHP()); @@ -75,7 +75,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading) Merc::~Merc() { AI_Stop(); - entity_list.RemoveMerc(this->GetID()); + //entity_list.RemoveMerc(this->GetID()); UninitializeBuffSlots(); } @@ -112,131 +112,66 @@ void Merc::CalcBonuses() rooted = FindType(SE_Root); } -void Merc::GetMercSize() { +float Merc::GetDefaultSize() { float MercSize = GetSize(); - switch(this->GetRace()) { - case 1: // Humans have no race bonus - break; - case 2: // Barbarian - MercSize = 7.0; - break; - case 3: // Erudite - break; - case 4: // Wood Elf - MercSize = 5.0; - break; - case 5: // High Elf - break; - case 6: // Dark Elf - MercSize = 5.0; - break; - case 7: // Half Elf - MercSize = 5.5; - break; - case 8: // Dwarf - MercSize = 4.0; - break; - case 9: // Troll - MercSize = 8.0; - break; - case 10: // Ogre - MercSize = 9.0; - break; - case 11: // Halfling - MercSize = 3.5; - break; - case 12: // Gnome - MercSize = 3.0; - break; - case 128: // Iksar - break; - case 130: // Vah Shir - MercSize = 7.0; - break; - case 330: // Froglok - MercSize = 5.0; - break; - case 522: // Drakkin - MercSize = 5.0; - break; + switch(this->GetRace()) + { + case 1: // Humans + MercSize = 6.0; + break; + case 2: // Barbarian + MercSize = 7.0; + break; + case 3: // Erudite + MercSize = 6.0; + break; + case 4: // Wood Elf + MercSize = 5.0; + break; + case 5: // High Elf + MercSize = 6.0; + break; + case 6: // Dark Elf + MercSize = 5.0; + break; + case 7: // Half Elf + MercSize = 5.5; + break; + case 8: // Dwarf + MercSize = 4.0; + break; + case 9: // Troll + MercSize = 8.0; + break; + case 10: // Ogre + MercSize = 9.0; + break; + case 11: // Halfling + MercSize = 3.5; + break; + case 12: // Gnome + MercSize = 3.0; + break; + case 128: // Iksar + MercSize = 6.0; + break; + case 130: // Vah Shir + MercSize = 7.0; + break; + case 330: // Froglok + MercSize = 5.0; + break; + case 522: // Drakkin + MercSize = 5.0; + break; + default: + MercSize = 6.0; + break; } - this->size = MercSize; -} - -void Merc::GenerateAppearance() { - // Randomize facial appearance - int iFace = 0; - if(this->GetRace() == 2) { // Barbarian w/Tatoo - iFace = MakeRandomInt(0, 79); - } - else { - iFace = MakeRandomInt(0, 7); - } - - int iHair = 0; - int iBeard = 0; - int iBeardColor = 1; - if(this->GetRace() == 522) { - iHair = MakeRandomInt(0, 8); - iBeard = MakeRandomInt(0, 11); - iBeardColor = MakeRandomInt(0, 3); - } - else if(this->GetGender()) { - iHair = MakeRandomInt(0, 2); - if(this->GetRace() == 8) { // Dwarven Females can have a beard - if(MakeRandomInt(1, 100) < 50) { - iFace += 10; - } - } - } - else { - iHair = MakeRandomInt(0, 3); - iBeard = MakeRandomInt(0, 5); - iBeardColor = MakeRandomInt(0, 19); - } - - int iHairColor = 0; - if(this->GetRace() == 522) { - iHairColor = MakeRandomInt(0, 3); - } - else { - iHairColor = MakeRandomInt(0, 19); - } - - uint8 iEyeColor1 = (uint8)MakeRandomInt(0, 9); - uint8 iEyeColor2 = 0; - if(this->GetRace() == 522) { - iEyeColor1 = iEyeColor2 = (uint8)MakeRandomInt(0, 11); - } - else if(MakeRandomInt(1, 100) > 96) { - iEyeColor2 = MakeRandomInt(0, 9); - } - else { - iEyeColor2 = iEyeColor1; - } - - int iHeritage = 0; - int iTattoo = 0; - int iDetails = 0; - if(this->GetRace() == 522) { - iHeritage = MakeRandomInt(0, 6); - iTattoo = MakeRandomInt(0, 7); - iDetails = MakeRandomInt(0, 7); - } - - this->luclinface = iFace; - this->hairstyle = iHair; - this->beard = iBeard; - this->beardcolor = iBeardColor; - this->haircolor = iHairColor; - this->eyecolor1 = iEyeColor1; - this->eyecolor2 = iEyeColor2; - this->drakkin_heritage = iHeritage; - this->drakkin_tattoo = iTattoo; - this->drakkin_details = iDetails; + return MercSize; } int Merc::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat) @@ -277,9 +212,9 @@ void Merc::CalcItemBonuses(StatBonuses* newbon) { //Power Source Slot /*if (GetClientVersion() >= EQClientSoF) { - const ItemInst* inst = m_inv[MainPowerSource]; - if(inst) - AddItemBonuses(inst, newbon); + const ItemInst* inst = m_inv[MainPowerSource]; + if(inst) + AddItemBonuses(inst, newbon); }*/ // Caps @@ -635,14 +570,14 @@ int Merc::GroupLeadershipAAHealthEnhancement() switch(g->GetLeadershipAA(groupAAHealthEnhancement)) { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; } return 0; @@ -657,14 +592,14 @@ int Merc::GroupLeadershipAAManaEnhancement() switch(g->GetLeadershipAA(groupAAManaEnhancement)) { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; + case 0: + return 0; + case 1: + return 30; + case 2: + return 60; + case 3: + return 100; } return 0; @@ -679,14 +614,14 @@ int Merc::GroupLeadershipAAHealthRegeneration() switch(g->GetLeadershipAA(groupAAHealthRegeneration)) { - case 0: - return 0; - case 1: - return 4; - case 2: - return 6; - case 3: - return 8; + case 0: + return 0; + case 1: + return 4; + case 2: + return 6; + case 3: + return 8; } return 0; @@ -701,26 +636,26 @@ int Merc::GroupLeadershipAAOffenseEnhancement() switch(g->GetLeadershipAA(groupAAOffenseEnhancement)) { - case 0: - return 0; - case 1: - return 10; - case 2: - return 19; - case 3: - return 28; - case 4: - return 34; - case 5: - return 40; + case 0: + return 0; + case 1: + return 10; + case 2: + return 19; + case 3: + return 28; + case 4: + return 34; + case 5: + return 40; } return 0; } -int16 Merc::CalcSTR() { - int16 val = _baseSTR + itembonuses.STR + spellbonuses.STR; +int32 Merc::CalcSTR() { + int32 val = _baseSTR + itembonuses.STR + spellbonuses.STR; - int16 mod = aabonuses.STR; + int32 mod = aabonuses.STR; STR = val + mod; @@ -730,10 +665,10 @@ int16 Merc::CalcSTR() { return(STR); } -int16 Merc::CalcSTA() { - int16 val = _baseSTA + itembonuses.STA + spellbonuses.STA; +int32 Merc::CalcSTA() { + int32 val = _baseSTA + itembonuses.STA + spellbonuses.STA; - int16 mod = aabonuses.STA; + int32 mod = aabonuses.STA; STA = val + mod; @@ -743,11 +678,11 @@ int16 Merc::CalcSTA() { return(STA); } -int16 Merc::CalcAGI() { - int16 val = _baseAGI + itembonuses.AGI + spellbonuses.AGI; - int16 mod = aabonuses.AGI; +int32 Merc::CalcAGI() { + int32 val = _baseAGI + itembonuses.AGI + spellbonuses.AGI; + int32 mod = aabonuses.AGI; - int16 str = GetSTR(); + int32 str = GetSTR(); AGI = val + mod; @@ -757,10 +692,10 @@ int16 Merc::CalcAGI() { return(AGI); } -int16 Merc::CalcDEX() { - int16 val = _baseDEX + itembonuses.DEX + spellbonuses.DEX; +int32 Merc::CalcDEX() { + int32 val = _baseDEX + itembonuses.DEX + spellbonuses.DEX; - int16 mod = aabonuses.DEX; + int32 mod = aabonuses.DEX; DEX = val + mod; @@ -770,10 +705,10 @@ int16 Merc::CalcDEX() { return(DEX); } -int16 Merc::CalcINT() { - int16 val = _baseINT + itembonuses.INT + spellbonuses.INT; +int32 Merc::CalcINT() { + int32 val = _baseINT + itembonuses.INT + spellbonuses.INT; - int16 mod = aabonuses.INT; + int32 mod = aabonuses.INT; INT = val + mod; @@ -783,10 +718,10 @@ int16 Merc::CalcINT() { return(INT); } -int16 Merc::CalcWIS() { - int16 val = _baseWIS + itembonuses.WIS + spellbonuses.WIS; +int32 Merc::CalcWIS() { + int32 val = _baseWIS + itembonuses.WIS + spellbonuses.WIS; - int16 mod = aabonuses.WIS; + int32 mod = aabonuses.WIS; WIS = val + mod; @@ -796,10 +731,10 @@ int16 Merc::CalcWIS() { return(WIS); } -int16 Merc::CalcCHA() { - int16 val = _baseCHA + itembonuses.CHA + spellbonuses.CHA; +int32 Merc::CalcCHA() { + int32 val = _baseCHA + itembonuses.CHA + spellbonuses.CHA; - int16 mod = aabonuses.CHA; + int32 mod = aabonuses.CHA; CHA = val + mod; @@ -812,7 +747,7 @@ int16 Merc::CalcCHA() { //The AA multipliers are set to be 5, but were 2 on WR //The resistant discipline which I think should be here is implemented //in Mob::ResistSpell -int16 Merc::CalcMR() +int32 Merc::CalcMR() { MR = _baseMR + itembonuses.MR + spellbonuses.MR + aabonuses.MR; @@ -822,7 +757,7 @@ int16 Merc::CalcMR() return(MR); } -int16 Merc::CalcFR() +int32 Merc::CalcFR() { FR = _baseFR + itembonuses.FR + spellbonuses.FR + aabonuses.FR; @@ -832,7 +767,7 @@ int16 Merc::CalcFR() return(FR); } -int16 Merc::CalcDR() +int32 Merc::CalcDR() { DR = _baseDR + itembonuses.DR + spellbonuses.DR + aabonuses.DR; @@ -842,7 +777,7 @@ int16 Merc::CalcDR() return(DR); } -int16 Merc::CalcPR() +int32 Merc::CalcPR() { PR = _basePR + itembonuses.PR + spellbonuses.PR + aabonuses.PR; @@ -852,7 +787,7 @@ int16 Merc::CalcPR() return(PR); } -int16 Merc::CalcCR() +int32 Merc::CalcCR() { CR = _baseCR + itembonuses.CR + spellbonuses.CR + aabonuses.CR; @@ -862,19 +797,19 @@ int16 Merc::CalcCR() return(CR); } -int16 Merc::CalcCorrup() +int32 Merc::CalcCorrup() { Corrup = _baseCorrup + itembonuses.Corrup + spellbonuses.Corrup + aabonuses.Corrup; return(Corrup); } -int16 Merc::CalcATK() { +int32 Merc::CalcATK() { ATK = _baseATK + itembonuses.ATK + spellbonuses.ATK + aabonuses.ATK + GroupLeadershipAAOffenseEnhancement(); return(ATK); } -int16 Merc::CalcAC() { +int32 Merc::CalcAC() { //spell AC bonuses are added directly to natural total AC = _baseAC + spellbonuses.AC; return(AC); @@ -905,7 +840,7 @@ int32 Merc::CalcMaxHP() { //but the actual effect sent on live causes the client //to apply it to (basehp + itemhp).. I will oblige to the client's whims over //the aa description - nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability + nd += aabonuses.MaxHP; //Natural Durability, Physical Enhancement, Planar Durability max_hp = (float)max_hp * (float)nd / (float)10000; //this is to fix the HP-above-495k issue max_hp += spellbonuses.HP + aabonuses.HP; @@ -936,20 +871,20 @@ int32 Merc::CalcMaxMana() { switch(GetCasterClass()) { - case 'I': - case 'W': { - max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); - break; - } - case 'N': { - max_mana = 0; - break; - } - default: { - LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); - max_mana = 0; - break; - } + case 'I': + case 'W': { + max_mana = (CalcBaseMana() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement()); + break; + } + case 'N': { + max_mana = 0; + break; + } + default: { + LogFile->write(EQEMuLog::Debug, "Invalid Class '%c' in CalcMaxMana", GetCasterClass()); + max_mana = 0; + break; + } } if (max_mana < 0) { max_mana = 0; @@ -1031,12 +966,12 @@ int32 Merc::CalcManaRegenCap() int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap; switch(GetCasterClass()) { - case 'I': - cap += (itembonuses.HeroicINT / 25); - break; - case 'W': - cap += (itembonuses.HeroicWIS / 25); - break; + case 'I': + cap += (itembonuses.HeroicINT / 25); + break; + case 'W': + cap += (itembonuses.HeroicWIS / 25); + break; } return (cap * RuleI(Character, ManaRegenMultiplier) / 100); @@ -1044,7 +979,7 @@ int32 Merc::CalcManaRegenCap() void Merc::CalcMaxEndurance() { - max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance; + max_end = CalcBaseEndurance() + spellbonuses.Endurance + itembonuses.Endurance + aabonuses.Endurance; if (max_end < 0) { max_end = 0; @@ -1160,19 +1095,24 @@ void Merc::SetEndurance(int32 newEnd) } void Merc::DoEnduranceUpkeep() { - int upkeep_sum = 0; + if (!HasEndurUpkeep()) + return; + + int upkeep_sum = 0; int cost_redux = spellbonuses.EnduranceReduction + itembonuses.EnduranceReduction; + bool has_effect = false; uint32 buffs_i; uint32 buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { if (buffs[buffs_i].spellid != SPELL_UNKNOWN) { int upkeep = spells[buffs[buffs_i].spellid].EndurUpkeep; if(upkeep > 0) { + has_effect = true; if(cost_redux > 0) { if(upkeep <= cost_redux) - continue; //reduced to 0 + continue; //reduced to 0 upkeep -= cost_redux; } if((upkeep+upkeep_sum) > GetEndurance()) { @@ -1187,6 +1127,9 @@ void Merc::DoEnduranceUpkeep() { if(upkeep_sum != 0) SetEndurance(GetEndurance() - upkeep_sum); + + if (!has_effect) + SetEndurUpkeep(false); } void Merc::CalcRestState() { @@ -1244,112 +1187,27 @@ void Merc::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { ns->spawn.lfg = 0; ns->spawn.anon = 0; ns->spawn.gm = 0; - ns->spawn.guildID = 0xFFFFFFFF; // 0xFFFFFFFF = NO GUILD, 0 = Unknown Guild - ns->spawn.is_npc = 1; // 0=no, 1=yes + ns->spawn.guildID = 0xFFFFFFFF; // 0xFFFFFFFF = NO GUILD, 0 = Unknown Guild + ns->spawn.is_npc = 1; // 0=no, 1=yes ns->spawn.is_pet = 0; ns->spawn.guildrank = 0; ns->spawn.showhelm = 1; ns->spawn.flymode = 0; - ns->spawn.size = 0; - ns->spawn.NPC = 1; // 0=player,1=npc,2=pc corpse,3=npc corpse + ns->spawn.NPC = 1; // 0=player,1=npc,2=pc corpse,3=npc corpse ns->spawn.IsMercenary = 1; - /*const Item_Struct* item = 0; - const ItemInst* inst = 0; - uint32 spawnedmercid = 0; - spawnedmercid = this->GetID(); - - inst = GetBotItem(SLOT_HANDS); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_HANDS] = item->Material; - ns->spawn.colors[MATERIAL_HANDS].color = GetEquipmentColor(MATERIAL_HANDS); + unsigned int i; + //should not include 21 (SLOT_AMMO) + for (i = 0; i < MainAmmo; i++) { + if(equipment[i] == 0) + continue; + const Item_Struct* item = database.GetItem(equipment[i]); + if(item) + { + ns->spawn.equipment[i] = item->Material; + ns->spawn.colors[i].color = item->Color; } } - - inst = GetBotItem(SLOT_HEAD); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_HEAD] = item->Material; - ns->spawn.colors[MATERIAL_HEAD].color = GetEquipmentColor(MATERIAL_HEAD); - } - } - - inst = GetBotItem(SLOT_ARMS); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_ARMS] = item->Material; - ns->spawn.colors[MATERIAL_ARMS].color = GetEquipmentColor(MATERIAL_ARMS); - } - } - - inst = GetBotItem(SLOT_BRACER01); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_BRACER] = item->Material; - ns->spawn.colors[MATERIAL_BRACER].color = GetEquipmentColor(MATERIAL_BRACER); - } - } - - inst = GetBotItem(SLOT_BRACER02); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_BRACER] = item->Material; - ns->spawn.colors[MATERIAL_BRACER].color = GetEquipmentColor(MATERIAL_BRACER); - } - } - - inst = GetBotItem(SLOT_CHEST); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_CHEST] = item->Material; - ns->spawn.colors[MATERIAL_CHEST].color = GetEquipmentColor(MATERIAL_CHEST); - } - } - - inst = GetBotItem(SLOT_LEGS); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_LEGS] = item->Material; - ns->spawn.colors[MATERIAL_LEGS].color = GetEquipmentColor(MATERIAL_LEGS); - } - } - - inst = GetBotItem(SLOT_FEET); - if(inst) { - item = inst->GetItem(); - if(item) { - ns->spawn.equipment[MATERIAL_FEET] = item->Material; - ns->spawn.colors[MATERIAL_FEET].color = GetEquipmentColor(MATERIAL_FEET); - } - } - - inst = GetBotItem(SLOT_PRIMARY); - if(inst) { - item = inst->GetItem(); - if(item) { - if(strlen(item->IDFile) > 2) - ns->spawn.equipment[MATERIAL_PRIMARY] = atoi(&item->IDFile[2]); - ns->spawn.colors[MATERIAL_PRIMARY].color = GetEquipmentColor(MATERIAL_PRIMARY); - } - } - - inst = GetBotItem(SLOT_SECONDARY); - if(inst) { - item = inst->GetItem(); - if(item) { - if(strlen(item->IDFile) > 2) - ns->spawn.equipment[MATERIAL_SECONDARY] = atoi(&item->IDFile[2]); - ns->spawn.colors[MATERIAL_SECONDARY].color = GetEquipmentColor(MATERIAL_SECONDARY); - } - }*/ } } @@ -1369,19 +1227,19 @@ bool Merc::Process() } if(!GetMercOwner()) { - //p_depop = true; //this was causing a crash - removed merc from entity list, but not group - //return false; //merc can live after client dies, not sure how long + //p_depop = true; //this was causing a crash - removed merc from entity list, but not group + //return false; //merc can live after client dies, not sure how long } - if(IsSuspended()) { - //return false; + if(IsSuspended()) + { + return false; } - if (HasGroup() && GetFollowID() == 0) { + if (HasGroup() && GetMercOwner() && GetFollowID() == 0) { SetFollowID(GetMercOwner()->GetID()); } - SpellProcess(); if(tic_timer.Check()) @@ -1440,7 +1298,7 @@ bool Merc::IsMercCasterCombatRange(Mob *target) { range *= range; - // half the max so the bot doesn't always stop at max range to allow combat movement + // half the max so the merc doesn't always stop at max range to allow combat movement range *= .5; float targetDistance = DistNoRootNoZ(*target); @@ -1462,19 +1320,30 @@ void Merc::AI_Process() { return; // A bot wont start its AI if not grouped - if(!GetOwner() || !HasGroup()) { + if(!HasGroup()) { return; } - if(GetAppearance() == eaDead) - return; - Mob* MercOwner = GetOwner(); - // The bots need an owner - if(!MercOwner) + if(GetAppearance() == eaDead) + { + if(!MercOwner) + { + Depop(); + } return; + } + // The merc needs an owner + if(!MercOwner) { + //SetTarget(0); + //SetOwnerID(0); + // TODO: Need to wait and try casting rez if merc is a healer with a dead owner + return; + } + + /* try { if(MercOwner->CastToClient()->IsDead()) { SetTarget(0); @@ -1487,6 +1356,7 @@ void Merc::AI_Process() { SetOwnerID(0); return; } + */ if(check_target_timer.Check()) { CheckHateList(); @@ -1597,8 +1467,10 @@ void Merc::AI_Process() { atCombatRange = true; } - if(atCombatRange) { - if(IsMoving()) { + if(atCombatRange) + { + if(IsMoving()) + { SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetRunAnimSpeed(0); @@ -1609,25 +1481,30 @@ void Merc::AI_Process() { } } - if(AImovement_timer->Check()) { - if(!IsMoving() && GetClass() == ROGUE && !BehindMob(GetTarget(), GetX(), GetY())) { + if(AImovement_timer->Check()) + { + if(!IsMoving() && GetClass() == ROGUE && !BehindMob(GetTarget(), GetX(), GetY())) + { // Move the rogue to behind the mob float newX = 0; float newY = 0; float newZ = 0; - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) { + if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) + { CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); return; } } - else if(!IsMoving() && GetClass() != ROGUE && (DistNoRootNoZ(*GetTarget()) < GetTarget()->GetSize())) { + else if(!IsMoving() && GetClass() != ROGUE && (DistNoRootNoZ(*GetTarget()) < GetTarget()->GetSize())) + { // If we are not a rogue trying to backstab, let's try to adjust our melee range so we don't appear to be bunched up float newX = 0; float newY = 0; float newZ = 0; - if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) { + if(PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) + { CalculateNewPosition2(newX, newY, newZ, GetRunspeed()); return; } @@ -1639,26 +1516,28 @@ void Merc::AI_Process() { SendPosition(); } - if(!IsMercCaster() && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead)) { + if(!IsMercCaster() && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead)) + { // we can't fight if we don't have a target, are stun/mezzed or dead.. // Stop attacking if the target is enraged if(IsEngaged() && !BehindMob(GetTarget(), GetX(), GetY()) && GetTarget()->IsEnraged()) return; //TODO: Implement Stances. /*if(GetBotStance() == BotStancePassive) - return;*/ + return;*/ // First, special attack per class (kick, backstab etc..) DoClassAttacks(GetTarget()); //try main hand first - if(attack_timer.Check()) { + if(attack_timer.Check()) + { Attack(GetTarget(), MainPrimary); bool tripleSuccess = false; - if(GetOwner() && GetTarget() && CanThisClassDoubleAttack()) { - + if(GetOwner() && GetTarget() && CanThisClassDoubleAttack()) + { if(GetOwner()) { Attack(GetTarget(), MainPrimary, true); } @@ -1690,90 +1569,95 @@ void Merc::AI_Process() { int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; if (GetTarget() && ExtraAttackChanceBonus) { - if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) - { - Attack(GetTarget(), MainPrimary, false); - } - } + if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) + { + Attack(GetTarget(), MainPrimary, false); + } + } } // TODO: Do mercs berserk? Find this out on live... //if (GetClass() == WARRIOR || GetClass() == BERSERKER) { - // if(GetHP() > 0 && !berserk && this->GetHPRatio() < 30) { - // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); - // this->berserk = true; - // } - // if (berserk && this->GetHPRatio() > 30) { - // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); - // this->berserk = false; - // } + // if(GetHP() > 0 && !berserk && this->GetHPRatio() < 30) { + // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); + // this->berserk = true; + // } + // if (berserk && this->GetHPRatio() > 30) { + // entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); + // this->berserk = false; + // } //} //now off hand - if(GetTarget() && attack_dw_timer.Check() && CanThisClassDualWield()) { + if(GetTarget() && attack_dw_timer.Check() && CanThisClassDualWield()) + { + int weapontype = 0; // No weapon type + bool bIsFist = true; - int weapontype = 0; // No weapon type - bool bIsFist = true; + if(bIsFist || ((weapontype != ItemType2HSlash) && (weapontype != ItemType2HPiercing) && (weapontype != ItemType2HBlunt))) + { + float DualWieldProbability = 0.0f; - if(bIsFist || ((weapontype != ItemType2HSlash) && (weapontype != ItemType2HPiercing) && (weapontype != ItemType2HBlunt))) { - float DualWieldProbability = 0.0f; + int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; + DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max + int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; + DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; - int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; - DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max - int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; - DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; + float random = MakeRandomFloat(0, 1); - float random = MakeRandomFloat(0, 1); + // Max 78% of DW + if (random < DualWieldProbability) + { + Attack(GetTarget(), MainSecondary); // Single attack with offhand - if (random < DualWieldProbability){ // Max 78% of DW - - Attack(GetTarget(), MainSecondary); // Single attack with offhand - - if( CanThisClassDoubleAttack()) { - if(GetTarget() && GetTarget()->GetHP() > -10) - Attack(GetTarget(), MainSecondary); // Single attack with offhand - } + if(CanThisClassDoubleAttack()) { + if(GetTarget() && GetTarget()->GetHP() > -10) + Attack(GetTarget(), MainSecondary); // Single attack with offhand } } } } - }// end in combat range - else { - if(GetTarget()->IsFeared() && !spellend_timer.Enabled()){ - // This is a mob that is fleeing either because it has been feared or is low on hitpoints - //TODO: Implement Stances. - //if(GetStance() != MercStancePassive) - AI_PursueCastCheck(); - } - - if (AImovement_timer->Check()) { - if(!IsRooted()) { - mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); - CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); - return; - } - - if(IsMoving()) - SendPosUpdate(); - else - SendPosition(); - } - } // end not in combat range - - if(!IsMoving() && !spellend_timer.Enabled()) { - - //TODO: Implement Stances. - //if(GetStance() == MercStancePassive) - // return; - - if(AI_EngagedCastCheck()) { - MercMeditate(false); - } - else if(GetArchetype() == ARCHETYPE_CASTER) - MercMeditate(true); } - } // end IsEngaged() - else { + } + else + { + if(GetTarget()->IsFeared() && !spellend_timer.Enabled()) { + // This is a mob that is fleeing either because it has been feared or is low on hitpoints + //TODO: Implement Stances. + //if(GetStance() != MercStancePassive) + AI_PursueCastCheck(); + } + + if (AImovement_timer->Check()) + { + if(!IsRooted()) { + mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", GetTarget()->GetCleanName()); + CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); + return; + } + + if(IsMoving()) + SendPosUpdate(); + else + SendPosition(); + } + } // end not in combat range + + if(!IsMoving() && !spellend_timer.Enabled()) + { + //TODO: Implement Stances. + //if(GetStance() == MercStancePassive) + // return; + + if(AI_EngagedCastCheck()) { + MercMeditate(false); + } + else if(GetArchetype() == ARCHETYPE_CASTER) + MercMeditate(true); + } + } + else + { // Not engaged in combat SetTarget(0); SetHatedCount(0); @@ -1783,8 +1667,8 @@ void Merc::AI_Process() { if(!check_target_timer.Enabled()) check_target_timer.Start(2000, false); - if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) { - + if(!IsMoving() && AIthink_timer->Check() && !spellend_timer.Enabled()) + { //TODO: Implement passive stances. //if(GetStance() != MercStancePassive) { if(!AI_IdleCastCheck() && !IsCasting()) { @@ -1794,11 +1678,14 @@ void Merc::AI_Process() { } } - if(AImovement_timer->Check()) { - if(GetFollowID()) { + if(AImovement_timer->Check()) + { + if(GetFollowID()) + { Mob* follow = entity_list.GetMob(GetFollowID()); - if(follow) { + if(follow) + { float dist = DistNoRoot(*follow); float speed = GetRunspeed(); @@ -1859,9 +1746,9 @@ bool Merc::AI_EngagedCastCheck() { bool result = false; bool failedToCast = false; - if (GetTarget() && AIautocastspell_timer->Check(false)) { - - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + if (GetTarget() && AIautocastspell_timer->Check(false)) + { + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. mlog(AI__SPELLS, "Engaged autocast check triggered (MERCS)."); @@ -1869,36 +1756,36 @@ bool Merc::AI_EngagedCastCheck() { switch(mercClass) { - case TANK: + case TANK: + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { + failedToCast = true; + } + } + break; + case HEALER: + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), MercAISpellRange, SpellType_Heal)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Buff), MercAISpellRange, SpellType_Buff)) { + failedToCast = true; + } + } + break; + case MELEEDPS: + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { failedToCast = true; } } - break; - case HEALER: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), MercAISpellRange, SpellType_Heal)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Buff), MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } + } + break; + case CASTERDPS: + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { + if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { + failedToCast = true; } - break; - case MELEEDPS: - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_InCombatBuff), SpellType_InCombatBuff)) { - failedToCast = true; - } - } - } - break; - case CASTERDPS: - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - break; + } + break; } if(!AIautocastspell_timer->Enabled()) { @@ -1920,36 +1807,36 @@ bool Merc::AI_IdleCastCheck() { #if MobAI_DEBUG_Spells >= 25 std::cout << "Non-Engaged autocast check triggered: " << this->GetCleanName() << std::endl; #endif - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. //Ok, IdleCastCheck depends of class. int8 mercClass = GetClass(); switch(mercClass) { - case TANK: - failedToCast = true; + case TANK: + failedToCast = true; break; - case HEALER: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Cure)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Heal)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Resurrect)) { - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } + case HEALER: + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Cure)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Heal)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Resurrect)) { + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { + failedToCast = true; } } } - result = true; - break; - case MELEEDPS: - if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { - failedToCast = true; - } - break; - case CASTERDPS: + } + result = true; + break; + case MELEEDPS: + if(!entity_list.Merc_AICheckCloseBeneficialSpells(this, 100, MercAISpellRange, SpellType_Buff)) { failedToCast = true; - break; + } + break; + case CASTERDPS: + failedToCast = true; + break; } if(!AIautocastspell_timer->Enabled()) @@ -1990,17 +1877,17 @@ bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, if(caster->HasGroup()) { if( mercCasterClass == HEALER) { - if( iSpellTypes == SpellType_Heal ) { + if( iSpellTypes == SpellType_Heal ) { if(caster->AICastSpell(100, SpellType_Heal)) return true; } - if( iSpellTypes == SpellType_Cure ) { + if( iSpellTypes == SpellType_Cure ) { if(caster->AICastSpell(100, SpellType_Cure)) return true; } - if( iSpellTypes == SpellType_Resurrect ) { + if( iSpellTypes == SpellType_Resurrect ) { if(caster->AICastSpell(100, SpellType_Resurrect)) return true; } @@ -2039,11 +1926,11 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon dist2 = DistNoRoot(*tar); if (((((spells[spellid].targettype==ST_GroupTeleport && mercSpell.type==SpellType_Heal) - || spells[spellid].targettype==ST_AECaster - || spells[spellid].targettype==ST_Group - || spells[spellid].targettype==ST_AEBard) - && dist2 <= spells[spellid].aoerange*spells[spellid].aoerange) - || dist2 <= GetActSpellRange(spellid, spells[spellid].range)*GetActSpellRange(spellid, spells[spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) + || spells[spellid].targettype==ST_AECaster + || spells[spellid].targettype==ST_Group + || spells[spellid].targettype==ST_AEBard) + && dist2 <= spells[spellid].aoerange*spells[spellid].aoerange) + || dist2 <= GetActSpellRange(spellid, spells[spellid].range)*GetActSpellRange(spellid, spells[spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana())) { SetRunAnimSpeed(0); SendPosition(); @@ -2085,7 +1972,7 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) { int8 mercClass = GetClass(); uint8 mercLevel = GetLevel(); - bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. + bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. bool castedSpell = false; bool isDiscipline = false; @@ -2103,417 +1990,417 @@ bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) { switch(mercClass) { - case TANK: - case MELEEDPS: - isDiscipline = true; + case TANK: + case MELEEDPS: + isDiscipline = true; break; - default: - isDiscipline = false; + default: + isDiscipline = false; break; } switch (iSpellTypes) { - case SpellType_Heal: { - Mob* tar = nullptr; - int8 numToHeal = g->GetNumberNeedingHealedInGroup(IsEngaged() ? 75 : 95, true); - int8 checkHPR = IsEngaged() ? 95 : 99; - int8 checkPetHPR = IsEngaged() ? 95 : 99; + case SpellType_Heal: { + Mob* tar = nullptr; + int8 numToHeal = g->GetNumberNeedingHealedInGroup(IsEngaged() ? 75 : 95, true); + int8 checkHPR = IsEngaged() ? 95 : 99; + int8 checkPetHPR = IsEngaged() ? 95 : 99; - //todo: check stance to determine healing spell selection + //todo: check stance to determine healing spell selection - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i] && !g->members[i]->qglobal) { - int8 hpr = (int8)g->members[i]->GetHPRatio(); + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && !g->members[i]->qglobal) { + int8 hpr = (int8)g->members[i]->GetHPRatio(); - if(g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < checkHPR) { - if(!tar || ((g->members[i]->GetPet()->GetHPRatio() + 25) < tar->GetHPRatio())) { - tar = g->members[i]->GetPet(); - checkPetHPR = g->members[i]->GetPet()->GetHPRatio() + 25; - } - } + if(g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < checkHPR) { + if(!tar || ((g->members[i]->GetPet()->GetHPRatio() + 25) < tar->GetHPRatio())) { + tar = g->members[i]->GetPet(); + checkPetHPR = g->members[i]->GetPet()->GetHPRatio() + 25; + } + } - if(hpr > checkHPR) { - continue; - } + if(hpr > checkHPR) { + continue; + } - if(IsEngaged() && (g->members[i]->GetClass() == NECROMANCER && hpr >= 50) - || (g->members[i]->GetClass() == SHAMAN && hpr >= 80)) { - //allow necros to lifetap & shaman to canni without wasting mana - continue; - } + if(IsEngaged() && (g->members[i]->GetClass() == NECROMANCER && hpr >= 50) + || (g->members[i]->GetClass() == SHAMAN && hpr >= 80)) { + //allow necros to lifetap & shaman to canni without wasting mana + continue; + } - if(hpr < checkHPR && g->members[i] == GetMercOwner()) { - if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) - tar = g->members[i]; //check owner first - } - else if(hpr < checkHPR && g->HasRole(g->members[i], RoleTank)){ - if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) - tar = g->members[i]; - } - else if( hpr < checkHPR && (!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR)))) { - tar = g->members[i]; - } - } - } - - if(numToHeal > 2) { - selectedMercSpell = GetBestMercSpellForGroupHeal(this); - } - - if(tar && selectedMercSpell.spellid == 0) { - if(tar->GetHPRatio() < 15) { - //check for very fast heals first (casting time < 1 s) - selectedMercSpell = GetBestMercSpellForVeryFastHeal(this); - - //check for fast heals next (casting time < 2 s) - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForFastHeal(this); - } - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else if (tar->GetHPRatio() < 35) { - //check for fast heals next (casting time < 2 s) - selectedMercSpell = GetBestMercSpellForFastHeal(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else if (tar->GetHPRatio() < 80) { - selectedMercSpell = GetBestMercSpellForPercentageHeal(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - else { - //check for heal over time. if not present, try it first - if(!tar->FindType(SE_HealOverTime)) { - selectedMercSpell = GetBestMercSpellForHealOverTime(this); - - //get regular heal - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); - } - } - } - } - - if(selectedMercSpell.spellid > 0) { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); - } - - if(castedSpell) { - char* gmsg = 0; - - if(tar != this) { - //we don't need spam of bots healing themselves - MakeAnyLenString(&gmsg, "Casting %s on %s.", spells[selectedMercSpell.spellid].name, tar->GetCleanName()); - if(gmsg) - { - MercGroupSay(this, gmsg); - safe_delete_array(gmsg); - } - } - } - - break; - } - case SpellType_Root: { - break; - } - case SpellType_Buff: { - - if(GetClass() == HEALER && GetManaRatio() < 50) { - return false; //mercs buff when Mana > 50% - } - - std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_Buff); - - for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(!((spells[selectedMercSpell.spellid].targettype == ST_Target || spells[selectedMercSpell.spellid].targettype == ST_Pet || - spells[selectedMercSpell.spellid].targettype == ST_Group || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport || - spells[selectedMercSpell.spellid].targettype == ST_Self))) { - continue; - } - - if(spells[selectedMercSpell.spellid].targettype == ST_Self) { - if( !this->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (this->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - if( this->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { - continue; + if(hpr < checkHPR && g->members[i] == GetMercOwner()) { + if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) + tar = g->members[i]; //check owner first + } + else if(hpr < checkHPR && g->HasRole(g->members[i], RoleTank)){ + if(!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR))) + tar = g->members[i]; + } + else if( hpr < checkHPR && (!tar || (hpr < tar->GetHPRatio() || (tar->IsPet() && hpr < checkPetHPR)))) { + tar = g->members[i]; + } + } } - uint32 TempDontBuffMeBeforeTime = this->DontBuffMeBefore(); + if(numToHeal > 2) { + selectedMercSpell = GetBestMercSpellForGroupHeal(this); + } + + if(tar && selectedMercSpell.spellid == 0) { + if(tar->GetHPRatio() < 15) { + //check for very fast heals first (casting time < 1 s) + selectedMercSpell = GetBestMercSpellForVeryFastHeal(this); + + //check for fast heals next (casting time < 2 s) + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForFastHeal(this); + } + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + else if (tar->GetHPRatio() < 35) { + //check for fast heals next (casting time < 2 s) + selectedMercSpell = GetBestMercSpellForFastHeal(this); + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + else if (tar->GetHPRatio() < 80) { + selectedMercSpell = GetBestMercSpellForPercentageHeal(this); + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + else { + //check for heal over time. if not present, try it first + if(!tar->FindType(SE_HealOverTime)) { + selectedMercSpell = GetBestMercSpellForHealOverTime(this); + + //get regular heal + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForRegularSingleTargetHeal(this); + } + } + } + } + + if(selectedMercSpell.spellid > 0) { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); + } + + if(castedSpell) { + char* gmsg = 0; + + if(tar != this) { + //we don't need spam of bots healing themselves + MakeAnyLenString(&gmsg, "Casting %s on %s.", spells[selectedMercSpell.spellid].name, tar->GetCleanName()); + if(gmsg) + { + MercGroupSay(this, gmsg); + safe_delete_array(gmsg); + } + } + } + + break; + } + case SpellType_Root: { + break; + } + case SpellType_Buff: { + + if(GetClass() == HEALER && GetManaRatio() < 50) { + return false; //mercs buff when Mana > 50% + } + + std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_Buff); + + for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { + MercSpell selectedMercSpell = *itr; + + if(!((spells[selectedMercSpell.spellid].targettype == ST_Target || spells[selectedMercSpell.spellid].targettype == ST_Pet || + spells[selectedMercSpell.spellid].targettype == ST_Group || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport || + spells[selectedMercSpell.spellid].targettype == ST_Self))) { + continue; + } + + if(spells[selectedMercSpell.spellid].targettype == ST_Self) { + if( !this->IsImmuneToSpell(selectedMercSpell.spellid, this) + && (this->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { + + if( this->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { + continue; + } + + uint32 TempDontBuffMeBeforeTime = this->DontBuffMeBefore(); + + if(selectedMercSpell.spellid > 0) { + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1, &TempDontBuffMeBeforeTime); + + if(TempDontBuffMeBeforeTime != this->DontBuffMeBefore()) + this->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + } + } + } + } + else { + for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i]) { + Mob* tar = g->members[i]; + + if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) + && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { + + if( tar->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { + continue; + } + + uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); + + if(selectedMercSpell.spellid > 0) { + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1, &TempDontBuffMeBeforeTime); + + if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore()) + tar->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + } + } + } + + if(!castedSpell && tar->GetPet()) { + + //don't cast group spells on pets + if(IsGroupSpell(selectedMercSpell.spellid) + || spells[selectedMercSpell.spellid].targettype == ST_Group + || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport ) { + continue; + } + + if(!tar->GetPet()->IsImmuneToSpell(selectedMercSpell.spellid, this) + && (tar->GetPet()->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { + + uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); + + if(selectedMercSpell.spellid > 0) { + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetPet()->GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1, &TempDontBuffMeBeforeTime); + + if(TempDontBuffMeBeforeTime != tar->GetPet()->DontBuffMeBefore()) + tar->GetPet()->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + } + } + } + } + } + } + } + } + break; + } + case SpellType_Nuke: { + switch(mercClass) + { + case TANK: + //check for taunt + if(CheckAETaunt()) { + if(MERC_DEBUG > 0) + GetOwner()->Message(7, "AE Taunting"); + //get AE taunt + selectedMercSpell = GetBestMercSpellForAETaunt(this); + } + + if(selectedMercSpell.spellid == 0 && CheckTaunt()) { + //get taunt + selectedMercSpell = GetBestMercSpellForTaunt(this); + } + + //get hate disc + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForHate(this); + } + + break; + case HEALER: + break; + case MELEEDPS: + break; + case CASTERDPS: + Mob* tar = GetTarget(); + + selectedMercSpell = GetBestMercSpellForAENuke(this, tar); + + if(selectedMercSpell.spellid == 0 && !tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned()) { + uint8 stunChance = 15; + if(MakeRandomInt(1, 100) <= stunChance) { + selectedMercSpell = GetBestMercSpellForStun(this); + } + } + + if(selectedMercSpell.spellid == 0) { + uint8 lureChance = 25; + if(MakeRandomInt(1, 100) <= lureChance) { + selectedMercSpell = GetBestMercSpellForNukeByTargetResists(this, tar); + } + } + + if(selectedMercSpell.spellid == 0) { + selectedMercSpell = GetBestMercSpellForNuke(this); + } + + break; + } if(selectedMercSpell.spellid > 0) { if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); + castedSpell = UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID()); } else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1, &TempDontBuffMeBeforeTime); - - if(TempDontBuffMeBeforeTime != this->DontBuffMeBefore()) - this->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1); } } - } - } - else { - for( int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i]) { - Mob* tar = g->members[i]; + + break; + } + case SpellType_InCombatBuff: { + std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_InCombatBuff); + Mob* tar = this; + + for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { + MercSpell selectedMercSpell = *itr; + + if(!(spells[selectedMercSpell.spellid].targettype == ST_Self)) { + continue; + } + + if(spells[selectedMercSpell.spellid].skill == SkillBackstab && spells[selectedMercSpell.spellid].targettype == ST_Self) { + if(!hidden) { + continue; + } + } if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - if( tar->GetArchetype() == ARCHETYPE_MELEE && IsEffectInSpell(selectedMercSpell.spellid, SE_IncreaseSpellHaste)) { - continue; - } - - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1, &TempDontBuffMeBeforeTime); - - if(TempDontBuffMeBeforeTime != tar->DontBuffMeBefore()) - tar->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); - } - } - } - - if(!castedSpell && tar->GetPet()) { - - //don't cast group spells on pets - if(IsGroupSpell(selectedMercSpell.spellid) - || spells[selectedMercSpell.spellid].targettype == ST_Group - || spells[selectedMercSpell.spellid].targettype == ST_GroupTeleport ) { - continue; - } - - if(!tar->GetPet()->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (tar->GetPet()->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); if(selectedMercSpell.spellid > 0) { if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetPet()->GetID()); + castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); } else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar->GetPet(), -1, &TempDontBuffMeBeforeTime); + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1); + } + } + } + } + break; + } + case SpellType_Cure: { + Mob* tar = nullptr; + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + if(g->members[i] && !g->members[i]->qglobal) { + if(GetNeedsCured(g->members[i]) && (g->members[i]->DontCureMeBefore() < Timer::GetCurrentTime())) { + tar = g->members[i]; + } + } + } - if(TempDontBuffMeBeforeTime != tar->GetPet()->DontBuffMeBefore()) - tar->GetPet()->SetDontBuffMeBefore(TempDontBuffMeBeforeTime); + if(tar && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 25 : 40, false) > 0) && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 40 : 60, false) > 2)) + { + selectedMercSpell = GetBestMercSpellForCure(this, tar); + + if(selectedMercSpell.spellid == 0) + break; + + uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); + + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, spells[selectedMercSpell.spellid].mana, &TempDontCureMeBeforeTime); + + if(castedSpell) { + if(IsGroupSpell(selectedMercSpell.spellid)){ + + if(this->HasGroup()) { + Group *g = this->GetGroup(); + + if(g) { + for( int i = 0; imembers[i] && !g->members[i]->qglobal) { + if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) + g->members[i]->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); + } + } } } } - } - } - } - } - } - break; - } - case SpellType_Nuke: { - switch(mercClass) - { - case TANK: - //check for taunt - if(CheckAETaunt()) { - if(MERC_DEBUG > 0) - GetOwner()->Message(7, "AE Taunting"); - //get AE taunt - selectedMercSpell = GetBestMercSpellForAETaunt(this); - } - - if(selectedMercSpell.spellid == 0 && CheckTaunt()) { - //get taunt - selectedMercSpell = GetBestMercSpellForTaunt(this); - } - - //get hate disc - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForHate(this); - } - - break; - case HEALER: - break; - case MELEEDPS: - break; - case CASTERDPS: - Mob* tar = GetTarget(); - - selectedMercSpell = GetBestMercSpellForAENuke(this, tar); - - if(selectedMercSpell.spellid == 0 && !tar->GetSpecialAbility(UNSTUNABLE) && !tar->IsStunned()) { - uint8 stunChance = 15; - if(MakeRandomInt(1, 100) <= stunChance) { - selectedMercSpell = GetBestMercSpellForStun(this); - } - } - - if(selectedMercSpell.spellid == 0) { - uint8 lureChance = 25; - if(MakeRandomInt(1, 100) <= lureChance) { - selectedMercSpell = GetBestMercSpellForNukeByTargetResists(this, tar); - } - } - - if(selectedMercSpell.spellid == 0) { - selectedMercSpell = GetBestMercSpellForNuke(this); - } - - break; - } - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetTarget()->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, GetTarget(), -1); - } - } - - break; - } - case SpellType_InCombatBuff: { - std::list buffSpellList = GetMercSpellsBySpellType(this, SpellType_InCombatBuff); - Mob* tar = this; - - for(std::list::iterator itr = buffSpellList.begin(); itr != buffSpellList.end(); ++itr) { - MercSpell selectedMercSpell = *itr; - - if(!(spells[selectedMercSpell.spellid].targettype == ST_Self)) { - continue; - } - - if(spells[selectedMercSpell.spellid].skill == SkillBackstab && spells[selectedMercSpell.spellid].targettype == ST_Self) { - if(!hidden) { - continue; - } - } - - if( !tar->IsImmuneToSpell(selectedMercSpell.spellid, this) - && (tar->CanBuffStack(selectedMercSpell.spellid, mercLevel, true) >= 0)) { - - uint32 TempDontBuffMeBeforeTime = tar->DontBuffMeBefore(); - - if(selectedMercSpell.spellid > 0) { - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, this, -1); - } - } - } - } - break; - } - case SpellType_Cure: { - Mob* tar = nullptr; - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(g->members[i] && !g->members[i]->qglobal) { - if(GetNeedsCured(g->members[i]) && (g->members[i]->DontCureMeBefore() < Timer::GetCurrentTime())) { - tar = g->members[i]; - } - } - } - - if(tar && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 25 : 40, false) > 0) && !(g->GetNumberNeedingHealedInGroup(IsEngaged() ? 40 : 60, false) > 2)) - { - selectedMercSpell = GetBestMercSpellForCure(this, tar); - - if(selectedMercSpell.spellid == 0) - break; - - uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); - - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, spells[selectedMercSpell.spellid].mana, &TempDontCureMeBeforeTime); - - if(castedSpell) { - if(IsGroupSpell(selectedMercSpell.spellid)){ - - if(this->HasGroup()) { - Group *g = this->GetGroup(); - - if(g) { - for( int i = 0; imembers[i] && !g->members[i]->qglobal) { - if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) - g->members[i]->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } + else { + if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) + tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); } } } - } - else { - if(TempDontCureMeBeforeTime != tar->DontCureMeBefore()) - tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - break; - } - case SpellType_Resurrect: { - Corpse *corpse = GetGroupMemberCorpse(); - - if(corpse) { - selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Resurrect); - - if(selectedMercSpell.spellid == 0) - break; - - uint32 TempDontRootMeBeforeTime = corpse->DontRootMeBefore(); - - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, corpse, spells[selectedMercSpell.spellid].mana, &TempDontRootMeBeforeTime); - - //CastSpell(selectedMercSpell.spellid, corpse->GetID(), 1, -1, -1, &TempDontRootMeBeforeTime); - corpse->SetDontRootMeBefore(TempDontRootMeBeforeTime); - } - - break; - } - case SpellType_Escape: { - Mob* tar = GetTarget(); - uint8 hpr = (uint8)GetHPRatio(); - bool mayGetAggro = false; - - if(tar && (mercClass == CASTERDPS) || (mercClass == MELEEDPS)) { - mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells - - if (mayGetAggro) { - selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Escape); - - if(selectedMercSpell.spellid == 0) break; + } + case SpellType_Resurrect: { + Corpse *corpse = GetGroupMemberCorpse(); - if(isDiscipline) { - castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); - } - else { - castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); - } - } - } - break; - } + if(corpse) { + selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Resurrect); + + if(selectedMercSpell.spellid == 0) + break; + + uint32 TempDontRootMeBeforeTime = corpse->DontRootMeBefore(); + + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, corpse, spells[selectedMercSpell.spellid].mana, &TempDontRootMeBeforeTime); + + //CastSpell(selectedMercSpell.spellid, corpse->GetID(), 1, -1, -1, &TempDontRootMeBeforeTime); + corpse->SetDontRootMeBefore(TempDontRootMeBeforeTime); + } + + break; + } + case SpellType_Escape: { + Mob* tar = GetTarget(); + uint8 hpr = (uint8)GetHPRatio(); + bool mayGetAggro = false; + + if(tar && (mercClass == CASTERDPS) || (mercClass == MELEEDPS)) { + mayGetAggro = HasOrMayGetAggro(); //classes have hate reducing spells + + if (mayGetAggro) { + selectedMercSpell = GetFirstMercSpellBySpellType(this, SpellType_Escape); + + if(selectedMercSpell.spellid == 0) + break; + + if(isDiscipline) { + castedSpell = UseDiscipline(selectedMercSpell.spellid, tar->GetID()); + } + else { + castedSpell = AIDoSpellCast(selectedMercSpell.spellid, tar, -1); + } + } + } + break; + } } } } @@ -2531,11 +2418,11 @@ void Merc::CheckHateList() { if(g) { Mob* MercOwner = GetOwner(); if(MercOwner && MercOwner->GetTarget() && MercOwner->GetTarget()->IsNPC() && (MercOwner->GetTarget()->GetHateAmount(MercOwner) || MercOwner->CastToClient()->AutoAttackEnabled()) && IsAttackAllowed(MercOwner->GetTarget())) { - float range = g->HasRole(MercOwner, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius); - range = range * range; - if(MercOwner->GetTarget()->DistNoRootNoZ(*this) < range) { - AddToHateList(MercOwner->GetTarget(), 1); - } + float range = g->HasRole(MercOwner, RolePuller) ? RuleI(Mercs, AggroRadiusPuller) : RuleI(Mercs, AggroRadius); + range = range * range; + if(MercOwner->GetTarget()->DistNoRootNoZ(*this) < range) { + AddToHateList(MercOwner->GetTarget(), 1); + } } else { std::list npc_list; @@ -2738,26 +2625,26 @@ int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) { // AA Focus /*if (aabonuses.FocusEffects[type]){ - int16 Total3 = 0; - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; + int16 Total3 = 0; + uint32 slots = 0; + uint32 aa_AA = 0; + uint32 aa_value = 0; - for (int i = 0; i < MAX_PP_AA_ARRAY; i++) - { - aa_AA = this->aa[i]->AA; - aa_value = this->aa[i]->value; - if (aa_AA < 1 || aa_value < 1) - continue; + for (int i = 0; i < MAX_PP_AA_ARRAY; i++) + { + aa_AA = this->aa[i]->AA; + aa_value = this->aa[i]->value; + if (aa_AA < 1 || aa_value < 1) + continue; - Total3 = CalcAAFocus(type, aa_AA, spell_id); - if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { - realTotal3 = Total3; - } - else if (Total3 < 0 && Total3 < realTotal3) { - realTotal3 = Total3; - } - } + Total3 = CalcAAFocus(type, aa_AA, spell_id); + if (Total3 > 0 && realTotal3 >= 0 && Total3 > realTotal3) { + realTotal3 = Total3; + } + else if (Total3 < 0 && Total3 < realTotal3) { + realTotal3 = Total3; + } + } }*/ if(type == focusReagentCost && IsSummonPetSpell(spell_id) && GetAA(aaElementalPact)) @@ -2773,7 +2660,7 @@ int16 Merc::GetFocusEffect(focusType type, uint16 spell_id) { int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { - + if (spells[spell_id].targettype == ST_Self) return value; @@ -2783,13 +2670,13 @@ int32 Merc::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) { value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); 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. + chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance; - if (MakeRandomInt(1,100) <= chance){ + if (chance > 0){ + + int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals. + + if (MakeRandomInt(1,100) <= chance){ Critical = true; ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease; ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack; @@ -2801,61 +2688,61 @@ int32 Merc::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*GetFocusEffect(focusImprovedDamage, spell_id)/100; + value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, 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 -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100; + value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100; - value -= GetFocusEffect(focusFcDamageAmt, spell_id); + value -= GetFocusEffect(focusFcDamageAmt, spell_id); if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value)*ratio/100; - value = (value * GetSpellScale() / 100); - + value = (value * GetSpellScale() / 100); + entity_list.MessageClose_StringID(this, false, 100, MT_SpellCrits, - OTHER_CRIT_BLAST, GetName(), itoa(-value)); + OTHER_CRIT_BLAST, GetName(), itoa(-value)); return value; } } - value = value_BaseEffect; - - value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; - - value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100; + value = value_BaseEffect; - if (target) { + value += value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100; + + value += value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, 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 -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); + value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id); + + value -= GetFocusEffect(focusFcDamageAmt, spell_id); - value -= GetFocusEffect(focusFcDamageAmt, 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); + + value = (value * GetSpellScale() / 100); - value = (value * GetSpellScale() / 100); - return value; - } +} int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { - + if (target == nullptr) target = this; @@ -2863,37 +2750,37 @@ int32 Merc::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) { int16 chance = 0; int8 modifier = 1; bool Critical = false; - - value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); - + + value_BaseEffect = value + (value*GetFocusEffect(focusFcBaseEffects, spell_id)/100); + value = value_BaseEffect; - value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, spell_id)/100); - + value += int(value_BaseEffect*GetFocusEffect(focusImprovedHeal, 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 += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; - value += GetFocusEffect(focusFcHealAmt, spell_id); - value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); - + value += GetFocusEffect(focusFcHealAmtCrit, spell_id) * modifier; + value += GetFocusEffect(focusFcHealAmt, 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); @@ -2903,14 +2790,14 @@ int32 Merc::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); } @@ -2984,126 +2871,126 @@ int8 Merc::GetChanceToCastBySpellType(int16 spellType) { int8 chance = 0; switch (spellType) { - case SpellType_Nuke: { - switch(mercClass) - { + case SpellType_Nuke: { + switch(mercClass) + { case TANK: { chance = 100; break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ chance = 100; break; - } + } case CASTERDPS:{ chance = 100; break; + } } - } - break; - } - case SpellType_Heal: { - switch(mercClass) - { + break; + } + case SpellType_Heal: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ chance = 100; break; - } + } case MELEEDPS:{ break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_Root: { - switch(mercClass) - { + break; + } + case SpellType_Root: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_Buff: { - switch(mercClass) - { + break; + } + case SpellType_Buff: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ chance = IsEngaged() ? 0 : 100; break; - } + } case MELEEDPS:{ break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_InCombatBuff: { - switch(mercClass) - { + break; + } + case SpellType_InCombatBuff: { + switch(mercClass) + { case TANK: { chance = 50; break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ chance = 50; break; - } + } case CASTERDPS:{ break; + } } - } - break; - } - case SpellType_Escape: { - switch(mercClass) - { + break; + } + case SpellType_Escape: { + switch(mercClass) + { case TANK: { break; - } + } case HEALER:{ break; - } + } case MELEEDPS:{ chance = 100; break; - } + } case CASTERDPS:{ chance = 100; break; + } } - } - break; - } - default: - chance = 0; - break; + break; + } + default: + chance = 0; + break; } return chance; @@ -3116,7 +3003,7 @@ bool Merc::CheckStance(int16 stance) { if(stance == 0 || (stance > 0 && stance == GetStance()) || (stance < 0 && abs(stance) != GetStance())) { - return true; + return true; } return false; @@ -3175,14 +3062,14 @@ MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, int spellType) { if((mercSpellList[i].type & spellType) && caster->CheckStance(mercSpellList[i].stance) && CheckSpellRecastTimers(caster, mercSpellList[i].spellid)) { - result.spellid = mercSpellList[i].spellid; - result.stance = mercSpellList[i].stance; - result.type = mercSpellList[i].type; - result.slot = mercSpellList[i].slot; - result.proc_chance = mercSpellList[i].proc_chance; - result.time_cancast = mercSpellList[i].time_cancast; + result.spellid = mercSpellList[i].spellid; + result.stance = mercSpellList[i].stance; + result.type = mercSpellList[i].type; + result.slot = mercSpellList[i].slot; + result.proc_chance = mercSpellList[i].proc_chance; + result.time_cancast = mercSpellList[i].time_cancast; - break; + break; } } } @@ -3212,14 +3099,14 @@ MercSpell Merc::GetMercSpellBySpellID(Merc* caster, uint16 spellid) { if((mercSpellList[i].spellid == spellid) && caster->CheckStance(mercSpellList[i].stance)) { - result.spellid = mercSpellList[i].spellid; - result.stance = mercSpellList[i].stance; - result.type = mercSpellList[i].type; - result.slot = mercSpellList[i].slot; - result.proc_chance = mercSpellList[i].proc_chance; - result.time_cancast = mercSpellList[i].time_cancast; + result.spellid = mercSpellList[i].spellid; + result.stance = mercSpellList[i].stance; + result.type = mercSpellList[i].type; + result.slot = mercSpellList[i].slot; + result.proc_chance = mercSpellList[i].proc_chance; + result.time_cancast = mercSpellList[i].time_cancast; - break; + break; } } } @@ -3306,14 +3193,14 @@ MercSpell Merc::GetBestMercSpellForVeryFastHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsVeryFastHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3338,14 +3225,14 @@ MercSpell Merc::GetBestMercSpellForFastHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsFastHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3410,14 +3297,14 @@ MercSpell Merc::GetBestMercSpellForPercentageHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsCompleteHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3442,14 +3329,14 @@ MercSpell Merc::GetBestMercSpellForRegularSingleTargetHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsRegularSingleTargetHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3475,14 +3362,14 @@ MercSpell Merc::GetFirstMercSpellForSingleTargetHeal(Merc* caster) { if((IsRegularSingleTargetHealSpell(mercSpellListItr->spellid) || IsFastHealSpell(mercSpellListItr->spellid)) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3507,14 +3394,14 @@ MercSpell Merc::GetBestMercSpellForGroupHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsRegularGroupHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3579,14 +3466,14 @@ MercSpell Merc::GetBestMercSpellForGroupCompleteHeal(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsGroupCompleteHealSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3613,14 +3500,14 @@ MercSpell Merc::GetBestMercSpellForAETaunt(Merc* caster) { || spells[mercSpellListItr->spellid].targettype == ST_AETarget || spells[mercSpellListItr->spellid].targettype == ST_UndeadAE) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3645,14 +3532,14 @@ MercSpell Merc::GetBestMercSpellForTaunt(Merc* caster) { // Assuming all the spells have been loaded into this list by level and in descending order if((spells[mercSpellListItr->spellid].targettype == ST_Target) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -3860,15 +3747,15 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - initialCastChance = 50; - break; - case MercStanceBalanced: - initialCastChance = 25; - break; - case MercStanceBurn: - initialCastChance = 0; - break; + case MercStanceBurnAE: + initialCastChance = 50; + break; + case MercStanceBalanced: + initialCastChance = 25; + break; + case MercStanceBurn: + initialCastChance = 0; + break; } //check of we even want to cast an AE nuke @@ -3895,8 +3782,8 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int numTargetsCheck = 1; //used to check for min number of targets to use AE bool spellSelected = false; result.spellid = 0; @@ -3908,13 +3795,13 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - numTargetsCheck = 1; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 2; - break; + case MercStanceBurnAE: + numTargetsCheck = 1; + break; + case MercStanceBalanced: + case MercStanceBurn: + numTargetsCheck = 2; + break; } if(caster) { @@ -3924,19 +3811,19 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsAENukeSpell(mercSpellListItr->spellid) && !IsAERainNukeSpell(mercSpellListItr->spellid) && !IsPBAENukeSpell(mercSpellListItr->spellid) && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - uint8 numTargets = 0; - if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) { - if(numTargets >= numTargetsCheck && MakeRandomInt(1, 100) <= castChance) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + uint8 numTargets = 0; + if(CheckAENuke(caster, tar, mercSpellListItr->spellid, numTargets)) { + if(numTargets >= numTargetsCheck && MakeRandomInt(1, 100) <= castChance) { + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; + } } - } - break; + break; } } } @@ -3946,8 +3833,8 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int numTargetsCheck = 1; //used to check for min number of targets to use AE bool spellSelected = false; result.spellid = 0; @@ -3959,13 +3846,13 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - numTargetsCheck = 2; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 3; - break; + case MercStanceBurnAE: + numTargetsCheck = 2; + break; + case MercStanceBalanced: + case MercStanceBurn: + numTargetsCheck = 3; + break; } if(caster) { @@ -3996,8 +3883,8 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) - int numTargetsCheck = 1; //used to check for min number of targets to use AE + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int numTargetsCheck = 1; //used to check for min number of targets to use AE bool spellSelected = false; result.spellid = 0; @@ -4009,13 +3896,13 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) { switch(caster->GetStance()) { - case MercStanceBurnAE: - numTargetsCheck = 1; - break; - case MercStanceBalanced: - case MercStanceBurn: - numTargetsCheck = 2; - break; + case MercStanceBurnAE: + numTargetsCheck = 1; + break; + case MercStanceBalanced: + case MercStanceBurn: + numTargetsCheck = 2; + break; } if(caster) { @@ -4046,7 +3933,7 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) { MercSpell Merc::GetBestMercSpellForNuke(Merc* caster) { MercSpell result; - int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) + int castChance = 50; //used to cycle through multiple spells (first has 50% overall chance, 2nd has 25%, etc.) bool spellSelected = false; result.spellid = 0; @@ -4062,15 +3949,15 @@ MercSpell Merc::GetBestMercSpellForNuke(Merc* caster) { for(std::list::iterator mercSpellListItr = mercSpellList.begin(); mercSpellListItr != mercSpellList.end(); ++mercSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order if(IsPureNukeSpell(mercSpellListItr->spellid) && !IsAENukeSpell(mercSpellListItr->spellid) - && MakeRandomInt(1, 100) <= castChance && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { - result.spellid = mercSpellListItr->spellid; - result.stance = mercSpellListItr->stance; - result.type = mercSpellListItr->type; - result.slot = mercSpellListItr->slot; - result.proc_chance = mercSpellListItr->proc_chance; - result.time_cancast = mercSpellListItr->time_cancast; + && MakeRandomInt(1, 100) <= castChance && CheckSpellRecastTimers(caster, mercSpellListItr->spellid)) { + result.spellid = mercSpellListItr->spellid; + result.stance = mercSpellListItr->stance; + result.type = mercSpellListItr->type; + result.slot = mercSpellListItr->slot; + result.proc_chance = mercSpellListItr->proc_chance; + result.time_cancast = mercSpellListItr->time_cancast; - break; + break; } } } @@ -4385,7 +4272,7 @@ Corpse* Merc::GetGroupMemberCorpse() { if(g->members[i] && g->members[i]->IsClient()) { corpse = entity_list.GetCorpseByOwnerWithinRange(g->members[i]->CastToClient(), this, RuleI(Mercs, ResurrectRadius)); - if(corpse && !corpse->Rezzed()) { + if(corpse && !corpse->IsRezzed()) { return corpse; } } @@ -4451,40 +4338,40 @@ bool Merc::CheckConfidence() { CurrentCon = this->GetLevelCon(mob->GetLevel()); switch(CurrentCon) { - case CON_GREEN: { - ConRating = 0; - break; - } + case CON_GREEN: { + ConRating = 0; + break; + } - case CON_LIGHTBLUE: { - ConRating = 0.2; - break; - } + case CON_LIGHTBLUE: { + ConRating = 0.2; + break; + } - case CON_BLUE: { - ConRating = 0.6; - break; - } + case CON_BLUE: { + ConRating = 0.6; + break; + } - case CON_WHITE: { - ConRating = 1.0; - break; - } + case CON_WHITE: { + ConRating = 1.0; + break; + } - case CON_YELLOW: { - ConRating = 1.2; - break; - } + case CON_YELLOW: { + ConRating = 1.2; + break; + } - case CON_RED: { - ConRating = 1.5; - break; - } + case CON_RED: { + ConRating = 1.5; + break; + } - default: { - ConRating = 0; - break; - } + default: { + ConRating = 0; + break; + } } ConfidenceCheck += ConRating; @@ -4502,8 +4389,13 @@ bool Merc::CheckConfidence() { } void Merc::MercMeditate(bool isSitting) { + // Don't try to meditate if engaged or dead + if (IsEngaged() || GetAppearance() == eaDead) + { + return; + } if(isSitting) { - // If the bot is a caster has less than 99% mana while its not engaged, he needs to sit to meditate + // If the merc is a caster and has less than 99% mana while its not engaged, he needs to sit to meditate if(GetManaRatio() < 99.0f) { if(!IsSitting()) @@ -4608,7 +4500,7 @@ float Merc::GetMaxMeleeRangeToTarget(Mob* target) { void Merc::DoClassAttacks(Mob *target) { if(target == nullptr) - return; //gotta have a target for all these + return; //gotta have a target for all these bool ca_time = classattack_timer.Check(false); @@ -4619,77 +4511,71 @@ 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 + int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will bool did_attack = false; //class specific stuff... switch(GetClass()) { - case MELEEDPS: - if(level >= 10) { - reuse = BackstabReuseTime * 1000; - TryBackstab(target, reuse); - did_attack = true; - } - break; - case TANK:{ - if(level >= RuleI(Combat, NPCBashKickLevel)){ - if(MakeRandomInt(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. - { - DoAnim(animKick); - int32 dmg = 0; - - if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ - dmg = -5; - } - else{ - if(target->CheckHitChance(this, SkillKick, 0)) { - if(RuleB(Combat, UseIntervalAC)) - dmg = GetKickDamage(); - else - dmg = MakeRandomInt(1, GetKickDamage()); - - } - } - - reuse = KickReuseTime * 1000; - DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); + case MELEEDPS: + if(level >= 10) { + reuse = BackstabReuseTime * 1000; + TryBackstab(target, reuse); did_attack = true; } - else - { - DoAnim(animTailRake); - int32 dmg = 0; + break; + case TANK:{ + if(level >= RuleI(Combat, NPCBashKickLevel)){ + if(MakeRandomInt(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. + { + DoAnim(animKick); + int32 dmg = 0; - if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ - dmg = -5; - } - else{ - if(target->CheckHitChance(this, SkillBash, 0)) { - if(RuleB(Combat, UseIntervalAC)) - dmg = GetBashDamage(); - else - dmg = MakeRandomInt(1, GetBashDamage()); + if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ + dmg = -5; } - } + else{ + if(target->CheckHitChance(this, SkillKick, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetKickDamage(); + else + dmg = MakeRandomInt(1, GetKickDamage()); - reuse = BashReuseTime * 1000; - DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); - did_attack = true; + } + } + + reuse = KickReuseTime * 1000; + DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); + did_attack = true; + } + else + { + DoAnim(animTailRake); + int32 dmg = 0; + + if(GetWeaponDamage(target, (const Item_Struct*)nullptr) <= 0){ + dmg = -5; + } + else{ + if(target->CheckHitChance(this, SkillBash, 0)) { + if(RuleB(Combat, UseIntervalAC)) + dmg = GetBashDamage(); + else + dmg = MakeRandomInt(1, GetBashDamage()); + } + } + + reuse = BashReuseTime * 1000; + DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); + did_attack = true; + } } - } - break; - } + break; + } } - 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) @@ -4747,21 +4633,23 @@ Mob* Merc::GetOwnerOrSelf() { bool Merc::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) { if(!NPC::Death(killerMob, damage, spell, attack_skill)) + { return false; + } Save(); - Mob *give_exp = hate_list.GetDamageTop(this); - Client *give_exp_client = nullptr; + //no corpse, no exp if we're a merc. + //We'll suspend instead, since that's what live does. + //Not actually sure live supports 'depopping' merc corpses. + //if(entity_list.GetCorpseByID(GetID())) + // entity_list.GetCorpseByID(GetID())->Depop(); - if(give_exp && give_exp->IsClient()) - give_exp_client = give_exp->CastToClient(); - - bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); - - //no corpse, no exp if we're a merc. We'll suspend instead, since that's what live does. I'm not actually sure live supports 'depopping' merc corpses. - if(entity_list.GetCorpseByID(GetID())) - entity_list.GetCorpseByID(GetID())->Depop(); + // If client is in zone, suspend merc, else depop it. + if (!Suspend()) + { + Depop(); + } return true; } @@ -4807,8 +4695,8 @@ const char* Merc::GetRandomName(){ bool vwl=false; bool dbl=false; if (rndnum>63) - { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel - rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" + { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel + rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" rndname[0]=paircons[rndnum]; rndname[1]=paircons[rndnum+1]; n=2; @@ -4827,37 +4715,37 @@ const char* Merc::GetRandomName(){ for (int i=n;i46) - { // pick a cons pair - if (i>namlen-3) // last 2 chars in name? - { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" + { // pick a cons pair + if (i>namlen-3) // last 2 chars in name? + { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" rndnum=MakeRandomInt(0, 7)*2; } else - { // pick any from the set + { // pick any from the set rndnum=(rndnum-47)*2; } rndname[i]=paircons[rndnum]; rndname[i+1]=paircons[rndnum+1]; - dlc=true; // flag keeps second letter from being doubled below + dlc=true; // flag keeps second letter from being doubled below i+=1; } else - { // select a single cons + { // select a single cons rndname[i]=cons[rndnum]; } } else - { // select a vowel + { // select a vowel rndname[i]=vowels[MakeRandomInt(0, 16)]; } vwl=!vwl; if (!dbl && !dlc) - { // one chance at double letters in name - if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name + { // one chance at double letters in name + if (!MakeRandomInt(0, i+9)) // chances decrease towards end of name { rndname[i+1]=rndname[i]; dbl=true; @@ -4888,7 +4776,6 @@ const char* Merc::GetRandomName(){ return name; } - bool Compare_Merc_Spells(MercSpell i, MercSpell j); bool Compare_Merc_Spells(MercSpell i, MercSpell j) @@ -4942,7 +4829,7 @@ bool Merc::LoadMercSpells() { GetMercOwner()->Message(7, "Mercenary Debug: Spell list for merc."); for (int i = merc_spells.size() - 1; i >= 0; i--) { - GetMercOwner()->Message(7, "%i] Slot: %i, SpellID: %i, Type: %i, Stance: %i, Proc Chance: %i", i, merc_spells[i].slot, merc_spells[i].spellid, merc_spells[i].type, merc_spells[i].stance, merc_spells[i].proc_chance); + GetMercOwner()->Message(7, "%i] Slot: %i, SpellID: %i, Type: %i, Stance: %i, Proc Chance: %i", i, merc_spells[i].slot, merc_spells[i].spellid, merc_spells[i].type, merc_spells[i].stance, merc_spells[i].proc_chance); }*/ } @@ -4950,28 +4837,33 @@ bool Merc::LoadMercSpells() { } bool Merc::Save() { - bool Result = false; + if(database.SaveMerc(this)){ - Result = true; + return true; } - return Result; + return false; } Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, bool updateFromDB) { - if(c) { - if(c->GetMercID()) { + if(c) + { + if(c->GetMercID()) + { merc_template = zone->GetMercTemplate(c->GetMercInfo().MercTemplateID); } } //get mercenary data - if(merc_template) { - const NPCType* npc_type_to_copy = database.GetMercType(merc_template->MercNPCID, merc_template->RaceID, c->GetLevel()); //TODO: Maybe add a way of updating client merc stats in a seperate function? like, for example, on leveling up. + if(merc_template) + { + //TODO: Maybe add a way of updating client merc stats in a seperate function? like, for example, on leveling up. + const NPCType* npc_type_to_copy = database.GetMercType(merc_template->MercNPCID, merc_template->RaceID, c->GetLevel()); if(npc_type_to_copy != nullptr) { - NPCType* npc_type = new NPCType; //This is actually a very terrible method of assigning stats, and should be changed at some point. See the comment in merc's deconstructor. + //This is actually a very terrible method of assigning stats, and should be changed at some point. See the comment in merc's deconstructor. + NPCType* npc_type = new NPCType; memset(npc_type, 0, sizeof(NPCType)); memcpy(npc_type, npc_type_to_copy, sizeof(NPCType)); if(c && !updateFromDB) @@ -4980,43 +4872,59 @@ Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, { snprintf(c->GetMercInfo().merc_name, 64, "%s", GetRandomName()); //sanity check. } - snprintf(c->GetEPP().merc_name, 64, "%s", c->GetMercInfo().merc_name); snprintf(npc_type->name, 64, "%s", c->GetMercInfo().merc_name); } - uint8 gender = 0; - if(merchant_id > 0) { + + npc_type->race = merc_template->RaceID; + + // Use the Gender and Size of the Merchant if possible + uint8 tmpgender = 0; + float tmpsize = 6.0f; + if(merchant_id > 0) + { NPC* tar = entity_list.GetNPCByID(merchant_id); - if(tar) { - gender = Mob::GetDefaultGender(npc_type->race, tar->GetGender()); + if(tar) + { + tmpgender = tar->GetGender(); + tmpsize = tar->GetSize(); } + else + { + tmpgender = Mob::GetDefaultGender(npc_type->race, c->GetMercInfo().Gender); + } + } - else { - gender = c->GetMercInfo().Gender; + else + { + tmpgender = c->GetMercInfo().Gender; + tmpsize = c->GetMercInfo().MercSize; } - sprintf(npc_type->lastname, "%s's %s", c->GetName(), "Mercenary"); - npc_type->gender = gender; + sprintf(npc_type->lastname, "%s's Mercenary", c->GetName()); + npc_type->gender = tmpgender; + npc_type->size = tmpsize; npc_type->loottable_id = 0; // Loottable has to be 0, otherwise we'll be leavin' some corpses! npc_type->npc_id = 0; //NPC ID has to be 0, otherwise db gets all confuzzled. - npc_type->race = merc_template->RaceID; npc_type->class_ = merc_template->ClassID; npc_type->maxlevel = 0; //We should hard-set this to override scalerate's functionality in the NPC class when it is constructed. Merc* merc = new Merc(npc_type, c->GetX(), c->GetY(), c->GetZ(), 0); - if(merc) { + if(merc) + { merc->SetMercData( merc_template->MercTemplateID ); database.LoadMercEquipment(merc); merc->UpdateMercStats(c); - if(updateFromDB) { + if(updateFromDB) + { database.LoadCurrentMerc(c); merc->SetMercID(c->GetMercInfo().mercid); snprintf(merc->name, 64, "%s", c->GetMercInfo().merc_name); - snprintf(c->GetEPP().merc_name, 64, "%s", c->GetMercInfo().merc_name); merc->SetSuspended(c->GetMercInfo().IsSuspended); merc->gender = c->GetMercInfo().Gender; + merc->size = c->GetMercInfo().MercSize; merc->SetHP(c->GetMercInfo().hp <= 0 ? merc->GetMaxHP() : c->GetMercInfo().hp); merc->SetMana(c->GetMercInfo().hp <= 0 ? merc->GetMaxMana() : c->GetMercInfo().mana); merc->SetEndurance(c->GetMercInfo().endurance); @@ -5031,6 +4939,11 @@ Merc* Merc::LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, merc->drakkin_tattoo = c->GetMercInfo().drakkinTattoo; merc->drakkin_details = c->GetMercInfo().drakkinDetails; } + else + { + // Give Random Features to newly hired Mercs + merc->RandomizeFeatures(false, true); + } if(merc->GetMercID()) { database.LoadMercBuffs(merc); @@ -5050,7 +4963,8 @@ void Merc::UpdateMercInfo(Client *c) { snprintf(c->GetMercInfo().merc_name, 64, "%s", name); c->GetMercInfo().mercid = GetMercID(); c->GetMercInfo().IsSuspended = IsSuspended(); - c->GetMercInfo().Gender = gender; + c->GetMercInfo().Gender = GetGender(); + c->GetMercInfo().MercSize = GetSize(); c->GetMercInfo().hp = GetHP(); c->GetMercInfo().mana = GetMana(); c->GetMercInfo().endurance = GetEndurance(); @@ -5110,7 +5024,18 @@ void Merc::UpdateMercStats(Client *c) { } } -void Merc::UpdateMercAppearance(Client *c) { +void Merc::UpdateMercAppearance() { + // Copied from Bot Code: + uint32 itemID = NO_ITEM; + uint8 materialFromSlot = _MaterialInvalid; + for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) { + itemID = equipment[i]; + if(itemID != NO_ITEM) { + materialFromSlot = Inventory::CalcMaterialFromSlot(i); + if(materialFromSlot != _MaterialInvalid) + this->SendWearChange(materialFromSlot); + } + } } void Merc::AddItem(uint8 slot, uint32 item_id) { @@ -5118,8 +5043,6 @@ void Merc::AddItem(uint8 slot, uint32 item_id) { } bool Merc::Spawn(Client *owner) { - if(!RuleB(Mercs, AllowMercs)) - return false; if(!owner) return false; @@ -5132,26 +5055,159 @@ bool Merc::Spawn(Client *owner) { entity_list.AddMerc(this, true, true); SendPosition(); + + if (MERC_DEBUG > 0) + owner->Message(7, "Mercenary Debug: Spawn."); + + //UpdateMercAppearance(); //printf("Spawned Merc with ID %i\n", npc->GetID()); fflush(stdout); - /* - uint32 itemID = NO_ITEM; - uint8 materialFromSlot = _MaterialInvalid; - for(int i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::EQUIPMENT_END; ++i) { - itemID = GetMercItemBySlot(i); - if(itemID != NO_ITEM) { - materialFromSlot = Inventory::CalcMaterialFromSlot(i); - if(materialFromSlot != _MaterialInvalid) { - this->SendWearChange(materialFromSlot); - } - } - } - */ - return true; } +void Client::SendMercResponsePackets(uint32 ResponseType) +{ + switch (ResponseType) + { + case 0: // Mercenary Spawned Successfully? + SendMercMerchantResponsePacket(0); + break; + case 1: //You do not have enough funds to make that purchase! + SendMercMerchantResponsePacket(1); + break; + case 2: //Mercenary does not exist! + SendMercMerchantResponsePacket(2); + break; + case 3: //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + break; + case 4: //Mercenaries are not allowed in raids! + SendMercMerchantResponsePacket(4); + break; + case 5: //You already have a pending mercenary purchase! + SendMercMerchantResponsePacket(5); + break; + case 6: //You have the maximum number of mercenaries. You must dismiss one before purchasing a new one! + SendMercMerchantResponsePacket(6); + break; + case 7: //You must dismiss your suspended mercenary before purchasing a new one! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(7); + else + //You have the maximum number of mercenaries. You must dismiss one before purchasing a new one! + SendMercMerchantResponsePacket(6); + break; + case 8: //You can not purchase a mercenary because your group is full! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(8); + else + SendMercMerchantResponsePacket(7); + break; + case 9: //You can not purchase a mercenary because you are in combat! + if (GetClientVersion() < EQClientRoF) + //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + else + SendMercMerchantResponsePacket(8); + break; + case 10: //You have recently dismissed a mercenary and must wait a few more seconds before you can purchase a new one! + if (GetClientVersion() < EQClientRoF) + //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + else + SendMercMerchantResponsePacket(9); + break; + case 11: //An error occurred created your mercenary! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(9); + else + SendMercMerchantResponsePacket(10); + break; + case 12: //Upkeep Charge Message + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(10); + else + SendMercMerchantResponsePacket(11); + break; + case 13: // ??? + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(11); + else + SendMercMerchantResponsePacket(12); + break; + case 14: //You ran out of funds to pay for your mercenary! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(12); + else + SendMercMerchantResponsePacket(13); + break; + case 15: // ??? + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(13); + else + SendMercMerchantResponsePacket(14); + break; + case 16: //Your mercenary is about to be suspended due to insufficient funds! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(14); + else + SendMercMerchantResponsePacket(15); + break; + case 17: //There is no mercenary liaison nearby! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(15); + else + SendMercMerchantResponsePacket(16); + break; + case 18: //You are too far from the liaison! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(16); + else + SendMercMerchantResponsePacket(17); + break; + case 19: //You do not meet the requirements for that mercenary! + if (GetClientVersion() < EQClientRoF) + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(18); + break; + case 20: //You are unable to interact with the liaison! + if (GetClientVersion() < EQClientRoF) + //You are too far from the liaison! + SendMercMerchantResponsePacket(16); + else + SendMercMerchantResponsePacket(19); + break; + case 21: //You do not have a high enough membership level to purchase this mercenary! + if (GetClientVersion() < EQClientRoF) + //You do not meet the requirements for that mercenary! + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(20); + break; + case 22: //Your purchase has failed because this mercenary requires a Gold membership! + if (GetClientVersion() < EQClientRoF) + //You do not meet the requirements for that mercenary! + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(21); + break; + case 23: //Your purchase has failed because this mercenary requires at least a Silver membership! + if (GetClientVersion() < EQClientRoF) + //You do not meet the requirements for that mercenary! + SendMercMerchantResponsePacket(17); + else + SendMercMerchantResponsePacket(22); + break; + default: //Mercenary failed to spawn! + SendMercMerchantResponsePacket(3); + break; + } + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercResponsePackets %i.", ResponseType); +} + void Client::UpdateMercTimer() { Merc *merc = GetMerc(); @@ -5160,135 +5216,93 @@ void Client::UpdateMercTimer() { if(GetMercTimer()->Check()) { - uint32 upkeep = Merc::CalcUpkeepCost(merc->GetMercTemplateID(), GetLevel()); + uint32 upkeep = merc->CalcUpkeepCost(merc->GetMercTemplateID(), GetLevel()); - if(CheckCanRetainMerc(upkeep)) { - if(RuleB(Mercs, ChargeMercUpkeepCost)) { + if(CheckCanRetainMerc(upkeep)) + { + if(RuleB(Mercs, ChargeMercUpkeepCost)) + { TakeMoneyFromPP((upkeep * 100), true); } } - else { + else + { merc->Suspend(); return; } + // Reset the upkeep timer GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - SendMercTimerPacket(GetMercID(), 5, 0, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + SendMercTimer(merc); GetMercTimer()->Start(RuleI(Mercs, UpkeepIntervalMS)); GetMercTimer()->SetTimer(GetMercInfo().MercTimerRemaining); - // Send upkeep charge message and reset the upkeep timer - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(10); - else - SendMercMerchantResponsePacket(11); + // Send upkeep charge message + SendMercResponsePackets(12); - /* - uint32 upkeep_plat = 0; - uint32 upkeep_gold = 0; - - if (upkeep >= 10) - upkeep_plat = (int)(upkeep / 10); - - if (upkeep - (upkeep_plat * 10) >= 1) - upkeep_gold = (int)((upkeep - (upkeep_plat * 10)) / 100); - */ + // Warn that mercenary is about to be suspended due to insufficient funds (on next upkeep) + if (RuleB(Mercs, ChargeMercUpkeepCost) && upkeep > 0 && !HasMoney(upkeep * 100)) + { + SendMercResponsePackets(16); + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: UpdateMercTimer Complete."); // Normal upkeep charge message //Message(7, "You have been charged a mercenary upkeep cost of %i plat, and %i gold and your mercenary upkeep cost timer has been reset to 15 minutes.", upkeep_plat, upkeep_gold, (int)(RuleI(Mercs, UpkeepIntervalMS) / 1000 / 60)); // Message below given when too low level to be charged - // Temporarily enabled for all upkeep costs until mercenary stuff is completed //Message(7, "Your mercenary waived an upkeep cost of %i plat, and %i gold or %i %s and your mercenary upkeep cost timer has been reset to %i minutes", upkeep_plat, upkeep_gold, 1, "Bayle Marks", (int)(RuleI(Mercs, UpkeepIntervalMS) / 1000 / 60)); } } } bool Client::CheckCanHireMerc(Mob* merchant, uint32 template_id) { + + if (!CheckCanSpawnMerc(template_id)) + { + return false; + } + MercTemplate* mercTemplate = zone->GetMercTemplate(template_id); - //invalid merc data - if(!mercTemplate) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(9); - else - SendMercMerchantResponsePacket(10); + //check for suspended merc + if(GetMercInfo().mercid != 0 && GetMercInfo().IsSuspended) { + SendMercResponsePackets(6); return false; } - //check client version - if(GetClientVersion() < mercTemplate->ClientVersion) { - SendMercMerchantResponsePacket(3); + // Check if max number of mercs is already reached + if(GetNumMercs() >= MAXMERCS) { + SendMercResponsePackets(6); return false; } - if(GetClientVersion() >= EQClientRoF && GetNumMercs() >= MAXMERCS) { - SendMercMerchantResponsePacket(6); - return false; - } - else if(GetMerc()) { //check for current merc - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(6); - else - SendMercMerchantResponsePacket(6); - return false; - } - else if(GetMercInfo().mercid != 0 && GetMercInfo().IsSuspended) { //has suspended merc - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(7); - else - SendMercMerchantResponsePacket(6); - return false; - } - - //check for sufficient funds - if(RuleB(Mercs, ChargeMercPurchaseCost)) { - uint32 cost = Merc::CalcPurchaseCost(template_id, GetLevel()) * 100; // Cost is in gold - if(cost > 0 && !HasMoney(cost)) { - SendMercMerchantResponsePacket(1); - return false; - } - } - - //check for raid - if(HasRaid()) { - SendMercMerchantResponsePacket(4); - return false; - } - - //check group size - if(HasGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(8); - else - SendMercMerchantResponsePacket(7); - return false; - } - - //check in combat - if(GetClientVersion() >= EQClientRoF && GetAggroCount() > 0) { - SendMercMerchantResponsePacket(8); - return false; - } - - //check for valid merchant - can check near area for any merchants + //check for valid merchant if(!merchant) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(14); - else - SendMercMerchantResponsePacket(16); + SendMercResponsePackets(17); return false; } //check for merchant too far away if(DistNoRoot(*merchant) > USE_NPC_RANGE2) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(15); - else - SendMercMerchantResponsePacket(17); + SendMercResponsePackets(18); return false; } + //check for sufficient funds and remove them last + if(RuleB(Mercs, ChargeMercPurchaseCost)) { + uint32 cost = Merc::CalcPurchaseCost(template_id, GetLevel()) * 100; // Cost is in gold + if(cost > 0 && !HasMoney(cost)) { + SendMercResponsePackets(1); + return false; + } + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: CheckCanHireMerc True."); + return true; } @@ -5299,7 +5313,7 @@ bool Client::CheckCanRetainMerc(uint32 upkeep) { if(RuleB(Mercs, ChargeMercPurchaseCost)) { if(merc) { if(upkeep > 0 && !HasMoney(upkeep * 100)) { - SendMercMerchantResponsePacket(1); + SendMercResponsePackets(14); return false; } } @@ -5308,117 +5322,141 @@ bool Client::CheckCanRetainMerc(uint32 upkeep) { return true; } +bool Client::CheckCanSpawnMerc(uint32 template_id) { + + // Check if mercs are enabled globally + if(!RuleB(Mercs, AllowMercs)) + { + return false; + } + + // Check if zone allows mercs + if(!zone->AllowMercs()) + { + SendMercResponsePackets(3); + return false; + } + + MercTemplate* mercTemplate = zone->GetMercTemplate(template_id); + + // Invalid merc data + if(!mercTemplate) + { + SendMercResponsePackets(11); + return false; + } + + // Check client version + if(GetClientVersion() < mercTemplate->ClientVersion) + { + SendMercResponsePackets(3); + return false; + } + + // Check for raid + if(HasRaid()) + { + SendMercResponsePackets(4); + return false; + } + + // Check group size + if(GetGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) // database.GroupCount(GetGroup()->GetID()) + { + SendMercResponsePackets(8); + return false; + } + + // Check in combat + if(GetAggroCount() > 0) + { + SendMercResponsePackets(9); + return false; + } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: CheckCanSpawnMerc True."); + + return true; +} + bool Client::CheckCanUnsuspendMerc() { + + if (!CheckCanSpawnMerc(GetMercInfo().MercTemplateID)) + { + return false; + } + MercTemplate* mercTemplate = zone->GetMercTemplate(GetMercInfo().MercTemplateID); - //invalid merc data - if(!mercTemplate) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(9); - else - SendMercMerchantResponsePacket(10); - return false; - } - - //check client version - if(GetClientVersion() < mercTemplate->ClientVersion) { - SendMercMerchantResponsePacket(3); - return false; - } - - //check for raid - if(HasRaid()) { - SendMercMerchantResponsePacket(4); - return false; - } - - //check group size - if(HasGroup() && GetGroup()->GroupCount() >= MAX_GROUP_MEMBERS) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(8); - else - SendMercMerchantResponsePacket(7); - return false; - } - - //check if zone allows mercs - if(!zone->AllowMercs()) { - if (GetClientVersion() < EQClientRoF) - SendMercMerchantResponsePacket(4); // ?? - else - SendMercMerchantResponsePacket(4); // ?? - return false; - } - - //check in combat - if(GetClientVersion() >= EQClientRoF && GetAggroCount() > 0) { - SendMercMerchantResponsePacket(8); - return false; - } - if(!GetPTimers().Expired(&database, pTimerMercSuspend, false)) { - SendMercMerchantResponsePacket(16); - Message(0, "You must wait %i seconds before unsuspending your mercenary.", GetPTimers().GetRemainingTime(pTimerMercSuspend)); //todo: find this packet response and tell them properly. - return false; - } - return true; -} - -bool Client::CheckCanDismissMerc() { - if(!GetMerc()) { - Message(7, "You have no mercenary to dismiss."); - return false; - } - - if(GetMerc()->IsCasting()) { - Message(7, "Unable to dismiss mercenary."); + SendMercResponsePackets(10); + //TODO: find this packet response and tell them properly. + Message(0, "You must wait %i seconds before unsuspending your mercenary.", GetPTimers().GetRemainingTime(pTimerMercSuspend)); return false; } + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: CheckCanUnsuspendMerc True."); return true; } -void Client::CheckMercSuspendTimer() -{ - if(GetMercInfo().SuspendedTime != 0) { - if(time(nullptr) >= GetMercInfo().SuspendedTime){ - SendMercSuspendResponsePacket(0); +void Client::CheckMercSuspendTimer() { + + if(GetMercInfo().SuspendedTime != 0) + { + if(time(nullptr) >= GetMercInfo().SuspendedTime) + { + GetMercInfo().SuspendedTime = 0; + SendMercResponsePackets(0); + SendMercSuspendResponsePacket(GetMercInfo().SuspendedTime); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: CheckMercSuspendTimer Ready."); } } } -void Client::SuspendMercCommand() -{ - bool ExistsMerc = GetMercInfo().MercTemplateID != 0; - if(ExistsMerc == true) - { - if(GetMercInfo().IsSuspended) { - //p_timers.Enable(pTimerMercReuse); +void Client::SuspendMercCommand() { + + if(GetMercInfo().MercTemplateID != 0) + { + if(GetMercInfo().IsSuspended) + { + if(!CheckCanUnsuspendMerc()) + { + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SuspendMercCommand Unable to Unsuspend."); - // Set time remaining to max on unsuspend - there is a charge for unsuspending as well - // GetEPP().mercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); - if(!CheckCanUnsuspendMerc()){ return; } // Get merc, assign it to client & spawn Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); - if(merc) { + if(merc) + { SpawnMerc(merc, true); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SuspendMercCommand Successful Unsuspend."); } - else { + else + { //merc failed to spawn - SendMercMerchantResponsePacket(3); + SendMercResponsePackets(3); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SuspendMercCommand Failed to Spawn Merc."); } } else { Merc* CurrentMerc = GetMerc(); - if(CurrentMerc && GetMercID()) { - //CurrentMerc->Save(); + if(CurrentMerc && GetMercID()) + { CurrentMerc->Suspend(); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SuspendMercCommand Successful Suspend."); } } } @@ -5427,68 +5465,116 @@ void Client::SuspendMercCommand() // Handles all client zone change event void Merc::ProcessClientZoneChange(Client* mercOwner) { - if(mercOwner) { + + if(mercOwner) + { Zone(); } } -void Client::SpawnMercOnZone() -{ +void Client::SpawnMercOnZone() { + if(!RuleB(Mercs, AllowMercs)) return; if (GetMerc()) return; - bool ExistsMerc = GetEPP().merc_name[0] != 0; - if(ExistsMerc == true) + if(database.LoadMercInfo(this)) { - if(!GetMercInfo().IsSuspended) { + if(!GetMercInfo().IsSuspended) + { GetMercInfo().SuspendedTime = 0; // Get merc, assign it to client & spawn - if(database.LoadMercInfo(this)) { - if(!CheckCanUnsuspendMerc()){ - return; - } - Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); - if(merc) { - SpawnMerc(merc, false); - SendMercTimerPacket(merc->GetID(), 5, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); - } + Merc* merc = Merc::LoadMerc(this, &zone->merc_templates[GetMercInfo().MercTemplateID], 0, true); + if(merc) + { + SpawnMerc(merc, false); } + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SpawnMercOnZone Normal Merc."); } else { - // Send Mercenary Status/Timer packet - SendMercTimerPacket(0, 1, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); - - SendMercPersonalInfo(); - - if(GetMercInfo().SuspendedTime != 0) { - if(time(nullptr) >= GetMercInfo().SuspendedTime){ - GetMercInfo().SuspendedTime = 0; + int32 TimeDiff = GetMercInfo().SuspendedTime - time(nullptr); + if (TimeDiff > 0) + { + if (!GetPTimers().Enabled(pTimerMercSuspend)) + { + // Start the timer to send the packet that refreshes the Unsuspend Button + GetPTimers().Start(pTimerMercSuspend, TimeDiff); } } - SendMercSuspendResponsePacket(GetMercInfo().SuspendedTime); + // Send Mercenary Status/Timer packet + SendMercTimer(GetMerc()); + //SendMercPersonalInfo(); + CheckMercSuspendTimer(); + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SpawnMercOnZone Suspended Merc."); } } + else + { + // No Merc Hired + SendClearMercInfo(); + } } -void Client::SpawnMerc(Merc* merc, bool setMaxStats) -{ - if(!RuleB(Mercs, AllowMercs)) - return; +void Client::SendMercTimer(Merc* merc) { - if(merc) { - merc->Spawn(this); - merc->SetSuspended(false); - SetMerc(merc); - merc->Unsuspend(setMaxStats); - merc->SetStance(GetMercInfo().Stance); + if (GetMercInfo().mercid == 0) + { + return; } + + if (!merc) + { + SendMercTimerPacket(NO_MERC_ID, MERC_STATE_SUSPENDED, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercTimer No Merc."); + } + else if (merc->IsSuspended()) + { + SendMercTimerPacket(NO_MERC_ID, MERC_STATE_SUSPENDED, GetMercInfo().SuspendedTime, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercTimer Suspended Merc."); + } + else + { + SendMercTimerPacket(merc->GetID(), MERC_STATE_NORMAL, NOT_SUSPENDED_TIME, GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SendMercTimer Normal Merc."); + } + +} + +void Client::SpawnMerc(Merc* merc, bool setMaxStats) { + + if (!merc || !CheckCanSpawnMerc(merc->GetMercTemplateID())) + { + if (merc) + { + merc->Suspend(); + } + return; + } + + merc->Spawn(this); + merc->SetSuspended(false); + SetMerc(merc); + merc->Unsuspend(setMaxStats); + merc->SetStance(GetMercInfo().Stance); + + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SpawnMerc Success."); + + return; + } bool Merc::Suspend() { + Client* mercOwner = GetMercOwner(); if(!mercOwner) @@ -5502,18 +5588,42 @@ bool Merc::Suspend() { mercOwner->GetMercInfo().Stance = GetStance(); Save(); mercOwner->GetMercTimer()->Disable(); - mercOwner->SendMercSuspendResponsePacket(mercOwner->GetMercInfo().SuspendedTime); - + mercOwner->SendMercTimer(this); + Depop(); - mercOwner->SendMercTimerPacket(0, 1, mercOwner->GetMercInfo().SuspendedTime, mercOwner->GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + // Start the timer to send the packet that refreshes the Unsuspend Button + mercOwner->GetPTimers().Start(pTimerMercSuspend, RuleI(Mercs, SuspendIntervalS)); + + if (MERC_DEBUG > 0) + mercOwner->Message(7, "Mercenary Debug: Suspend Complete."); + return true; } +bool Client::MercOnlyOrNoGroup() { + + if (!GetGroup()) + { + return true; + } + if (GetMerc()) + { + if (GetMerc()->HasGroup() && GetMerc()->GetGroup() == GetGroup()) + { + if (GetGroup()->GroupCount() < 3) + { + return true; + } + } + } + return false; +} + bool Merc::Unsuspend(bool setMaxStats) { + Client* mercOwner = nullptr; - bool loaded = false; if(GetMercOwner()) { mercOwner = GetMercOwner(); @@ -5522,10 +5632,9 @@ bool Merc::Unsuspend(bool setMaxStats) { if(!mercOwner) return false; - if(GetID()) { - uint32 mercState = 5; - uint32 suspendedTime = 0; - + if(GetID()) + { + // Set time remaining to max on unsuspend - there is a charge for unsuspending as well SetSuspended(false); mercOwner->GetMercInfo().mercid = GetMercID(); @@ -5534,91 +5643,65 @@ bool Merc::Unsuspend(bool setMaxStats) { mercOwner->SendMercenaryUnsuspendPacket(0); mercOwner->SendMercenaryUnknownPacket(1); mercOwner->GetMercInfo().SuspendedTime = 0; + // Reset the upkeep timer + mercOwner->GetMercInfo().MercTimerRemaining = RuleI(Mercs, UpkeepIntervalMS); mercOwner->GetMercTimer()->Start(RuleI(Mercs, UpkeepIntervalMS)); - mercOwner->GetMercTimer()->SetTimer(mercOwner->GetMercInfo().MercTimerRemaining); - mercOwner->SendMercTimerPacket(GetID(), mercState, suspendedTime, mercOwner->GetMercInfo().MercTimerRemaining, RuleI(Mercs, SuspendIntervalMS)); + //mercOwner->GetMercTimer()->SetTimer(mercOwner->GetMercInfo().MercTimerRemaining); + mercOwner->SendMercTimer(this); if(!mercOwner->GetPTimers().Expired(&database, pTimerMercSuspend, false)) mercOwner->GetPTimers().Clear(&database, pTimerMercSuspend); - mercOwner->SendMercPersonalInfo(); - Group* g = entity_list.GetGroupByClient(mercOwner); - - if(!g) { //nobody from our group is here... start a new group - g = new Group(mercOwner); - - if(!g) { - delete g; - g = nullptr; - return false; - } - - entity_list.AddGroup(g); - - if(g->GetID() == 0) { - delete g; - g = nullptr; - return false; - } - - if(AddMercToGroup(this, g)) { - entity_list.AddGroup(g, g->GetID()); - database.SetGroupLeaderName(g->GetID(), mercOwner->GetName()); - database.SetGroupID(mercOwner->GetName(), g->GetID(), mercOwner->CharacterID()); - database.SetGroupID(this->GetName(), g->GetID(), mercOwner->CharacterID(), true); - database.RefreshGroupFromDB(mercOwner); - g->SaveGroupLeaderAA(); - loaded = true; - } - else { - g->DisbandGroup(); - } - } //else, somebody from our group is already here... - else if (AddMercToGroup(this, mercOwner->GetGroup())) { - database.SetGroupID(GetName(), mercOwner->GetGroup()->GetID(), mercOwner->CharacterID(), true); - database.RefreshGroupFromDB(mercOwner); - - loaded = true; - } - else { - if(MERC_DEBUG > 0) - mercOwner->Message(7, "Mercenary failed to join the group - Suspending"); - - Suspend(); - } - - - if(loaded) { - LoadMercSpells(); - - if(setMaxStats) { + if (MercJoinClientGroup()) + { + if(setMaxStats) + { SetHP(GetMaxHP()); SetMana(GetMaxMana()); SetEndurance(GetMaxEndurance()); } + + //check for sufficient funds and remove them last + if(RuleB(Mercs, ChargeMercUpkeepCost)) + { + uint32 cost = CalcUpkeepCost(GetMercTemplateID(), GetLevel()) * 100; // Cost is in gold + if(cost > 0 && !mercOwner->HasMoney(cost)) + { + mercOwner->SendMercResponsePackets(1); + Suspend(); + return false; + } + } + Save(); } } return true; } -bool Merc::Dismiss(){ +bool Client::DismissMerc(uint32 MercID) { - Client* mercOwner = GetMercOwner(); + bool Dismissed = true; + if (!database.DeleteMerc(MercID)) + { + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Dismiss Failed for MercID %i", MercID); + Dismissed = false; + } + else + { + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Dismiss Successful."); + } + + if (GetMerc()) + { + GetMerc()->Depop(); + } - if(!mercOwner) - return false; + SendClearMercInfo(); + SetMerc(nullptr); - mercOwner->SendClearMercInfo(); - - //SetMercEntityID(0); - - mercOwner->SetMerc(0); - - database.DeleteMerc(GetMercID()); - - Depop(); - - return true; + return Dismissed; } void Merc::Zone() { @@ -5627,18 +5710,27 @@ void Merc::Zone() { } void Merc::Depop() { + WipeHateList(); - entity_list.RemoveMerc(this->GetID()); - entity_list.RemoveFromHateLists(this); - if(HasGroup()) - Merc::RemoveMercFromGroup(this, GetGroup()); - - if(HasPet()) { - GetPet()->Depop(); + if(IsCasting()) + { + InterruptSpell(); } - SetOwnerID(0); + entity_list.RemoveFromHateLists(this); + + if(GetGroup()) + { + RemoveMercFromGroup(this, GetGroup()); + } + + entity_list.RemoveMerc(this->GetID()); + + if(HasPet()) + { + GetPet()->Depop(); + } p_depop = true; @@ -5646,16 +5738,23 @@ void Merc::Depop() { } bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { + bool Result = false; - if(merc && group) { - if(merc->HasGroup()) { - if(!group->IsLeader(merc)) { + if(merc && group) + { + if(merc->HasGroup()) + { + if(!group->IsLeader(merc)) + { merc->SetFollowID(0); - if(group->DelMember(merc)) { + if(group->DelMember(merc, true)) + { if(merc->GetMercCharacterID() != 0) + { database.SetGroupID(merc->GetName(), 0, merc->GetMercCharacterID(), true); + } } if(group->GroupCount() <= 1 && ZoneLoaded) @@ -5663,21 +5762,31 @@ bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { group->DisbandGroup(); } } - else { - for(int i = 0; i < MAX_GROUP_MEMBERS; i++) { + else + { + // A merc is group leader - Disband and re-group each member with their mercs + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) + { + if(!group->members[i]) + continue; + + if(!group->members[i]->IsClient()) + continue; + + group->members[i]->CastToClient()->LeaveGroup(); + } + for(int i = 0; i < MAX_GROUP_MEMBERS; i++) + { if(!group->members[i]) continue; if(!group->members[i]->IsMerc()) continue; - Merc* groupmerc = group->members[i]->CastToMerc(); - - groupmerc->SetOwnerID(0); + group->members[i]->CastToMerc()->MercJoinClientGroup(); } - + // Group should be removed by now, but just in case: group->DisbandGroup(); - database.SetGroupID(merc->GetCleanName(), 0, merc->GetMercCharacterID(), true); } Result = true; @@ -5687,21 +5796,119 @@ bool Merc::RemoveMercFromGroup(Merc* merc, Group* group) { return Result; } +bool Merc::MercJoinClientGroup() { + + Client* mercOwner = nullptr; + + if(GetMercOwner()) + { + mercOwner = GetMercOwner(); + } + + if(!mercOwner) + { + Suspend(); + return false; + } + + if(GetID()) + { + if (HasGroup()) + { + RemoveMercFromGroup(this, GetGroup()); + } + + Group* g = entity_list.GetGroupByClient(mercOwner); + + //nobody from our group is here... start a new group + if(!g) + { + g = new Group(mercOwner); + + if(!g) + { + delete g; + g = nullptr; + return false; + } + + entity_list.AddGroup(g); + + if(g->GetID() == 0) + { + delete g; + g = nullptr; + return false; + } + + if(AddMercToGroup(this, g)) + { + entity_list.AddGroup(g, g->GetID()); + database.SetGroupLeaderName(g->GetID(), mercOwner->GetName()); + database.SetGroupID(mercOwner->GetName(), g->GetID(), mercOwner->CharacterID(), false); + database.SetGroupID(this->GetName(), g->GetID(), mercOwner->CharacterID(), true); + database.RefreshGroupFromDB(mercOwner); + g->SaveGroupLeaderAA(); + if(MERC_DEBUG > 0) + mercOwner->Message(7, "Mercenary Debug: Mercenary joined new group."); + } + else + { + g->DisbandGroup(); + Suspend(); + + if(MERC_DEBUG > 0) + mercOwner->Message(7, "Mercenary Debug: Mercenary disbanded new group."); + } + + } + else if (AddMercToGroup(this, mercOwner->GetGroup())) + { + // Group already exists + database.SetGroupID(GetName(), mercOwner->GetGroup()->GetID(), mercOwner->CharacterID(), true); + database.RefreshGroupFromDB(mercOwner); + // Update members that are out of zone + GetGroup()->SendGroupJoinOOZ(this); + + if(MERC_DEBUG > 0) + mercOwner->Message(7, "Mercenary Debug: Mercenary joined existing group."); + } + else + { + Suspend(); + + if(MERC_DEBUG > 0) + mercOwner->Message(7, "Mercenary Debug: Mercenary failed to join the group - Suspending"); + } + } + + return true; +} + bool Merc::AddMercToGroup(Merc* merc, Group* group) { bool Result = false; if(merc && group) { - // Remove merc from current group if any - if(merc->HasGroup()) { - Merc::RemoveMercFromGroup(merc, merc->GetGroup()); + // Remove merc from current group if it's not the destination group + if(merc->HasGroup()) + { + if(merc->GetGroup() == group && merc->GetMercOwner()) + { + // Merc is already in the destination group + merc->SetFollowID(merc->GetMercOwner()->GetID()); + return true; + } + merc->RemoveMercFromGroup(merc, merc->GetGroup()); } //Try and add the member, followed by checking if the merc owner exists. - if(group->AddMember(merc) && merc->GetMercOwner() != nullptr) { - merc->SetFollowID(merc->GetMercOwner()->GetID()); - Result = true; + if(group->AddMember(merc) && merc->GetMercOwner()) + { + merc->SetFollowID(merc->GetMercOwner()->GetID()); + Result = true; } - else { - //Suspend it if the member is not added and the merc's owner is not valid. + else + { + //Suspend it if the member is not added or the merc's owner is not valid. merc->Suspend(); } } @@ -5710,42 +5917,62 @@ bool Merc::AddMercToGroup(Merc* merc, Group* group) { } void Client::InitializeMercInfo() { - for(int i=0; i 0) + Message(7, "Mercenary Debug: GetMerc 0."); + return (nullptr); + } Merc* tmp = entity_list.GetMercByID(GetMercID()); - if(tmp == nullptr) { + if(tmp == nullptr) + { SetMercID(0); - return(nullptr); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: GetMerc No Merc."); + return (nullptr); } - if(tmp->GetOwnerID() != GetID()) { + if(tmp->GetOwnerID() != GetID()) + { SetMercID(0); - return(nullptr); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: GetMerc Owner Mismatch."); + return (nullptr); } - return(tmp); + return (tmp); } uint8 Client::GetNumMercs() { + uint8 numMercs = 0; - for(int i=0; i 0) + Message(7, "Mercenary Debug: GetNumMercs %i.", numMercs); return numMercs; } void Merc::SetMercData( uint32 template_id ) { + MercTemplate* merc_template = zone->GetMercTemplate(template_id); SetMercTemplateID( merc_template->MercTemplateID ); SetMercType( merc_template->MercType ); @@ -5754,6 +5981,7 @@ void Merc::SetMercData( uint32 template_id ) { SetTierID( merc_template->TierID ); SetCostFormula( merc_template->CostFormula ); SetMercNameType( merc_template->MercNameType ); + } MercTemplate* Zone::GetMercTemplate( uint32 template_id ) { @@ -5761,11 +5989,15 @@ MercTemplate* Zone::GetMercTemplate( uint32 template_id ) { } void Client::SetMerc(Merc* newmerc) { + Merc* oldmerc = GetMerc(); - if (oldmerc) { + if (oldmerc) + { oldmerc->SetOwnerID(0); } - if (!newmerc) { + + if (!newmerc) + { SetMercID(0); GetMercInfo().mercid = 0; GetMercInfo().MercTemplateID = 0; @@ -5775,10 +6007,12 @@ void Client::SetMerc(Merc* newmerc) { GetMercInfo().Gender = 0; GetMercInfo().State = 0; memset(GetMercInfo().merc_name, 0, 64); - memset(GetEPP().merc_name, 0, 64); - } else { + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SetMerc No Merc."); + } + else + { SetMercID(newmerc->GetID()); - //newmerc->SetMercEntityID(newmerc->GetID()); //Client* oldowner = entity_list.GetClientByID(newmerc->GetOwnerID()); newmerc->SetOwnerID(this->GetID()); newmerc->SetMercCharacterID(this->CharacterID()); @@ -5789,44 +6023,63 @@ void Client::SetMerc(Merc* newmerc) { GetMercInfo().IsSuspended = newmerc->IsSuspended(); GetMercInfo().SuspendedTime = 0; GetMercInfo().Gender = newmerc->GetGender(); - //GetMercInfo().State = newmerc->GetStance(); + GetMercInfo().State = newmerc->IsSuspended() ? MERC_STATE_SUSPENDED : MERC_STATE_NORMAL; + snprintf(GetMercInfo().merc_name, 64, "%s", newmerc->GetName()); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: SetMerc New Merc."); } } void Client::UpdateMercLevel() { Merc* merc = GetMerc(); - if (merc) { + if (merc) + { merc->UpdateMercStats(this); } } void Client::SendMercMerchantResponsePacket(int32 response_type) { // This response packet brings up the Mercenary Manager window - if(GetClientVersion() >= EQClientSoD) { + if(GetClientVersion() >= EQClientSoD) + { EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryHire, sizeof(MercenaryMerchantResponse_Struct)); MercenaryMerchantResponse_Struct* mmr = (MercenaryMerchantResponse_Struct*)outapp->pBuffer; - mmr->ResponseType = response_type; // send specified response type + mmr->ResponseType = response_type; // send specified response type FastQueuePacket(&outapp); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Sent SendMercMerchantResponsePacket %i.", response_type); } } void Client::SendMercenaryUnknownPacket(uint8 type) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnknown1, 1); outapp->WriteUInt8(type); FastQueuePacket(&outapp); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Sent SendMercenaryUnknownPacket %i.", type); + } void Client::SendMercenaryUnsuspendPacket(uint8 type) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenaryUnsuspendResponse, 1); outapp->WriteUInt8(type); FastQueuePacket(&outapp); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Sent SendMercenaryUnsuspendPacket %i.", type); + } void Client::SendMercSuspendResponsePacket(uint32 suspended_time) { + EQApplicationPacket *outapp = new EQApplicationPacket(OP_MercenarySuspendResponse, sizeof(SuspendMercenaryResponse_Struct)); SuspendMercenaryResponse_Struct* smr = (SuspendMercenaryResponse_Struct*)outapp->pBuffer; - smr->SuspendTime = suspended_time; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp + smr->SuspendTime = suspended_time; // Seen 0 (not suspended) or c9 c2 64 4f (suspended on Sat Mar 17 11:58:49 2012) - Unix Timestamp FastQueuePacket(&outapp); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Sent SendMercSuspendResponsePacket %i.", suspended_time); + } void Client::SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspended_time, int32 update_interval, int32 unk01) { @@ -5840,6 +6093,9 @@ void Client::SendMercTimerPacket(int32 entity_id, int32 merc_state, int32 suspen mss->UpdateInterval = update_interval; // Seen 900000 - 15 minutes in ms mss->MercUnk01 = unk01; // Seen 180000 - 3 minutes in ms - Used for the unsuspend button refresh timer FastQueuePacket(&outapp); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Sent SendMercTimerPacket %i, %i, %i, %i, %i.", entity_id, merc_state, suspended_time, update_interval, unk01); + } void Client::SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02) { @@ -5849,77 +6105,77 @@ void Client::SendMercAssignPacket(uint32 entityID, uint32 unk01, uint32 unk02) { mas->MercUnk01 = unk01; mas->MercUnk02 = unk02; FastQueuePacket(&outapp); + if (MERC_DEBUG > 0) + Message(7, "Mercenary Debug: Sent SendMercAssignPacket %i, %i, %i.", entityID, unk01, unk02); } -void NPC::LoadMercTypes(){ - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; +void NPC::LoadMercTypes() { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT DISTINCT MTyp.dbstring, MTyp.clientversion FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, merc_types MTyp, merc_templates MTem WHERE MME.merchant_id = %i AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id AND MMTE.merc_template_id = MTem.merc_template_id AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - MercType tempMercType; - - tempMercType.Type = atoi(DataRow[0]); - tempMercType.ClientVersion = atoi(DataRow[1]); - - mercTypeList.push_back(tempMercType); - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - Query = 0; - - if(!errorMessage.empty()) { + std::string query = StringFormat("SELECT DISTINCT MTyp.dbstring, MTyp.clientversion " + "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " + "merc_types MTyp, merc_templates MTem " + "WHERE MME.merchant_id = %i " + "AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " + "AND MMTE.merc_template_id = MTem.merc_template_id " + "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + { LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); + return; } + + for (auto row = results.begin(); row != results.end(); ++row) + { + MercType tempMercType; + + tempMercType.Type = atoi(row[0]); + tempMercType.ClientVersion = atoi(row[1]); + + mercTypeList.push_back(tempMercType); + } + } -void NPC::LoadMercs(){ +void NPC::LoadMercs() { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + std::string query = StringFormat("SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring AS merc_type_id, " + "MTem.dbstring AS merc_subtype_id, 0 AS CostFormula, " + "CASE WHEN MTem.clientversion > MTyp.clientversion " + "THEN MTem.clientversion " + "ELSE MTyp.clientversion END AS clientversion, MTem.merc_npc_type_id " + "FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, " + "merc_types MTyp, merc_templates MTem " + "WHERE MME.merchant_id = %i AND " + "MME.merc_merchant_template_id = MMTE.merc_merchant_template_id " + "AND MMTE.merc_template_id = MTem.merc_template_id " + "AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()); + auto results = database.QueryDatabase(query); - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring AS merc_type_id, MTem.dbstring AS merc_subtype_id, 0 AS CostFormula, CASE WHEN MTem.clientversion > MTyp.clientversion then MTem.clientversion ELSE MTyp.clientversion END AS clientversion, MTem.merc_npc_type_id FROM merc_merchant_entries MME, merc_merchant_template_entries MMTE, merc_types MTyp, merc_templates MTem WHERE MME.merchant_id = %i AND MME.merc_merchant_template_id = MMTE.merc_merchant_template_id AND MMTE.merc_template_id = MTem.merc_template_id AND MTem.merc_type_id = MTyp.merc_type_id;", GetNPCTypeID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - MercData tempMerc; - - tempMerc.MercTemplateID = atoi(DataRow[0]); - tempMerc.MercType = atoi(DataRow[1]); - tempMerc.MercSubType = atoi(DataRow[2]); - tempMerc.CostFormula = atoi(DataRow[3]); - tempMerc.ClientVersion = atoi(DataRow[4]); - tempMerc.NPCID = atoi(DataRow[5]); - - mercDataList.push_back(tempMerc); - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - Query = 0; - - if(!errorMessage.empty()) { + if (!results.Success()) + { LogFile->write(EQEMuLog::Error, "Error in NPC::LoadMercTypes()"); + return; } + + for (auto row = results.begin(); row != results.end(); ++row) + { + MercData tempMerc; + + tempMerc.MercTemplateID = atoi(row[0]); + tempMerc.MercType = atoi(row[1]); + tempMerc.MercSubType = atoi(row[2]); + tempMerc.CostFormula = atoi(row[3]); + tempMerc.ClientVersion = atoi(row[4]); + tempMerc.NPCID = atoi(row[5]); + + mercDataList.push_back(tempMerc); + } + } -int NPC::GetNumMercTypes(uint32 clientVersion) -{ +int NPC::GetNumMercTypes(uint32 clientVersion) { + int count = 0; std::list mercTypeList = GetMercTypesList(); @@ -5931,8 +6187,8 @@ int NPC::GetNumMercTypes(uint32 clientVersion) return count; } -int NPC::GetNumMercs(uint32 clientVersion) -{ +int NPC::GetNumMercs(uint32 clientVersion) { + int count = 0; std::list mercDataList = GetMercsList(); @@ -5945,11 +6201,15 @@ int NPC::GetNumMercs(uint32 clientVersion) } std::list NPC::GetMercTypesList(uint32 clientVersion) { + std::list result; - if(GetNumMercTypes() > 0) { - for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) { - if(mercTypeListItr->ClientVersion <= clientVersion) { + if(GetNumMercTypes() > 0) + { + for(std::list::iterator mercTypeListItr = mercTypeList.begin(); mercTypeListItr != mercTypeList.end(); ++mercTypeListItr) + { + if(mercTypeListItr->ClientVersion <= clientVersion) + { MercType mercType; mercType.Type = mercTypeListItr->Type; mercType.ClientVersion = mercTypeListItr->ClientVersion; @@ -5962,14 +6222,19 @@ std::list NPC::GetMercTypesList(uint32 clientVersion) { } std::list NPC::GetMercsList(uint32 clientVersion) { + std::list result; - if(GetNumMercs() > 0) { - for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) { - if(mercListItr->ClientVersion <= clientVersion) { + if(GetNumMercs() > 0) + { + for(std::list::iterator mercListItr = mercDataList.begin(); mercListItr != mercDataList.end(); ++mercListItr) + { + if(mercListItr->ClientVersion <= clientVersion) + { MercTemplate *merc_template = zone->GetMercTemplate(mercListItr->MercTemplateID); - if(merc_template) { + if(merc_template) + { MercData mercData; mercData.MercTemplateID = mercListItr->MercTemplateID; mercData.MercType = merc_template->MercType; @@ -5986,54 +6251,70 @@ std::list NPC::GetMercsList(uint32 clientVersion) { return result; } -uint32 Merc::CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type) { +uint32 Merc::CalcPurchaseCost(uint32 templateID , uint8 level, uint8 currency_type) { + uint32 cost = 0; MercTemplate *mercData = zone->GetMercTemplate(templateID); - if(mercData) { - if(currency_type == 0) { //calculate cost in coin - cost in gold + if(mercData) + { + //calculate cost in coin - cost in gold + if(currency_type == 0) + { int levels_above_cutoff; - switch (mercData->CostFormula) { + switch (mercData->CostFormula) + { case 0: levels_above_cutoff = level > 10 ? (level - 10) : 0; cost = levels_above_cutoff * 300; cost += level >= 10 ? 100 : 0; + cost /= 100; break; default: break; } } - else if(currency_type == 19) { - cost = 0; - } - } - - return cost/100; -} - -uint32 Merc::CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type) { - uint32 cost = 0; - - MercTemplate *mercData = zone->GetMercTemplate(templateID); - - if(mercData) { - if(currency_type == 0) { //calculate cost in coin - cost in gold - int levels_above_cutoff; - switch (mercData->CostFormula) { - case 0: - levels_above_cutoff = level > 10 ? (level - 10) : 0; - cost = levels_above_cutoff * 300; - cost += level >= 10 ? 100 : 0; - break; - default: - break; - } - } - else if(currency_type == 19) { // cost in Bayle Marks + else if(currency_type == 19) + { + // cost in Bayle Marks cost = 1; } } - return cost/100; + return cost; } + +uint32 Merc::CalcUpkeepCost(uint32 templateID , uint8 level, uint8 currency_type) { + + uint32 cost = 0; + + MercTemplate *mercData = zone->GetMercTemplate(templateID); + + if(mercData) + { + //calculate cost in coin - cost in gold + if(currency_type == 0) + { + int levels_above_cutoff; + switch (mercData->CostFormula) + { + case 0: + levels_above_cutoff = level > 10 ? (level - 10) : 0; + cost = levels_above_cutoff * 300; + cost += level >= 10 ? 100 : 0; + cost /= 100; + break; + default: + break; + } + } + else if(currency_type == 19) + { + // cost in Bayle Marks + cost = 1; + } + } + + return cost; +} \ No newline at end of file diff --git a/zone/merc.h b/zone/merc.h index dabf68789..fdcdbec49 100644 --- a/zone/merc.h +++ b/zone/merc.h @@ -4,12 +4,18 @@ #include "zonedb.h" #include "npc.h" -#define MAXMERCS 1 #define MERC_DEBUG 0 -#define TANK 1 -#define HEALER 2 -#define MELEEDPS 9 -#define CASTERDPS 12 +#define MAXMERCS 1 +#define TANK 1 +#define HEALER 2 +#define MELEEDPS 9 +#define CASTERDPS 12 + +#define NO_MERC_ID 0 +#define MERC_STATE_NORMAL 5 +#define MERC_STATE_SUSPENDED 1 +#define NOT_SUSPENDED_TIME 0 + const int MercAISpellRange = 100; // TODO: Write a method that calcs what the merc's spell range is based on spell, equipment, AA, whatever and replace this enum MercStanceType { @@ -25,19 +31,19 @@ enum MercStanceType { }; struct MercSpell { - uint16 spellid; // <= 0 = no spell - uint32 type; // 0 = never, must be one (and only one) of the defined values - int16 stance; // 0 = all, + = only this stance, - = all except this stance - int16 slot; - uint16 proc_chance; - uint32 time_cancast; // when we can cast this spell next + uint16 spellid; // <= 0 = no spell + uint32 type; // 0 = never, must be one (and only one) of the defined values + int16 stance; // 0 = all, + = only this stance, - = all except this stance + int16 slot; + uint16 proc_chance; + uint32 time_cancast; // when we can cast this spell next }; struct MercTimer { - uint16 timerid; // EndurTimerIndex - uint8 timertype; // 1 = spell, 2 = disc - uint16 spellid; // <= 0 = no spell - uint32 time_cancast; // when we can cast this spell next + uint16 timerid; // EndurTimerIndex + uint8 timertype; // 1 = spell, 2 = disc + uint16 spellid; // <= 0 = no spell + uint32 time_cancast; // when we can cast this spell next }; class Merc : public NPC { @@ -48,8 +54,8 @@ public: //abstract virtual function implementations requird by base abstract class virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill); virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false); - virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, - bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr); + virtual bool Attack(Mob* other, int Hand = MainPrimary, bool FromRiposte = false, bool IsStrikethrough = false, + bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr); virtual bool HasRaid() { return false; } virtual bool HasGroup() { return (GetGroup() ? true : false); } virtual Raid* GetRaid() { return 0; } @@ -126,21 +132,21 @@ public: static Merc* LoadMerc(Client *c, MercTemplate* merc_template, uint32 merchant_id, bool updateFromDB = false); void UpdateMercInfo(Client *c); void UpdateMercStats(Client *c); - void UpdateMercAppearance(Client *c); + void UpdateMercAppearance(); void AddItem(uint8 slot, uint32 item_id); static const char *GetRandomName(); bool Spawn(Client *owner); - bool Dismiss(); bool Suspend(); bool Unsuspend(bool setMaxStats); + bool MercJoinClientGroup(); void Zone(); virtual void Depop(); virtual bool Save(); bool GetDepop() { return p_depop; } bool IsDead() { return GetHP() < 0;}; - bool IsMedding() {return _medding; }; - bool IsSuspended() {return _suspended; }; + bool IsMedding() { return _medding; }; + bool IsSuspended() { return _suspended; }; static uint32 CalcPurchaseCost( uint32 templateID , uint8 level, uint8 currency_type = 0); static uint32 CalcUpkeepCost( uint32 templateID , uint8 level, uint8 currency_type = 0); @@ -164,75 +170,75 @@ public: inline const uint8 GetClientVersion() const { return _OwnerClientVersion; } virtual void SetTarget(Mob* mob); - bool HasSkill(SkillUseTypes skill_id) const; - bool CanHaveSkill(SkillUseTypes skill_id) const; - uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; - inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } + bool HasSkill(SkillUseTypes skill_id) const; + bool CanHaveSkill(SkillUseTypes skill_id) const; + uint16 MaxSkill(SkillUseTypes skillid, uint16 class_, uint16 level) const; + inline uint16 MaxSkill(SkillUseTypes skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); } virtual void DoClassAttacks(Mob *target); - void CheckHateList(); - bool CheckTaunt(); - bool CheckAETaunt(); - bool CheckConfidence(); - bool TryHide(); + void CheckHateList(); + bool CheckTaunt(); + bool CheckAETaunt(); + bool CheckConfidence(); + bool TryHide(); // stat functions virtual void CalcBonuses(); - int32 GetEndurance() const {return cur_end;} //This gets our current endurance - inline virtual int16 GetAC() const { return AC; } - inline virtual int16 GetATK() const { return ATK; } - inline virtual int16 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } - int GetRawACNoShield(int &shield_ac) const; + int32 GetEndurance() const {return cur_end;} //This gets our current endurance + inline virtual int32 GetAC() const { return AC; } + inline virtual int32 GetATK() const { return ATK; } + inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } + int32 GetRawACNoShield(int &shield_ac) const; - inline virtual int16 GetSTR() const { return STR; } - inline virtual int16 GetSTA() const { return STA; } - inline virtual int16 GetDEX() const { return DEX; } - inline virtual int16 GetAGI() const { return AGI; } - inline virtual int16 GetINT() const { return INT; } - inline virtual int16 GetWIS() const { return WIS; } - inline virtual int16 GetCHA() const { return CHA; } - inline virtual int16 GetMR() const { return MR; } - inline virtual int16 GetFR() const { return FR; } - inline virtual int16 GetDR() const { return DR; } - inline virtual int16 GetPR() const { return PR; } - inline virtual int16 GetCR() const { return CR; } - inline virtual int16 GetCorrup() const { return Corrup; } + inline virtual int32 GetSTR() const { return STR; } + inline virtual int32 GetSTA() const { return STA; } + inline virtual int32 GetDEX() const { return DEX; } + inline virtual int32 GetAGI() const { return AGI; } + inline virtual int32 GetINT() const { return INT; } + inline virtual int32 GetWIS() const { return WIS; } + inline virtual int32 GetCHA() const { return CHA; } + inline virtual int32 GetMR() const { return MR; } + inline virtual int32 GetFR() const { return FR; } + inline virtual int32 GetDR() const { return DR; } + inline virtual int32 GetPR() const { return PR; } + inline virtual int32 GetCR() const { return CR; } + inline virtual int32 GetCorrup() const { return Corrup; } - inline virtual int16 GetHeroicSTR() const { return itembonuses.HeroicSTR; } - inline virtual int16 GetHeroicSTA() const { return itembonuses.HeroicSTA; } - inline virtual int16 GetHeroicDEX() const { return itembonuses.HeroicDEX; } - inline virtual int16 GetHeroicAGI() const { return itembonuses.HeroicAGI; } - inline virtual int16 GetHeroicINT() const { return itembonuses.HeroicINT; } - inline virtual int16 GetHeroicWIS() const { return itembonuses.HeroicWIS; } - inline virtual int16 GetHeroicCHA() const { return itembonuses.HeroicCHA; } - inline virtual int16 GetHeroicMR() const { return itembonuses.HeroicMR; } - inline virtual int16 GetHeroicFR() const { return itembonuses.HeroicFR; } - inline virtual int16 GetHeroicDR() const { return itembonuses.HeroicDR; } - inline virtual int16 GetHeroicPR() const { return itembonuses.HeroicPR; } - inline virtual int16 GetHeroicCR() const { return itembonuses.HeroicCR; } - inline virtual int16 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } + inline virtual int32 GetHeroicSTR() const { return itembonuses.HeroicSTR; } + inline virtual int32 GetHeroicSTA() const { return itembonuses.HeroicSTA; } + inline virtual int32 GetHeroicDEX() const { return itembonuses.HeroicDEX; } + inline virtual int32 GetHeroicAGI() const { return itembonuses.HeroicAGI; } + inline virtual int32 GetHeroicINT() const { return itembonuses.HeroicINT; } + inline virtual int32 GetHeroicWIS() const { return itembonuses.HeroicWIS; } + inline virtual int32 GetHeroicCHA() const { return itembonuses.HeroicCHA; } + inline virtual int32 GetHeroicMR() const { return itembonuses.HeroicMR; } + inline virtual int32 GetHeroicFR() const { return itembonuses.HeroicFR; } + inline virtual int32 GetHeroicDR() const { return itembonuses.HeroicDR; } + inline virtual int32 GetHeroicPR() const { return itembonuses.HeroicPR; } + inline virtual int32 GetHeroicCR() const { return itembonuses.HeroicCR; } + inline virtual int32 GetHeroicCorrup() const { return itembonuses.HeroicCorrup; } // Mod2 - inline virtual int16 GetShielding() const { return itembonuses.MeleeMitigation; } - inline virtual int16 GetSpellShield() const { return itembonuses.SpellShield; } - inline virtual int16 GetDoTShield() const { return itembonuses.DoTShielding; } - inline virtual int16 GetStunResist() const { return itembonuses.StunResist; } - inline virtual int16 GetStrikeThrough() const { return itembonuses.StrikeThrough; } - inline virtual int16 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } - inline virtual int16 GetAccuracy() const { return itembonuses.HitChance; } - inline virtual int16 GetCombatEffects() const { return itembonuses.ProcChance; } - inline virtual int16 GetDS() const { return itembonuses.DamageShield; } + inline virtual int32 GetShielding() const { return itembonuses.MeleeMitigation; } + inline virtual int32 GetSpellShield() const { return itembonuses.SpellShield; } + inline virtual int32 GetDoTShield() const { return itembonuses.DoTShielding; } + inline virtual int32 GetStunResist() const { return itembonuses.StunResist; } + inline virtual int32 GetStrikeThrough() const { return itembonuses.StrikeThrough; } + inline virtual int32 GetAvoidance() const { return itembonuses.AvoidMeleeChance; } + inline virtual int32 GetAccuracy() const { return itembonuses.HitChance; } + inline virtual int32 GetCombatEffects() const { return itembonuses.ProcChance; } + inline virtual int32 GetDS() const { return itembonuses.DamageShield; } // Mod3 - inline virtual int16 GetHealAmt() const { return itembonuses.HealAmt; } - inline virtual int16 GetSpellDmg() const { return itembonuses.SpellDmg; } - inline virtual int16 GetClair() const { return itembonuses.Clairvoyance; } - inline virtual int16 GetDSMit() const { return itembonuses.DSMitigation; } + inline virtual int32 GetHealAmt() const { return itembonuses.HealAmt; } + inline virtual int32 GetSpellDmg() const { return itembonuses.SpellDmg; } + inline virtual int32 GetClair() const { return itembonuses.Clairvoyance; } + inline virtual int32 GetDSMit() const { return itembonuses.DSMitigation; } - inline virtual int16 GetSingMod() const { return itembonuses.singingMod; } - inline virtual int16 GetBrassMod() const { return itembonuses.brassMod; } - inline virtual int16 GetPercMod() const { return itembonuses.percussionMod; } - inline virtual int16 GetStringMod() const { return itembonuses.stringedMod; } - inline virtual int16 GetWindMod() const { return itembonuses.windMod; } + inline virtual int32 GetSingMod() const { return itembonuses.singingMod; } + inline virtual int32 GetBrassMod() const { return itembonuses.brassMod; } + inline virtual int32 GetPercMod() const { return itembonuses.percussionMod; } + inline virtual int32 GetStringMod() const { return itembonuses.stringedMod; } + inline virtual int32 GetWindMod() const { return itembonuses.windMod; } - inline virtual int16 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } + inline virtual int32 GetDelayDeath() const { return aabonuses.DelayDeath + spellbonuses.DelayDeath + itembonuses.DelayDeath + 11; } // "SET" Class Methods void SetMercData (uint32 templateID ); @@ -267,67 +273,66 @@ protected: void AddItemBonuses(const Item_Struct *item, StatBonuses* newbon); int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat); - int16 GetFocusEffect(focusType type, uint16 spell_id); + int16 GetFocusEffect(focusType type, uint16 spell_id); std::vector merc_spells; std::map timers; - uint16 skills[HIGHEST_SKILL+1]; - uint32 equipment[EmuConstants::EQUIPMENT_SIZE]; //this is an array of item IDs - uint16 d_meele_texture1; //this is an item Material value - uint16 d_meele_texture2; //this is an item Material value (offhand) - uint8 prim_melee_type; //Sets the Primary Weapon attack message and animation - uint8 sec_melee_type; //Sets the Secondary Weapon attack message and animation + uint16 skills[HIGHEST_SKILL+1]; + uint32 equipment[EmuConstants::EQUIPMENT_SIZE]; //this is an array of item IDs + uint16 d_meele_texture1; //this is an item Material value + uint16 d_meele_texture2; //this is an item Material value (offhand) + uint8 prim_melee_type; //Sets the Primary Weapon attack message and animation + uint8 sec_melee_type; //Sets the Secondary Weapon attack message and animation private: - int16 CalcAC(); - int16 GetACMit(); - int16 GetACAvoid(); - int16 acmod(); - int16 CalcATK(); - //int CalcHaste(); + int32 CalcAC(); + int32 GetACMit(); + int32 GetACAvoid(); + int32 acmod(); + int32 CalcATK(); + //int CalcHaste(); - int16 CalcSTR(); - int16 CalcSTA(); - int16 CalcDEX(); - int16 CalcAGI(); - int16 CalcINT(); - int16 CalcWIS(); - int16 CalcCHA(); + int32 CalcSTR(); + int32 CalcSTA(); + int32 CalcDEX(); + int32 CalcAGI(); + int32 CalcINT(); + int32 CalcWIS(); + int32 CalcCHA(); - int16 CalcMR(); - int16 CalcFR(); - int16 CalcDR(); - int16 CalcPR(); - int16 CalcCR(); - int16 CalcCorrup(); - int32 CalcMaxHP(); - int32 CalcBaseHP(); - int32 GetClassHPFactor(); - int32 CalcHPRegen(); - int32 CalcHPRegenCap(); - int32 CalcMaxMana(); - int32 CalcBaseMana(); - int32 CalcManaRegen(); - int32 CalcBaseManaRegen(); - int32 CalcManaRegenCap(); - void CalcMaxEndurance(); //This calculates the maximum endurance we can have - int32 CalcBaseEndurance(); //Calculates Base End - int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call - int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() - int32 CalcEnduranceRegenCap(); - void SetEndurance(int32 newEnd); //This sets the current endurance to the new value - void DoEnduranceUpkeep(); //does the endurance upkeep - void CalcRestState(); + int32 CalcMR(); + int32 CalcFR(); + int32 CalcDR(); + int32 CalcPR(); + int32 CalcCR(); + int32 CalcCorrup(); + int32 CalcMaxHP(); + int32 CalcBaseHP(); + int32 GetClassHPFactor(); + int32 CalcHPRegen(); + int32 CalcHPRegenCap(); + int32 CalcMaxMana(); + int32 CalcBaseMana(); + int32 CalcManaRegen(); + int32 CalcBaseManaRegen(); + int32 CalcManaRegenCap(); + void CalcMaxEndurance(); //This calculates the maximum endurance we can have + int32 CalcBaseEndurance(); //Calculates Base End + int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call + int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen() + int32 CalcEnduranceRegenCap(); + void SetEndurance(int32 newEnd); //This sets the current endurance to the new value + void DoEnduranceUpkeep(); //does the endurance upkeep + void CalcRestState(); - int GroupLeadershipAAHealthEnhancement(); - int GroupLeadershipAAManaEnhancement(); - int GroupLeadershipAAHealthRegeneration(); - int GroupLeadershipAAOffenseEnhancement(); + int GroupLeadershipAAHealthEnhancement(); + int GroupLeadershipAAManaEnhancement(); + int GroupLeadershipAAHealthRegeneration(); + int GroupLeadershipAAOffenseEnhancement(); - void GetMercSize(); - void GenerateAppearance(); + float GetDefaultSize(); bool LoadMercSpells(); bool CheckStance(int16 stance); @@ -335,23 +340,23 @@ private: // Private "base stats" Members int32 base_mana; - int _baseAC; - uint16 _baseSTR; - uint16 _baseSTA; - uint16 _baseDEX; - uint16 _baseAGI; - uint16 _baseINT; - uint16 _baseWIS; - uint16 _baseCHA; - uint16 _baseATK; - uint16 _baseRace; // Necessary to preserve the race otherwise mercs get their race updated in the db when they get an illusion. - uint8 _baseGender; // Merc gender. Necessary to preserve the original value otherwise it can be changed by illusions. - uint16 _baseMR; - uint16 _baseCR; - uint16 _baseDR; - uint16 _baseFR; - uint16 _basePR; - uint16 _baseCorrup; + int32 _baseAC; + uint32 _baseSTR; + uint32 _baseSTA; + uint32 _baseDEX; + uint32 _baseAGI; + uint32 _baseINT; + uint32 _baseWIS; + uint32 _baseCHA; + uint32 _baseATK; + uint32 _baseRace; // Necessary to preserve the race otherwise mercs get their race updated in the db when they get an illusion. + uint8 _baseGender; // Merc gender. Necessary to preserve the original value otherwise it can be changed by illusions. + uint32 _baseMR; + uint32 _baseCR; + uint32 _baseDR; + uint32 _baseFR; + uint32 _basePR; + uint32 _baseCorrup; uint32 RestRegenHP; uint32 RestRegenMana; uint32 RestRegenEndurance; @@ -368,19 +373,19 @@ private: uint32 _currentStance; Inventory m_inv; - int32 max_end; - int32 cur_end; - bool _medding; - bool _suspended; - bool p_depop; - bool _check_confidence; - bool _lost_confidence; - int _hatedCount; - uint32 owner_char_id; - Timer endupkeep_timer; - Timer rest_timer; - Timer confidence_timer; - Timer check_target_timer; + int32 max_end; + int32 cur_end; + bool _medding; + bool _suspended; + bool p_depop; + bool _check_confidence; + bool _lost_confidence; + int _hatedCount; + uint32 owner_char_id; + Timer endupkeep_timer; + Timer rest_timer; + Timer confidence_timer; + Timer check_target_timer; }; -#endif // MERC_H +#endif // MERC_H \ No newline at end of file diff --git a/zone/mob.cpp b/zone/mob.cpp index 028e52c5a..f651ef714 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -15,19 +15,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "../common/debug.h" -#include "masterentity.h" + #include "../common/spdat.h" -#include "../common/StringUtil.h" -#include "StringIDs.h" +#include "../common/string_util.h" +#include "quest_parser_collection.h" +#include "string_ids.h" #include "worldserver.h" -#include "QuestParserCollection.h" #include "remote_call.h" #include "remote_call_subscribe.h" - -#include -#include #include +#include +#include extern EntityList entity_list; @@ -176,8 +174,9 @@ Mob::Mob(const char* in_name, drakkin_heritage = in_drakkin_heritage; drakkin_tattoo = in_drakkin_tattoo; drakkin_details = in_drakkin_details; - attack_speed= 0; - slow_mitigation= 0; + attack_speed = 0; + attack_delay = 0; + slow_mitigation = 0; findable = false; trackable = true; has_shieldequiped = false; @@ -185,6 +184,7 @@ Mob::Mob(const char* in_name, has_MGB = false; has_ProjectIllusion = false; SpellPowerDistanceMod = 0; + last_los_check = false; if(in_aa_title>0) aa_title = in_aa_title; @@ -264,7 +264,7 @@ Mob::Mob(const char* in_name, logging_enabled = false; isgrouped = false; israidgrouped = false; - islooting = false; + entity_id_being_looted = 0; _appearance = eaStanding; pRunAnimSpeed = 0; @@ -279,13 +279,24 @@ Mob::Mob(const char* in_name, casting_spell_inventory_slot = 0; target = 0; - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_spell_id[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_target_id[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_increment[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_x[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_y[i] = 0; } - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { projectile_z[i] = 0; } - projectile_timer.Disable(); + ActiveProjectileATK = false; + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) + { + ProjectileAtk[i].increment = 0; + ProjectileAtk[i].hit_increment = 0; + ProjectileAtk[i].target_id = 0; + ProjectileAtk[i].wpn_dmg = 0; + ProjectileAtk[i].origin_x = 0.0f; + ProjectileAtk[i].origin_y = 0.0f; + ProjectileAtk[i].origin_z = 0.0f; + ProjectileAtk[i].tlast_x = 0.0f; + ProjectileAtk[i].tlast_y = 0.0f; + ProjectileAtk[i].ranged_id = 0; + ProjectileAtk[i].ammo_id = 0; + ProjectileAtk[i].ammo_slot = 0; + ProjectileAtk[i].skill = 0; + ProjectileAtk[i].speed_mod = 0.0f; + } memset(&itembonuses, 0, sizeof(StatBonuses)); memset(&spellbonuses, 0, sizeof(StatBonuses)); @@ -300,6 +311,8 @@ Mob::Mob(const char* in_name, held = false; nocast = false; focused = false; + _IsTempPet = false; + pet_owner_client = false; attacked_count = 0; mezzed = false; @@ -313,7 +326,7 @@ Mob::Mob(const char* in_name, shielder[m].shielder_id = 0; shielder[m].shielder_bonus = 0; } - + destructibleobject = false; wandertype=0; pausetype=0; @@ -343,6 +356,7 @@ Mob::Mob(const char* in_name, viral_spells[i] = 0; } pStandingPetOrder = SPO_Follow; + pseudo_rooted = false; see_invis = in_see_invis; see_invis_undead = in_see_invis_undead != 0; @@ -359,7 +373,8 @@ Mob::Mob(const char* in_name, nexthpevent = -1; nextinchpevent = -1; - TempPets(false); + hasTempPet = false; + count_TempPet = 0; m_is_running = false; @@ -368,6 +383,10 @@ Mob::Mob(const char* in_name, nimbus_effect3 = 0; m_targetable = true; + targetring_x = 0.0f; + targetring_y = 0.0f; + targetring_z = 0.0f; + flymode = FlyMode3; // Pathing PathingLOSState = UnknownLOS; @@ -386,6 +405,7 @@ Mob::Mob(const char* in_name, for (int i = 0; i < HIGHEST_RESIST+2; i++) { Vulnerability_Mod[i] = 0; } emoteid = 0; + endur_upkeep = false; } Mob::~Mob() @@ -415,7 +435,7 @@ Mob::~Mob() delete trade; } - if(HadTempPets()){ + if(HasTempPetsActive()){ entity_list.DestroyTempPets(this); } entity_list.UnMarkNPC(GetID()); @@ -719,7 +739,8 @@ void Mob::CreateSpawnPacket(EQApplicationPacket* app, Mob* ForWho) { NewSpawn_Struct* ns = (NewSpawn_Struct*)app->pBuffer; FillSpawnStruct(ns, ForWho); - if(strlen(ns->spawn.lastName) == 0) { + if(strlen(ns->spawn.lastName) == 0) + { switch(ns->spawn.class_) { case TRIBUTE_MASTER: @@ -797,70 +818,78 @@ void Mob::CreateSpawnPacket(EQApplicationPacket* app, NewSpawn_Struct* ns) { // Custom packet data NewSpawn_Struct* ns2 = (NewSpawn_Struct*)app->pBuffer; strcpy(ns2->spawn.name, ns->spawn.name); - switch(ns->spawn.class_) - { - case TRIBUTE_MASTER: - strcpy(ns2->spawn.lastName, "Tribute Master"); - break; - case ADVENTURERECRUITER: - strcpy(ns2->spawn.lastName, "Adventure Recruiter"); - break; - case BANKER: - strcpy(ns2->spawn.lastName, "Banker"); - break; - case ADVENTUREMERCHANT: - strcpy(ns->spawn.lastName,"Adventure Merchant"); - break; - case WARRIORGM: - strcpy(ns2->spawn.lastName, "GM Warrior"); - break; - case PALADINGM: - strcpy(ns2->spawn.lastName, "GM Paladin"); - break; - case RANGERGM: - strcpy(ns2->spawn.lastName, "GM Ranger"); - break; - case SHADOWKNIGHTGM: - strcpy(ns2->spawn.lastName, "GM Shadowknight"); - break; - case DRUIDGM: - strcpy(ns2->spawn.lastName, "GM Druid"); - break; - case BARDGM: - strcpy(ns2->spawn.lastName, "GM Bard"); - break; - case ROGUEGM: - strcpy(ns2->spawn.lastName, "GM Rogue"); - break; - case SHAMANGM: - strcpy(ns2->spawn.lastName, "GM Shaman"); - break; - case NECROMANCERGM: - strcpy(ns2->spawn.lastName, "GM Necromancer"); - break; - case WIZARDGM: - strcpy(ns2->spawn.lastName, "GM Wizard"); - break; - case MAGICIANGM: - strcpy(ns2->spawn.lastName, "GM Magician"); - break; - case ENCHANTERGM: - strcpy(ns2->spawn.lastName, "GM Enchanter"); - break; - case BEASTLORDGM: - strcpy(ns2->spawn.lastName, "GM Beastlord"); - break; - case BERSERKERGM: - strcpy(ns2->spawn.lastName, "GM Berserker"); - break; - case MERCERNARY_MASTER: - strcpy(ns->spawn.lastName, "Mercenary Recruiter"); - break; - default: - strcpy(ns2->spawn.lastName, ns->spawn.lastName); - break; - } + // Set default Last Names for certain Classes if not defined + if (strlen(ns->spawn.lastName) == 0) + { + switch (ns->spawn.class_) + { + case TRIBUTE_MASTER: + strcpy(ns2->spawn.lastName, "Tribute Master"); + break; + case ADVENTURERECRUITER: + strcpy(ns2->spawn.lastName, "Adventure Recruiter"); + break; + case BANKER: + strcpy(ns2->spawn.lastName, "Banker"); + break; + case ADVENTUREMERCHANT: + strcpy(ns2->spawn.lastName, "Adventure Merchant"); + break; + case WARRIORGM: + strcpy(ns2->spawn.lastName, "GM Warrior"); + break; + case PALADINGM: + strcpy(ns2->spawn.lastName, "GM Paladin"); + break; + case RANGERGM: + strcpy(ns2->spawn.lastName, "GM Ranger"); + break; + case SHADOWKNIGHTGM: + strcpy(ns2->spawn.lastName, "GM Shadowknight"); + break; + case DRUIDGM: + strcpy(ns2->spawn.lastName, "GM Druid"); + break; + case BARDGM: + strcpy(ns2->spawn.lastName, "GM Bard"); + break; + case ROGUEGM: + strcpy(ns2->spawn.lastName, "GM Rogue"); + break; + case SHAMANGM: + strcpy(ns2->spawn.lastName, "GM Shaman"); + break; + case NECROMANCERGM: + strcpy(ns2->spawn.lastName, "GM Necromancer"); + break; + case WIZARDGM: + strcpy(ns2->spawn.lastName, "GM Wizard"); + break; + case MAGICIANGM: + strcpy(ns2->spawn.lastName, "GM Magician"); + break; + case ENCHANTERGM: + strcpy(ns2->spawn.lastName, "GM Enchanter"); + break; + case BEASTLORDGM: + strcpy(ns2->spawn.lastName, "GM Beastlord"); + break; + case BERSERKERGM: + strcpy(ns2->spawn.lastName, "GM Berserker"); + break; + case MERCERNARY_MASTER: + strcpy(ns2->spawn.lastName, "Mercenary liaison"); + break; + default: + strcpy(ns2->spawn.lastName, ns->spawn.lastName); + break; + } + } + else + { + strcpy(ns2->spawn.lastName, ns->spawn.lastName); + } memset(&app->pBuffer[sizeof(Spawn_Struct)-7], 0xFF, 7); } @@ -895,7 +924,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players ns->spawn.NPC = IsClient() ? 0 : 1; ns->spawn.IsMercenary = (IsMerc() || no_target_hotkey) ? 1 : 0; - + ns->spawn.petOwnerId = ownerid; ns->spawn.haircolor = haircolor; @@ -921,7 +950,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) } ns->spawn.guildrank = 0xFF; - ns->spawn.size = size; + ns->spawn.size = size; ns->spawn.bodytype = bodytype; // The 'flymode' settings have the following effect: // 0 - Mobs in water sink like a stone to the bottom @@ -1258,7 +1287,7 @@ void Mob::ShowStats(Client* client) } else if (IsCorpse()) { if (IsPlayerCorpse()) { - client->Message(0, " CharID: %i PlayerCorpse: %i", CastToCorpse()->GetCharID(), CastToCorpse()->GetDBID()); + client->Message(0, " CharID: %i PlayerCorpse: %i", CastToCorpse()->GetCharID(), CastToCorpse()->GetCorpseDBID()); } else { client->Message(0, " NPCCorpse", GetID()); @@ -1379,147 +1408,150 @@ void Mob::SendIllusionPacket(uint16 in_race, uint8 in_gender, uint8 in_texture, uint16 BaseRace = GetBaseRace(); - if (in_race == 0) { - this->race = BaseRace; + if (in_race == 0) + { + race = BaseRace; if (in_gender == 0xFF) - this->gender = GetBaseGender(); - else - this->gender = in_gender; - } - else { - this->race = in_race; - if (in_gender == 0xFF) { - uint8 tmp = Mob::GetDefaultGender(this->race, gender); - if (tmp == 2) - gender = 2; - else if (gender == 2 && GetBaseGender() == 2) - gender = tmp; - else if (gender == 2) - gender = GetBaseGender(); - } + gender = GetBaseGender(); else gender = in_gender; } - if (in_texture == 0xFF) { - if (in_race <= 12 || in_race == 128 || in_race == 130 || in_race == 330 || in_race == 522) - this->texture = 0xFF; - else - this->texture = GetTexture(); - } else - this->texture = in_texture; + { + race = in_race; + if (in_gender == 0xFF) + gender = GetDefaultGender(race, gender); + else + gender = in_gender; + } - if (in_helmtexture == 0xFF) { - if (in_race <= 12 || in_race == 128 || in_race == 130 || in_race == 330 || in_race == 522) - this->helmtexture = 0xFF; - else if (in_texture != 0xFF) - this->helmtexture = in_texture; + if (in_texture == 0xFF) + { + if (IsPlayerRace(in_race)) + texture = 0xFF; else - this->helmtexture = GetHelmTexture(); + texture = GetTexture(); } else - this->helmtexture = in_helmtexture; + { + texture = in_texture; + } + + if (in_helmtexture == 0xFF) + { + if (IsPlayerRace(in_race)) + helmtexture = 0xFF; + else if (in_texture != 0xFF) + helmtexture = in_texture; + else + helmtexture = GetHelmTexture(); + } + else + { + helmtexture = in_helmtexture; + } if (in_haircolor == 0xFF) - this->haircolor = GetHairColor(); + haircolor = GetHairColor(); else - this->haircolor = in_haircolor; + haircolor = in_haircolor; if (in_beardcolor == 0xFF) - this->beardcolor = GetBeardColor(); + beardcolor = GetBeardColor(); else - this->beardcolor = in_beardcolor; + beardcolor = in_beardcolor; if (in_eyecolor1 == 0xFF) - this->eyecolor1 = GetEyeColor1(); + eyecolor1 = GetEyeColor1(); else - this->eyecolor1 = in_eyecolor1; + eyecolor1 = in_eyecolor1; if (in_eyecolor2 == 0xFF) - this->eyecolor2 = GetEyeColor2(); + eyecolor2 = GetEyeColor2(); else - this->eyecolor2 = in_eyecolor2; + eyecolor2 = in_eyecolor2; if (in_hairstyle == 0xFF) - this->hairstyle = GetHairStyle(); + hairstyle = GetHairStyle(); else - this->hairstyle = in_hairstyle; + hairstyle = in_hairstyle; if (in_luclinface == 0xFF) - this->luclinface = GetLuclinFace(); + luclinface = GetLuclinFace(); else - this->luclinface = in_luclinface; + luclinface = in_luclinface; if (in_beard == 0xFF) - this->beard = GetBeard(); + beard = GetBeard(); else - this->beard = in_beard; + beard = in_beard; - this->aa_title = 0xFF; + aa_title = in_aa_title; if (in_drakkin_heritage == 0xFFFFFFFF) - this->drakkin_heritage = GetDrakkinHeritage(); + drakkin_heritage = GetDrakkinHeritage(); else - this->drakkin_heritage = in_drakkin_heritage; + drakkin_heritage = in_drakkin_heritage; if (in_drakkin_tattoo == 0xFFFFFFFF) - this->drakkin_tattoo = GetDrakkinTattoo(); + drakkin_tattoo = GetDrakkinTattoo(); else - this->drakkin_tattoo = in_drakkin_tattoo; + drakkin_tattoo = in_drakkin_tattoo; if (in_drakkin_details == 0xFFFFFFFF) - this->drakkin_details = GetDrakkinDetails(); + drakkin_details = GetDrakkinDetails(); else - this->drakkin_details = in_drakkin_details; + drakkin_details = in_drakkin_details; - if (in_size == 0xFFFFFFFF) - this->size = GetSize(); + if (in_size <= 0.0f) + size = GetSize(); else - this->size = in_size; + size = in_size; - // Forces the feature information to be pulled from the Player Profile - if (this->IsClient() && in_race == 0) { - this->race = CastToClient()->GetBaseRace(); - this->gender = CastToClient()->GetBaseGender(); - this->texture = 0xFF; - this->helmtexture = 0xFF; - this->haircolor = CastToClient()->GetBaseHairColor(); - this->beardcolor = CastToClient()->GetBaseBeardColor(); - this->eyecolor1 = CastToClient()->GetBaseEyeColor(); - this->eyecolor2 = CastToClient()->GetBaseEyeColor(); - this->hairstyle = CastToClient()->GetBaseHairStyle(); - this->luclinface = CastToClient()->GetBaseFace(); - this->beard = CastToClient()->GetBaseBeard(); - this->aa_title = 0xFF; - this->drakkin_heritage = CastToClient()->GetBaseHeritage(); - this->drakkin_tattoo = CastToClient()->GetBaseTattoo(); - this->drakkin_details = CastToClient()->GetBaseDetails(); + // Reset features to Base from the Player Profile + if (IsClient() && in_race == 0) + { + race = CastToClient()->GetBaseRace(); + gender = CastToClient()->GetBaseGender(); + texture = 0xFF; + helmtexture = 0xFF; + haircolor = CastToClient()->GetBaseHairColor(); + beardcolor = CastToClient()->GetBaseBeardColor(); + eyecolor1 = CastToClient()->GetBaseEyeColor(); + eyecolor2 = CastToClient()->GetBaseEyeColor(); + hairstyle = CastToClient()->GetBaseHairStyle(); + luclinface = CastToClient()->GetBaseFace(); + beard = CastToClient()->GetBaseBeard(); + aa_title = 0xFF; + drakkin_heritage = CastToClient()->GetBaseHeritage(); + drakkin_tattoo = CastToClient()->GetBaseTattoo(); + drakkin_details = CastToClient()->GetBaseDetails(); switch(race){ case OGRE: - this->size = 9; + size = 9; break; case TROLL: - this->size = 8; + size = 8; break; case VAHSHIR: case BARBARIAN: - this->size = 7; + size = 7; break; case HALF_ELF: case WOOD_ELF: case DARK_ELF: case FROGLOK: - this->size = 5; + size = 5; break; case DWARF: - this->size = 4; + size = 4; break; case HALFLING: case GNOME: - this->size = 3; + size = 3; break; default: - this->size = 6; + size = 6; break; } } @@ -1527,39 +1559,250 @@ void Mob::SendIllusionPacket(uint16 in_race, uint8 in_gender, uint8 in_texture, EQApplicationPacket* outapp = new EQApplicationPacket(OP_Illusion, sizeof(Illusion_Struct)); memset(outapp->pBuffer, 0, sizeof(outapp->pBuffer)); Illusion_Struct* is = (Illusion_Struct*) outapp->pBuffer; - is->spawnid = this->GetID(); + is->spawnid = GetID(); strcpy(is->charname, GetCleanName()); - is->race = this->race; - is->gender = this->gender; - is->texture = this->texture; - is->helmtexture = this->helmtexture; - is->haircolor = this->haircolor; - is->beardcolor = this->beardcolor; - is->beard = this->beard; - is->eyecolor1 = this->eyecolor1; - is->eyecolor2 = this->eyecolor2; - is->hairstyle = this->hairstyle; - is->face = this->luclinface; - //is->aa_title = this->aa_title; - is->drakkin_heritage = this->drakkin_heritage; - is->drakkin_tattoo = this->drakkin_tattoo; - is->drakkin_details = this->drakkin_details; - is->size = this->size; + is->race = race; + is->gender = gender; + is->texture = texture; + is->helmtexture = helmtexture; + is->haircolor = haircolor; + is->beardcolor = beardcolor; + is->beard = beard; + is->eyecolor1 = eyecolor1; + is->eyecolor2 = eyecolor2; + is->hairstyle = hairstyle; + is->face = luclinface; + is->drakkin_heritage = drakkin_heritage; + is->drakkin_tattoo = drakkin_tattoo; + is->drakkin_details = drakkin_details; + is->size = size; entity_list.QueueClients(this, outapp); safe_delete(outapp); mlog(CLIENT__SPELLS, "Illusion: Race = %i, Gender = %i, Texture = %i, HelmTexture = %i, HairColor = %i, BeardColor = %i, EyeColor1 = %i, EyeColor2 = %i, HairStyle = %i, Face = %i, DrakkinHeritage = %i, DrakkinTattoo = %i, DrakkinDetails = %i, Size = %f", - this->race, this->gender, this->texture, this->helmtexture, this->haircolor, this->beardcolor, this->eyecolor1, this->eyecolor2, this->hairstyle, this->luclinface, this->drakkin_heritage, this->drakkin_tattoo, this->drakkin_details, this->size); + race, gender, texture, helmtexture, haircolor, beardcolor, eyecolor1, eyecolor2, hairstyle, luclinface, drakkin_heritage, drakkin_tattoo, drakkin_details, size); } +bool Mob::RandomizeFeatures(bool send_illusion, bool set_variables) +{ + if (IsPlayerRace(GetRace())) + { + uint8 Gender = GetGender(); + uint8 Texture = 0xFF; + uint8 HelmTexture = 0xFF; + uint8 HairColor = 0xFF; + uint8 BeardColor = 0xFF; + uint8 EyeColor1 = 0xFF; + uint8 EyeColor2 = 0xFF; + uint8 HairStyle = 0xFF; + uint8 LuclinFace = 0xFF; + uint8 Beard = 0xFF; + uint32 DrakkinHeritage = 0xFFFFFFFF; + uint32 DrakkinTattoo = 0xFFFFFFFF; + uint32 DrakkinDetails = 0xFFFFFFFF; + + // Set some common feature settings + EyeColor1 = MakeRandomInt(0, 9); + EyeColor2 = MakeRandomInt(0, 9); + LuclinFace = MakeRandomInt(0, 7); + + // Adjust all settings based on the min and max for each feature of each race and gender + switch (GetRace()) + { + case 1: // Human + HairColor = MakeRandomInt(0, 19); + if (Gender == 0) { + BeardColor = HairColor; + HairStyle = MakeRandomInt(0, 3); + Beard = MakeRandomInt(0, 5); + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 2: // Barbarian + HairColor = MakeRandomInt(0, 19); + LuclinFace = MakeRandomInt(0, 87); + if (Gender == 0) { + BeardColor = HairColor; + HairStyle = MakeRandomInt(0, 3); + Beard = MakeRandomInt(0, 5); + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 3: // Erudite + if (Gender == 0) { + BeardColor = MakeRandomInt(0, 19); + Beard = MakeRandomInt(0, 5); + LuclinFace = MakeRandomInt(0, 57); + } + if (Gender == 1) { + LuclinFace = MakeRandomInt(0, 87); + } + break; + case 4: // WoodElf + HairColor = MakeRandomInt(0, 19); + if (Gender == 0) { + HairStyle = MakeRandomInt(0, 3); + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 5: // HighElf + HairColor = MakeRandomInt(0, 14); + if (Gender == 0) { + HairStyle = MakeRandomInt(0, 3); + LuclinFace = MakeRandomInt(0, 37); + BeardColor = HairColor; + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 6: // DarkElf + HairColor = MakeRandomInt(13, 18); + if (Gender == 0) { + HairStyle = MakeRandomInt(0, 3); + LuclinFace = MakeRandomInt(0, 37); + BeardColor = HairColor; + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 7: // HalfElf + HairColor = MakeRandomInt(0, 19); + if (Gender == 0) { + HairStyle = MakeRandomInt(0, 3); + LuclinFace = MakeRandomInt(0, 37); + BeardColor = HairColor; + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 8: // Dwarf + HairColor = MakeRandomInt(0, 19); + BeardColor = HairColor; + if (Gender == 0) { + HairStyle = MakeRandomInt(0, 3); + Beard = MakeRandomInt(0, 5); + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + LuclinFace = MakeRandomInt(0, 17); + } + break; + case 9: // Troll + EyeColor1 = MakeRandomInt(0, 10); + EyeColor2 = MakeRandomInt(0, 10); + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 3); + HairColor = MakeRandomInt(0, 23); + } + break; + case 10: // Ogre + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 3); + HairColor = MakeRandomInt(0, 23); + } + break; + case 11: // Halfling + HairColor = MakeRandomInt(0, 19); + if (Gender == 0) { + BeardColor = HairColor; + HairStyle = MakeRandomInt(0, 3); + Beard = MakeRandomInt(0, 5); + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 12: // Gnome + HairColor = MakeRandomInt(0, 24); + if (Gender == 0) { + BeardColor = HairColor; + HairStyle = MakeRandomInt(0, 3); + Beard = MakeRandomInt(0, 5); + } + if (Gender == 1) { + HairStyle = MakeRandomInt(0, 2); + } + break; + case 128: // Iksar + case 130: // VahShir + break; + case 330: // Froglok + LuclinFace = MakeRandomInt(0, 9); + case 522: // Drakkin + HairColor = MakeRandomInt(0, 3); + BeardColor = HairColor; + EyeColor1 = MakeRandomInt(0, 11); + EyeColor2 = MakeRandomInt(0, 11); + LuclinFace = MakeRandomInt(0, 6); + DrakkinHeritage = MakeRandomInt(0, 6); + DrakkinTattoo = MakeRandomInt(0, 7); + DrakkinDetails = MakeRandomInt(0, 7); + if (Gender == 0) { + Beard = MakeRandomInt(0, 12); + HairStyle = MakeRandomInt(0, 8); + } + if (Gender == 1) { + Beard = MakeRandomInt(0, 3); + HairStyle = MakeRandomInt(0, 7); + } + break; + default: + break; + } + + if (set_variables) + { + haircolor = HairColor; + beardcolor = BeardColor; + eyecolor1 = EyeColor1; + eyecolor2 = EyeColor2; + hairstyle = HairStyle; + luclinface = LuclinFace; + beard = Beard; + drakkin_heritage = DrakkinHeritage; + drakkin_tattoo = DrakkinTattoo; + drakkin_details = DrakkinDetails; + } + + if (send_illusion) + { + SendIllusionPacket(GetRace(), Gender, Texture, HelmTexture, HairColor, BeardColor, + EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage, + DrakkinTattoo, DrakkinDetails); + } + + return true; + } + return false; +} + + +bool Mob::IsPlayerRace(uint16 in_race) { + + if ((in_race >= HUMAN && in_race <= GNOME) || in_race == IKSAR || in_race == VAHSHIR || in_race == FROGLOK || in_race == DRAKKIN) + { + return true; + } + + return false; +} + + uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) { //std::cout << "Gender in: " << (int)in_gender << std::endl; // undefined cout [CODEBUG] - if ((in_race > 0 && in_race <= GNOME ) - || in_race == IKSAR || in_race == VAHSHIR || in_race == FROGLOK || in_race == DRAKKIN - || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118) { + if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118) { if (in_gender >= 2) { - // Female default for PC Races - return 1; + // Male default for PC Races + return 0; } else return in_gender; @@ -1943,6 +2186,7 @@ void Mob::SetZone(uint32 zone_id, uint32 instance_id) { CastToClient()->GetPP().zone_id = zone_id; CastToClient()->GetPP().zoneInstance = instance_id; + CastToClient()->Save(); } Save(); } @@ -1951,159 +2195,6 @@ void Mob::Kill() { Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand); } -void Mob::SetAttackTimer() { - float PermaHaste; - if(GetHaste() > 0) - PermaHaste = 1 / (1 + (float)GetHaste()/100); - else if(GetHaste() < 0) - PermaHaste = 1 * (1 - (float)GetHaste()/100); - else - PermaHaste = 1.0f; - - //default value for attack timer in case they have - //an invalid weapon equipped: - attack_timer.SetAtTrigger(4000, true); - - Timer* TimerToUse = nullptr; - const Item_Struct* PrimaryWeapon = nullptr; - - for (int i=MainRange; i<=MainSecondary; i++) { - - //pick a timer - if (i == MainPrimary) - TimerToUse = &attack_timer; - else if (i == MainRange) - TimerToUse = &ranged_timer; - else if(i == MainSecondary) - TimerToUse = &attack_dw_timer; - else //invalid slot (hands will always hit this) - continue; - - const Item_Struct* ItemToUse = nullptr; - - //find our item - if (IsClient()) { - ItemInst* ci = CastToClient()->GetInv().GetItem(i); - if (ci) - ItemToUse = ci->GetItem(); - } else if(IsNPC()) - { - //The code before here was fundementally flawed because equipment[] - //isn't the same as PC inventory and also: - //NPCs don't use weapon speed to dictate how fast they hit anyway. - ItemToUse = nullptr; - } - - //special offhand stuff - if(i == MainSecondary) { - //if we have a 2H weapon in our main hand, no dual - if(PrimaryWeapon != nullptr) { - if( PrimaryWeapon->ItemClass == ItemClassCommon - && (PrimaryWeapon->ItemType == ItemType2HSlash - || PrimaryWeapon->ItemType == ItemType2HBlunt - || PrimaryWeapon->ItemType == ItemType2HPiercing)) { - attack_dw_timer.Disable(); - continue; - } - } - - //clients must have the skill to use it... - if(IsClient()) { - //if we cant dual wield, skip it - if (!CanThisClassDualWield()) { - attack_dw_timer.Disable(); - continue; - } - } else { - //NPCs get it for free at 13 - if(GetLevel() < 13) { - attack_dw_timer.Disable(); - continue; - } - } - } - - //see if we have a valid weapon - if(ItemToUse != nullptr) { - //check type and damage/delay - if(ItemToUse->ItemClass != ItemClassCommon - || ItemToUse->Damage == 0 - || ItemToUse->Delay == 0) { - //no weapon - ItemToUse = nullptr; - } - // Check to see if skill is valid - else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) { - //no weapon - ItemToUse = nullptr; - } - } - - int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands; - if (DelayMod < -99) - DelayMod = -99; - - //if we have no weapon.. - if (ItemToUse == nullptr) { - //above checks ensure ranged weapons do not fall into here - // Work out if we're a monk - if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) { - //we are a monk, use special delay - int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - // 1200 seemed too much, with delay 10 weapons available - if(speed < RuleI(Combat, MinHastedDelay)) //lower bound - speed = RuleI(Combat, MinHastedDelay); - TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic - } else { - //not a monk... using fist, regular delay - int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound - speed = RuleI(Combat, MinHastedDelay); - TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36 - } - } else { - //we have a weapon, use its delay - // Convert weapon delay to timer resolution (milliseconds) - //delay * 100 - int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste); - if(speed < RuleI(Combat, MinHastedDelay)) - speed = RuleI(Combat, MinHastedDelay); - - if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing)) - { - if(IsClient()) - { - float max_quiver = 0; - for(int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) - { - const ItemInst *pi = CastToClient()->GetInv().GetItem(r); - if(!pi) - continue; - if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) - { - float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) ); - if(temp_wr > max_quiver) - { - max_quiver = temp_wr; - } - } - } - if(max_quiver > 0) - { - float quiver_haste = 1 / (1 + max_quiver / 100); - speed *= quiver_haste; - } - } - } - TimerToUse->SetAtTrigger(speed, true); - } - - if(i == MainPrimary) - PrimaryWeapon = ItemToUse; - } - -} - bool Mob::CanThisClassDualWield(void) const { if(!IsClient()) { return(GetSkill(SkillDualWield) > 0); @@ -2406,7 +2497,7 @@ bool Mob::HateSummon() { { if(summon_level == 1) { entity_list.MessageClose(this, true, 500, MT_Say, "%s says,'You will not evade me, %s!' ", GetCleanName(), target->GetCleanName() ); - + if (target->IsClient()) { target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), x_pos, y_pos, z_pos, target->GetHeading(), 0, SummonPC); } @@ -2418,12 +2509,12 @@ bool Mob::HateSummon() { target->CastToBot()->SetPreSummonX(target->GetX()); target->CastToBot()->SetPreSummonY(target->GetY()); target->CastToBot()->SetPreSummonZ(target->GetZ()); - + } #endif //BOTS target->GMMove(x_pos, y_pos, z_pos, target->GetHeading()); } - + return true; } else if(summon_level == 2) { entity_list.MessageClose(this, true, 500, MT_Say, "%s says,'You will not evade me, %s!'", GetCleanName(), target->GetCleanName()); @@ -2593,7 +2684,7 @@ void Mob::WearChange(uint8 material_slot, uint16 texture, uint32 color) int32 Mob::GetEquipmentMaterial(uint8 material_slot) const { const Item_Struct *item; - + int ornamentationAugtype = RuleI(Character, OrnamentationAugmentType); item = database.GetItem(GetEquipment(material_slot)); if(item != 0) { @@ -2603,10 +2694,29 @@ int32 Mob::GetEquipmentMaterial(uint8 material_slot) const material_slot == MaterialSecondary ) { - if(strlen(item->IDFile) > 2) - return atoi(&item->IDFile[2]); - else //may as well try this, since were going to 0 anyways - return item->Material; + if (this->IsClient()){ + int currMatslot = MaterialPrimary == material_slot ? MainPrimary : MainSecondary; + const ItemInst* inst = CastToClient()->m_inv[currMatslot]; + if (inst && inst->GetOrnamentationAug(ornamentationAugtype)) { + item = inst->GetOrnamentationAug(ornamentationAugtype)->GetItem(); + return atoi(&item->IDFile[2]); + } + else if (inst->GetOrnamentationIcon() && inst->GetOrnamentationIDFile()) { + return inst->GetOrnamentationIDFile(); + } + else { + if (strlen(item->IDFile) > 2) + return atoi(&item->IDFile[2]); + else //may as well try this, since were going to 0 anyways + return item->Material; + } + } + else { + if (strlen(item->IDFile) > 2) + return atoi(&item->IDFile[2]); + else //may as well try this, since were going to 0 anyways + return item->Material; + } } else { @@ -2898,12 +3008,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; @@ -2923,21 +3050,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) { @@ -3054,7 +3173,7 @@ void Mob::TriggerDefensiveProcs(const ItemInst* weapon, Mob *on, uint16 hand, in case (-1): skillinuse = SkillBlock; break; - + case (-2): skillinuse = SkillParry; break; @@ -3189,17 +3308,16 @@ void Mob::TriggerOnCast(uint32 focus_spell, uint32 spell_id, bool aa_trigger) if(IsValidSpell(trigger_spell_id) && GetTarget()){ SpellFinished(trigger_spell_id, GetTarget(),10, 0, -1, spells[trigger_spell_id].ResistDiff); - CheckNumHitsRemaining(NUMHIT_MatchingSpells,0, focus_spell); + CheckNumHitsRemaining(NUMHIT_MatchingSpells,-1, focus_spell); } } } -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++) @@ -3218,8 +3336,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 { @@ -3233,37 +3353,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) @@ -3287,7 +3385,7 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP if (!spellbonuses.TriggerOnValueAmount) return; - + if (spellbonuses.TriggerOnValueAmount){ int buff_count = GetMaxTotalSlots(); @@ -3308,15 +3406,15 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP if (IsHP){ if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5) use_spell = true; - + else if (base2 = 1004 && GetHPRatio() < 80) use_spell = true; } else if (IsMana){ - if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40)) + if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40)) use_spell = true; - + else if (base2 = 38311 && GetManaRatio() < 10) use_spell = true; } @@ -3336,7 +3434,7 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP if (use_spell){ SpellFinished(spells[spell_id].base[i], this, 10, 0, -1, spells[spell_id].ResistDiff); - + if(!TryFadeEffect(e)) BuffFadeBySlot(e); } @@ -3396,7 +3494,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) if (!caster) return 0; - + int32 value = 0; //Apply innate vulnerabilities @@ -3409,7 +3507,7 @@ int32 Mob::GetVulnerability(Mob* caster, uint32 spell_id, uint32 ticsremaining) //Apply spell derived vulnerabilities if (spellbonuses.FocusEffects[focusSpellVulnerability]){ - + int32 tmp_focus = 0; int tmp_buffslot = -1; @@ -3470,15 +3568,15 @@ int16 Mob::GetSkillDmgTaken(const SkillUseTypes skill_used) } int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) { - + int16 heal_rate = 0; - heal_rate += itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; - heal_rate += GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, caster, spell_id); + heal_rate += itembonuses.HealRate + spellbonuses.HealRate + aabonuses.HealRate; + heal_rate += GetFocusIncoming(focusFcHealPctIncoming, SE_FcHealPctIncoming, caster, spell_id); if(heal_rate < -99) heal_rate = -99; - + return heal_rate; } @@ -3488,7 +3586,7 @@ bool Mob::TryFadeEffect(int slot) { for(int i = 0; i < EFFECT_COUNT; i++) { - if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || + if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways || spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect) { uint16 spell_id = spells[buffs[slot].spellid].base[i]; @@ -3546,8 +3644,8 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id) else SpellFinished(focus_trigger, target, 10, 0, -1, spells[focus_trigger].ResistDiff); } - - CheckNumHitsRemaining(NUMHIT_MatchingSpells, 0, focus_spell); + + CheckNumHitsRemaining(NUMHIT_MatchingSpells, -1, focus_spell); } } @@ -3567,7 +3665,7 @@ int32 Mob::GetItemStat(uint32 itemid, const char *identifier) int32 stat = 0; std::string id = identifier; - for(int i = 0; i < id.length(); ++i) + for(uint32 i = 0; i < id.length(); ++i) { id[i] = tolower(id[i]); } @@ -3942,36 +4040,26 @@ void Mob::TarGlobal(const char *varname, const char *value, const char *duration } void Mob::DelGlobal(const char *varname) { - // delglobal(varname) - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + int qgZoneid=zone->GetZoneID(); int qgCharid=0; int qgNpcid=0; if (this->IsNPC()) - { qgNpcid = this->GetNPCTypeID(); - } if (this->IsClient()) - { qgCharid = this->CastToClient()->CharacterID(); - } else - { qgCharid = -qgNpcid; // make char id negative npc id as a fudge - } - 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)) - { - //_log(QUESTS, "DelGlobal error deleting %s : %s", varname, errbuf); - } - 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); + + database.QueryDatabase(query); if(zone) { @@ -3994,33 +4082,22 @@ void Mob::DelGlobal(const char *varname) { // Inserts global variable into quest_globals table void Mob::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; - } //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)) - { - //_log(QUESTS, "SelGlobal error inserting %s : %s", varname, errbuf); - } - safe_delete_array(query); + 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, duration_ss.str().c_str()); + database.QueryDatabase(query); if(zone) { @@ -4046,14 +4123,12 @@ void Mob::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varna 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); strcpy((char*)qgu->value, varvalue); qgu->id = last_id; @@ -4376,8 +4451,8 @@ int16 Mob::GetSkillDmgAmt(uint16 skill) } void Mob::MeleeLifeTap(int32 damage) { - - int16 lifetap_amt = 0; + + int32 lifetap_amt = 0; lifetap_amt = spellbonuses.MeleeLifetap + itembonuses.MeleeLifetap + aabonuses.MeleeLifetap + spellbonuses.Vampirism + itembonuses.Vampirism + aabonuses.Vampirism; @@ -4385,7 +4460,7 @@ void Mob::MeleeLifeTap(int32 damage) { lifetap_amt = damage * lifetap_amt / 100; mlog(COMBAT__DAMAGE, "Melee lifetap healing for %d damage.", damage); - + if (lifetap_amt > 0) HealDamage(lifetap_amt); //Heal self for modified damage amount. else @@ -4397,58 +4472,15 @@ bool Mob::TryReflectSpell(uint32 spell_id) { if (!spells[spell_id].reflectable) return false; - + int chance = itembonuses.reflect_chance + spellbonuses.reflect_chance + aabonuses.reflect_chance; - + if(chance && MakeRandomInt(0, 99) < chance) return true; return false; } -void Mob::SpellProjectileEffect() -{ - bool time_disable = false; - - for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { - - if (projectile_increment[i] == 0){ - continue; - } - - Mob* target = entity_list.GetMobID(projectile_target_id[i]); - - float dist = 0; - - if (target) - dist = target->CalculateDistance(projectile_x[i], projectile_y[i], projectile_z[i]); - - int increment_end = 0; - increment_end = (dist / 10) - 1; //This pretty accurately determines end time for speed for 1.5 and timer of 250 ms - - if (increment_end <= projectile_increment[i]){ - - if (target && IsValidSpell(projectile_spell_id[i])) - SpellOnTarget(projectile_spell_id[i], target, false, true, spells[projectile_spell_id[i]].ResistDiff, true); - - projectile_spell_id[i] = 0; - projectile_target_id[i] = 0; - projectile_x[i] = 0, projectile_y[i] = 0, projectile_z[i] = 0; - projectile_increment[i] = 0; - time_disable = true; - } - - else { - projectile_increment[i]++; - time_disable = false; - } - } - - if (time_disable) - projectile_timer.Disable(); -} - - void Mob::DoGravityEffect() { Mob *caster = nullptr; @@ -4547,7 +4579,7 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID) // Only spread in zones without perm buffs if(!zone->BuffTimersSuspended()) { for(int i = 0; i < num_targs; i++) { - target = entity_list.GetTargetForVirus(this); + target = entity_list.GetTargetForVirus(this, spells[spell_id].viral_range); if(target) { // Only spreads to the uninfected if(!target->FindBuff(spell_id)) { @@ -4562,6 +4594,15 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID) void Mob::RemoveNimbusEffect(int effectid) { + if (effectid == nimbus_effect1) + nimbus_effect1 = 0; + + else if (effectid == nimbus_effect2) + nimbus_effect2 = 0; + + else if (effectid == nimbus_effect3) + nimbus_effect3 = 0; + EQApplicationPacket* outapp = new EQApplicationPacket(OP_RemoveNimbusEffect, sizeof(RemoveNimbusEffect_Struct)); RemoveNimbusEffect_Struct* rne = (RemoveNimbusEffect_Struct*)outapp->pBuffer; rne->spawnid = GetID(); @@ -4695,7 +4736,7 @@ void Mob::SlowMitigation(Mob* caster) else if ((GetSlowMitigation() >= 74) && (GetSlowMitigation() < 101)) caster->Message_StringID(MT_SpellFailure, SLOW_SLIGHTLY_SUCCESSFUL); - else if (GetSlowMitigation() > 100) + else if (GetSlowMitigation() > 100) caster->Message_StringID(MT_SpellFailure, SPELL_OPPOSITE_EFFECT); } } @@ -4772,10 +4813,10 @@ bool Mob::PassLimitToSkill(uint16 spell_id, uint16 skill) { } uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) { - + uint16 weapon_speed = 0; switch (hand) { - + case 13: weapon_speed = attack_timer.GetDuration(); break; @@ -4798,7 +4839,7 @@ int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) { if (!IsValidSpell(spell_id)) return false; - int spell_level = spells[spell_id].classes[(GetClass()%16) - 1]; + int spell_level = spells[spell_id].classes[(GetClass()%16) - 1]; int effect_value = 0; int lvlModifier = 100; @@ -4807,16 +4848,16 @@ int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) { if (IsValidSpell(buffs[slot].spellid)){ for (int i = 0; i < EFFECT_COUNT; i++){ if(spells[buffs[slot].spellid].effectid[i] == spelleffect) { - - int critchance = spells[buffs[slot].spellid].base[i]; + + int critchance = spells[buffs[slot].spellid].base[i]; int decay = spells[buffs[slot].spellid].base2[i]; - int lvldiff = spell_level - spells[buffs[slot].spellid].max[i]; - + int lvldiff = spell_level - spells[buffs[slot].spellid].max[i]; + if(lvldiff > 0 && decay > 0) { - lvlModifier -= decay*lvldiff; + lvlModifier -= decay*lvldiff; if (lvlModifier > 0){ - critchance = (critchance*lvlModifier)/100; + critchance = (critchance*lvlModifier)/100; effect_value += critchance; } } @@ -4827,7 +4868,7 @@ int8 Mob::GetDecayEffectValue(uint16 spell_id, uint16 spelleffect) { } } } - + return effect_value; } @@ -5018,7 +5059,7 @@ bool Mob::HasSpellEffect(int effectid) { int i; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid == SPELL_UNKNOWN) { continue; } @@ -5102,7 +5143,7 @@ void Mob::ClearSpecialAbilities() { } } -void Mob::ProcessSpecialAbilities(const std::string str) { +void Mob::ProcessSpecialAbilities(const std::string &str) { ClearSpecialAbilities(); std::vector sp = SplitString(str, '^'); @@ -5149,7 +5190,7 @@ bool Mob::IsFacingMob(Mob *other) return false; float angle = HeadingAngleToMob(other); // what the client uses appears to be 2x our internal heading - float heading = GetHeading() * 2.0; + float heading = GetHeading() * 2.0f; if (angle > 472.0 && heading < 40.0) angle = heading; @@ -5175,19 +5216,135 @@ float Mob::HeadingAngleToMob(Mob *other) if (y_diff < 0.0000009999999974752427) y_diff = 0.0000009999999974752427; - float angle = atan2(x_diff, y_diff) * 180.0 * 0.3183099014828645; // angle, nice "pi" + float angle = atan2(x_diff, y_diff) * 180.0f * 0.3183099014828645f; // angle, nice "pi" // return the right thing based on relative quadrant // I'm sure this could be improved for readability, but whatever if (this_y >= mob_y) { if (mob_x >= this_x) - return (90.0 - angle + 90.0) * 511.5 * 0.0027777778; + return (90.0f - angle + 90.0f) * 511.5f * 0.0027777778f; if (mob_x <= this_x) - return (angle + 180.0) * 511.5 * 0.0027777778; + return (angle + 180.0f) * 511.5f * 0.0027777778f; } if (this_y > mob_y || mob_x > this_x) - return angle * 511.5 * 0.0027777778; + return angle * 511.5f * 0.0027777778f; else - return (90.0 - angle + 270.0) * 511.5 * 0.0027777778; + return (90.0f - angle + 270.0f) * 511.5f * 0.0027777778f; +} + +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(uint32 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 = static_cast(spells[spell_id].range); } + else if (id == "aoerange") {stat = static_cast(spells[spell_id].aoerange);} + else if (id == "pushback") {stat = static_cast(spells[spell_id].pushback);} + else if (id == "pushup") {stat = static_cast(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 = static_cast(spells[spell_id].directional_start); } + else if (id == "directional_end") {stat = static_cast(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 = static_cast(spells[spell_id].min_dist); } + else if (id == "min_dist_mod") {stat = static_cast(spells[spell_id].min_dist_mod); } + else if (id == "max_dist") {stat = static_cast(spells[spell_id].max_dist); } + else if (id == "min_range") {stat = static_cast(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 fe0919f7e..18f61281f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -18,21 +18,28 @@ #ifndef MOB_H #define MOB_H -#include "../common/features.h" #include "common.h" #include "entity.h" #include "hate_list.h" #include "pathing.h" #include #include -#include char* strn0cpy(char* dest, const char* source, uint32 size); #define MAX_SPECIAL_ATTACK_PARAMS 8 class EGNode; -class MobFearState; +class Client; +class EQApplicationPacket; +class Group; +class ItemInst; +class NPC; +class Raid; +struct Item_Struct; +struct NewSpawn_Struct; +struct PlayerPositionUpdateServer_Struct; + class Mob : public Entity { public: enum CLIENT_CONN_STATUS { CLIENT_CONNECTING, CLIENT_CONNECTED, CLIENT_LINKDEAD, @@ -205,7 +212,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, @@ -230,12 +237,13 @@ public: uint16 CastingSpellID() const { return casting_spell_id; } bool DoCastingChecks(); bool TryDispel(uint8 caster_level, uint8 buff_level, int level_modifier); - void SpellProjectileEffect(); - bool TrySpellProjectile(Mob* spell_target, uint16 spell_id); + bool TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed = 1.5f); void ResourceTap(int32 damage, uint16 spell_id); void TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker); bool CheckSpellCategory(uint16 spell_id, int category_id, int effect_id); - + void CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ); + void BeamDirectional(uint16 spell_id, int16 resist_adjust); + void ConeDirectional(uint16 spell_id, int16 resist_adjust); //Buff void BuffProcess(); @@ -273,7 +281,7 @@ public: int16 GetBuffSlotFromType(uint16 type); uint16 GetSpellIDFromSlot(uint8 slot); int CountDispellableBuffs(); - void CheckNumHitsRemaining(uint8 type, uint32 buff_slot=0, uint16 spell_id=SPELL_UNKNOWN); + void CheckNumHitsRemaining(uint8 type, int32 buff_slot=-1, uint16 spell_id=SPELL_UNKNOWN); bool HasNumhits() const { return has_numhits; } inline void Numhits(bool val) { has_numhits = val; } bool HasMGB() const { return has_MGB; } @@ -287,6 +295,11 @@ public: inline virtual uint32 GetNimbusEffect2() const { return nimbus_effect2; } inline virtual uint32 GetNimbusEffect3() const { return nimbus_effect3; } void RemoveNimbusEffect(int effectid); + inline float GetTargetRingX() const { return targetring_x; } + inline float GetTargetRingY() const { return targetring_y; } + inline float GetTargetRingZ() const { return targetring_z; } + inline bool HasEndurUpkeep() const { return endur_upkeep; } + inline void SetEndurUpkeep(bool val) { endur_upkeep = val; } //Basic Stats/Inventory virtual void SetLevel(uint8 in_level, bool command = false) { level = in_level; } @@ -340,39 +353,39 @@ public: inline Mob* GetTarget() const { return target; } virtual void SetTarget(Mob* mob); virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); } - inline virtual int16 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; } - inline virtual int16 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; } - inline virtual int16 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } - inline virtual int16 GetSTR() const { return STR + itembonuses.STR + spellbonuses.STR; } - inline virtual int16 GetSTA() const { return STA + itembonuses.STA + spellbonuses.STA; } - inline virtual int16 GetDEX() const { return DEX + itembonuses.DEX + spellbonuses.DEX; } - inline virtual int16 GetAGI() const { return AGI + itembonuses.AGI + spellbonuses.AGI; } - inline virtual int16 GetINT() const { return INT + itembonuses.INT + spellbonuses.INT; } - inline virtual int16 GetWIS() const { return WIS + itembonuses.WIS + spellbonuses.WIS; } - inline virtual int16 GetCHA() const { return CHA + itembonuses.CHA + spellbonuses.CHA; } - inline virtual int16 GetMR() const { return MR + itembonuses.MR + spellbonuses.MR; } - inline virtual int16 GetFR() const { return FR + itembonuses.FR + spellbonuses.FR; } - inline virtual int16 GetDR() const { return DR + itembonuses.DR + spellbonuses.DR; } - inline virtual int16 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; } - inline virtual int16 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; } - inline virtual int16 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; } - inline virtual int16 GetPhR() const { return PhR; } + inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; } + inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; } + inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } + inline virtual int32 GetSTR() const { return STR + itembonuses.STR + spellbonuses.STR; } + inline virtual int32 GetSTA() const { return STA + itembonuses.STA + spellbonuses.STA; } + inline virtual int32 GetDEX() const { return DEX + itembonuses.DEX + spellbonuses.DEX; } + inline virtual int32 GetAGI() const { return AGI + itembonuses.AGI + spellbonuses.AGI; } + inline virtual int32 GetINT() const { return INT + itembonuses.INT + spellbonuses.INT; } + inline virtual int32 GetWIS() const { return WIS + itembonuses.WIS + spellbonuses.WIS; } + inline virtual int32 GetCHA() const { return CHA + itembonuses.CHA + spellbonuses.CHA; } + inline virtual int32 GetMR() const { return MR + itembonuses.MR + spellbonuses.MR; } + inline virtual int32 GetFR() const { return FR + itembonuses.FR + spellbonuses.FR; } + inline virtual int32 GetDR() const { return DR + itembonuses.DR + spellbonuses.DR; } + inline virtual int32 GetPR() const { return PR + itembonuses.PR + spellbonuses.PR; } + inline virtual int32 GetCR() const { return CR + itembonuses.CR + spellbonuses.CR; } + inline virtual int32 GetCorrup() const { return Corrup + itembonuses.Corrup + spellbonuses.Corrup; } + inline virtual int32 GetPhR() const { return PhR; } inline StatBonuses GetItemBonuses() const { return itembonuses; } inline StatBonuses GetSpellBonuses() const { return spellbonuses; } inline StatBonuses GetAABonuses() const { return aabonuses; } - inline virtual int16 GetMaxSTR() const { return GetSTR(); } - inline virtual int16 GetMaxSTA() const { return GetSTA(); } - inline virtual int16 GetMaxDEX() const { return GetDEX(); } - inline virtual int16 GetMaxAGI() const { return GetAGI(); } - inline virtual int16 GetMaxINT() const { return GetINT(); } - inline virtual int16 GetMaxWIS() const { return GetWIS(); } - inline virtual int16 GetMaxCHA() const { return GetCHA(); } - inline virtual int16 GetMaxMR() const { return 255; } - inline virtual int16 GetMaxPR() const { return 255; } - inline virtual int16 GetMaxDR() const { return 255; } - inline virtual int16 GetMaxCR() const { return 255; } - inline virtual int16 GetMaxFR() const { return 255; } - inline virtual int16 GetDelayDeath() const { return 0; } + inline virtual int32 GetMaxSTR() const { return GetSTR(); } + inline virtual int32 GetMaxSTA() const { return GetSTA(); } + inline virtual int32 GetMaxDEX() const { return GetDEX(); } + inline virtual int32 GetMaxAGI() const { return GetAGI(); } + inline virtual int32 GetMaxINT() const { return GetINT(); } + inline virtual int32 GetMaxWIS() const { return GetWIS(); } + inline virtual int32 GetMaxCHA() const { return GetCHA(); } + inline virtual int32 GetMaxMR() const { return 255; } + inline virtual int32 GetMaxPR() const { return 255; } + inline virtual int32 GetMaxDR() const { return 255; } + inline virtual int32 GetMaxCR() const { return 255; } + inline virtual int32 GetMaxFR() const { return 255; } + inline virtual int32 GetDelayDeath() const { return 0; } inline int32 GetHP() const { return cur_hp; } inline int32 GetMaxHP() const { return max_hp; } virtual int32 CalcMaxHP(); @@ -464,6 +477,8 @@ public: bool CheckLosFN(float posX, float posY, float posZ, float mobSize); inline void SetChanged() { pLastChange = Timer::GetCurrentTime(); } inline const uint32 LastChange() const { return pLastChange; } + inline void SetLastLosState(bool value) { last_los_check = value; } + inline bool CheckLastLosState() const { return last_los_check; } //Quest void QuestReward(Client *c = nullptr, uint32 silver = 0, uint32 gold = 0, uint32 platinum = 0); @@ -482,6 +497,7 @@ public: //Util static uint32 RandomTimer(int min, int max); static uint8 GetDefaultGender(uint16 in_race, uint8 in_gender = 0xFF); + static bool IsPlayerRace(uint16 in_race); uint16 GetSkillByItemType(int ItemType); uint8 GetItemTypeBySkill(SkillUseTypes skill); virtual void MakePet(uint16 spell_id, const char* pettype, const char *petname = nullptr); @@ -524,7 +540,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;} @@ -561,13 +577,14 @@ public: uint8 in_haircolor = 0xFF, uint8 in_beardcolor = 0xFF, uint8 in_eyecolor1 = 0xFF, uint8 in_eyecolor2 = 0xFF, uint8 in_hairstyle = 0xFF, uint8 in_luclinface = 0xFF, uint8 in_beard = 0xFF, uint8 in_aa_title = 0xFF, uint32 in_drakkin_heritage = 0xFFFFFFFF, uint32 in_drakkin_tattoo = 0xFFFFFFFF, - uint32 in_drakkin_details = 0xFFFFFFFF, float in_size = 0xFFFFFFFF); + uint32 in_drakkin_details = 0xFFFFFFFF, float in_size = -1.0f); + bool RandomizeFeatures(bool send_illusion = true, bool set_variables = true); virtual void Stun(int duration); virtual void UnStun(); inline void Silence(bool newval) { silenced = newval; } inline void Amnesia(bool newval) { amnesiad = newval; } - void TemporaryPets(uint16 spell_id, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0); - void TypesTemporaryPets(uint32 typesid, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme = false); + void TemporaryPets(uint16 spell_id, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme=true, bool sticktarg=false); + void TypesTemporaryPets(uint32 typesid, Mob *target, const char *name_override = nullptr, uint32 duration_override = 0, bool followme=true, bool sticktarg=false); void WakeTheDead(uint16 spell_id, Mob *target, uint32 duration); void Spin(); void Kill(); @@ -577,8 +594,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); @@ -619,6 +635,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); @@ -664,9 +681,15 @@ public: inline virtual bool HasOwner() { if(GetOwnerID()==0){return false;} return( entity_list.GetMob(GetOwnerID()) != 0); } inline virtual bool IsPet() { return(HasOwner() && !IsMerc()); } inline bool HasPet() const { if(GetPetID()==0){return false;} return (entity_list.GetMob(GetPetID()) != 0);} - bool HadTempPets() const { return(hasTempPet); } - void TempPets(bool i) { hasTempPet = i; } + inline bool HasTempPetsActive() const { return(hasTempPet); } + inline void SetTempPetsActive(bool i) { hasTempPet = i; } + inline int16 GetTempPetCount() const { return count_TempPet; } + inline void SetTempPetCount(int16 i) { count_TempPet = i; } bool HasPetAffinity() { if (aabonuses.GivePetGroupTarget || itembonuses.GivePetGroupTarget || spellbonuses.GivePetGroupTarget) return true; return false; } + inline bool IsPetOwnerClient() const { return pet_owner_client; } + inline void SetPetOwnerClient(bool value) { pet_owner_client = value; } + inline bool IsTempPet() const { return _IsTempPet; } + inline void SetTempPet(bool value) { _IsTempPet = value; } inline const bodyType GetBodyType() const { return bodytype; } inline const bodyType GetOrigBodyType() const { return orig_bodytype; } @@ -695,7 +718,7 @@ public: bool CanThisClassBlock(void) const; int GetMonkHandToHandDelay(void); - uint16 GetClassLevelFactor(); + uint32 GetClassLevelFactor(); void Mesmerize(); inline bool IsMezzed() const { return mezzed; } inline bool IsStunned() const { return stunned; } @@ -707,9 +730,13 @@ public: int32 ReduceAllDamage(int32 damage); virtual void DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance=false, bool CanAvoid=true); - virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* item=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0); + virtual void DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const Item_Struct* AmmoItem=nullptr, uint16 weapon_damage=0, int16 chance_mod=0,int16 focus=0, int ReuseTime=0, uint32 range_id=0, int AmmoSlot=0, float speed = 4.0f); virtual void DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes skillinuse, int16 chance_mod=0, int16 focus=0, bool CanRiposte=false, int ReuseTime=0); - virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0); + virtual void DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon=nullptr, const ItemInst* Ammo=nullptr, uint16 weapon_damage=0, int16 chance_mod=0, int16 focus=0, int ReuseTime=0, uint32 range_id=0, uint32 ammo_id=0, const Item_Struct *AmmoItem=nullptr, int AmmoSlot=0, float speed= 4.0f); + bool TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot, float speed); + void ProjectileAttack(); + inline bool HasProjectileAttack() const { return ActiveProjectileATK; } + inline void SetProjectileAttack(bool value) { ActiveProjectileATK = value; } bool CanDoSpecialAttack(Mob *other); bool Flurry(ExtraAttackOptions *opts); bool Rampage(ExtraAttackOptions *opts); @@ -725,6 +752,7 @@ public: virtual void AI_Init(); virtual void AI_Start(uint32 iMoveDelay = 0); virtual void AI_Stop(); + virtual void AI_ShutDown(); virtual void AI_Process(); const char* GetEntityVariable(const char *id); @@ -752,7 +780,8 @@ public: inline const bool IsRooted() const { return rooted || permarooted; } inline const bool HasVirus() const { return has_virus; } int GetSnaredAmount(); - + inline const bool IsPseudoRooted() const { return pseudo_rooted; } + inline void SetPseudoRoot(bool prState) { pseudo_rooted = prState; } int GetCurWp() { return cur_wp; } @@ -764,6 +793,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); @@ -796,8 +826,8 @@ public: void SetGrouped(bool v); inline bool IsRaidGrouped() const { return israidgrouped; } void SetRaidGrouped(bool v); - inline bool IsLooting() const { return islooting; } - void SetLooting(bool val) { islooting = val; } + inline uint16 IsLooting() const { return entity_id_being_looted; } + void SetLooting(uint16 val) { entity_id_being_looted = val; } bool CheckWillAggro(Mob *mob); @@ -813,7 +843,7 @@ public: virtual int32 CheckHealAggroAmount(uint16 spell_id, uint32 heal_possible = 0); virtual uint32 GetAA(uint32 aa_id) const { return(0); } - uint16 GetInstrumentMod(uint16 spell_id) const; + uint32 GetInstrumentMod(uint16 spell_id) const; int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, Mob *caster = nullptr, int ticsremaining = 0); int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0); virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1); @@ -822,7 +852,7 @@ public: // HP Event inline int GetNextHPEvent() const { return nexthpevent; } void SetNextHPEvent( int hpevent ); - void SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse); + void SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse, float velocity= 4.0); inline int& GetNextIncHPEvent() { return nextinchpevent; } void SetNextIncHPEvent( int inchpevent ); @@ -838,7 +868,7 @@ public: void StopSpecialAbilityTimer(int ability); Timer *GetSpecialAbilityTimer(int ability); void ClearSpecialAbilities(); - void ProcessSpecialAbilities(const std::string str); + void ProcessSpecialAbilities(const std::string &str); Shielders_Struct shielder[MAX_SHIELDERS]; Trade* trade; @@ -922,26 +952,26 @@ protected: bool isgrouped; bool israidgrouped; bool pendinggroup; - bool islooting; + uint16 entity_id_being_looted; //the id of the entity being looted, 0 if not looting. uint8 texture; uint8 helmtexture; int AC; - int16 ATK; - int16 STR; - int16 STA; - int16 DEX; - int16 AGI; - int16 INT; - int16 WIS; - int16 CHA; - int16 MR; - int16 CR; - int16 FR; - int16 DR; - int16 PR; - int16 Corrup; - int16 PhR; + int32 ATK; + int32 STR; + int32 STA; + int32 DEX; + int32 AGI; + int32 INT; + int32 WIS; + int32 CHA; + int32 MR; + int32 CR; + int32 FR; + int32 DR; + int32 PR; + int32 Corrup; + int32 PhR; bool moving; int targeted; bool findable; @@ -1048,7 +1078,8 @@ protected: Timer attack_dw_timer; Timer ranged_timer; float attack_speed; //% increase/decrease in attack speed (not haste) - float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) + int8 attack_delay; //delay between attacks in 10ths of seconds + int16 slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%) Timer tic_timer; Timer mana_timer; @@ -1071,11 +1102,8 @@ protected: uint8 bardsong_slot; uint32 bardsong_target_id; - Timer projectile_timer; - uint32 projectile_spell_id[MAX_SPELL_PROJECTILE]; - uint16 projectile_target_id[MAX_SPELL_PROJECTILE]; - uint8 projectile_increment[MAX_SPELL_PROJECTILE]; - float projectile_x[MAX_SPELL_PROJECTILE], projectile_y[MAX_SPELL_PROJECTILE], projectile_z[MAX_SPELL_PROJECTILE]; + bool ActiveProjectileATK; + tProjatk ProjectileAtk[MAX_SPELL_PROJECTILE]; float rewind_x; float rewind_y; @@ -1119,6 +1147,9 @@ protected: bool has_MGB; bool has_ProjectIllusion; int16 SpellPowerDistanceMod; + bool last_los_check; + bool pseudo_rooted; + bool endur_upkeep; // Bind wound Timer bindwound_timer; @@ -1211,6 +1242,9 @@ protected: //temppet bool hasTempPet; + bool _IsTempPet; + int16 count_TempPet; + bool pet_owner_client; //Flags regular and pets as belonging to a client EGNode *_egnode; //the EG node we are in float tarx; @@ -1223,6 +1257,10 @@ protected: float tar_vz; float test_vector; + float targetring_x; + float targetring_y; + float targetring_z; + uint32 m_spellHitsLeft[38]; // Used to track which spells will have their numhits incremented when spell finishes casting, 38 Buffslots int flymode; bool m_targetable; diff --git a/zone/MobAI.cpp b/zone/mob_ai.cpp similarity index 88% rename from zone/MobAI.cpp rename to zone/mob_ai.cpp index e4052e47f..1f6eb47b8 100644 --- a/zone/MobAI.cpp +++ b/zone/mob_ai.cpp @@ -23,15 +23,15 @@ #include #include "npc.h" #include "masterentity.h" -#include "NpcAI.h" +#include "npc_ai.h" #include "map.h" #include "../common/moremath.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "string_ids.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "../common/rulesys.h" #include "../common/features.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "water_map.h" #include "remote_call.h" #include "remote_call_subscribe.h" @@ -543,13 +543,16 @@ void NPC::AI_Start(uint32 iMoveDelay) { void Mob::AI_Stop() { if (!IsAIControlled()) return; + pAIControlled = false; + safe_delete(AIthink_timer); safe_delete(AIwalking_timer); safe_delete(AImovement_timer); - safe_delete(AItarget_check_timer) + safe_delete(AItarget_check_timer); safe_delete(AIscanarea_timer); safe_delete(AIfeignremember_timer); + hate_list.Wipe(); } @@ -584,6 +587,33 @@ void Client::AI_Stop() { } } +// only call this on a zone shutdown event +void Mob::AI_ShutDown() { + safe_delete(PathingLOSCheckTimer); + safe_delete(PathingRouteUpdateTimerShort); + safe_delete(PathingRouteUpdateTimerLong); + + attack_timer.Disable(); + attack_dw_timer.Disable(); + ranged_timer.Disable(); + tic_timer.Disable(); + mana_timer.Disable(); + spellend_timer.Disable(); + rewind_timer.Disable(); + bindwound_timer.Disable(); + stunned_timer.Disable(); + spun_timer.Disable(); + bardsong_timer.Disable(); + gravity_timer.Disable(); + viral_timer.Disable(); + flee_timer.Disable(); + + for (int sat = 0; sat < MAX_SPECIAL_ATTACK; ++sat) { + if (SpecialAbilities[sat].timer) + SpecialAbilities[sat].timer->Disable(); + } +} + //todo: expand the logic here to cover: //redundant debuffs //buffing owner @@ -1011,7 +1041,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()) { @@ -1058,7 +1088,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 { @@ -1183,7 +1215,7 @@ void Mob::AI_Process() { if (GetSpecialAbility(SPECATK_FLURRY)) { int flurry_chance = GetSpecialAbilityParam(SPECATK_FLURRY, 0); - flurry_chance = flurry_chance > 0 ? flurry_chance : RuleI(Combat, NPCFlurryChance); + flurry_chance = flurry_chance > 0 ? flurry_chance : RuleI(Combat, NPCFlurryChance); if (MakeRandomInt(0, 99) < flurry_chance) { ExtraAttackOptions opts; @@ -1217,16 +1249,16 @@ void Mob::AI_Process() { if (IsPet() || (IsNPC() && CastToNPC()->GetSwarmOwner())) { Mob *owner = nullptr; - + if (IsPet()) owner = GetOwner(); - else + else owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); - + if (owner) { int16 flurry_chance = owner->aabonuses.PetFlurry + owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry; - + if (flurry_chance && (MakeRandomInt(0, 99) < flurry_chance)) Flurry(nullptr); } @@ -1407,7 +1439,7 @@ void Mob::AI_Process() { else { if(AIfeignremember_timer->Check()) { - // EverHood - 6/14/06 + // 6/14/06 // Improved Feign Death Memory // check to see if any of our previous feigned targets have gotten up. std::set::iterator RememberedCharID; @@ -1571,11 +1603,7 @@ void Mob::AI_Process() { //Do Ranged attack here if(doranged) { - int attacks = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0); - attacks = attacks > 0 ? attacks : 1; - for(int i = 0; i < attacks; ++i) { - RangedAttack(target); - } + RangedAttack(target); } } @@ -1606,7 +1634,7 @@ void NPC::AI_DoMovement() { roambox_movingto_x -= movex * 2; if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) roambox_movingto_y -= movey * 2; - //New coord is still invalid, ignore distance and just pick a new random coord. + //New coord is still invalid, ignore distance and just pick a new random coord. //If we're here we may have a roambox where one side is shorter than the specified distance. Commons, Wkarana, etc. if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x) roambox_movingto_x = MakeRandomFloat(roambox_min_x+1,roambox_max_x-1); @@ -1863,7 +1891,7 @@ void Mob::AI_Event_NoLongerEngaged() { pLastFightingDelayMoving += minLastFightingDelayMoving; else pLastFightingDelayMoving += MakeRandomInt(minLastFightingDelayMoving, maxLastFightingDelayMoving); - // EverHood - So mobs don't keep running as a ghost until AIwalking_timer fires + // So mobs don't keep running as a ghost until AIwalking_timer fires // if they were moving prior to losing all hate if(IsMoving()){ SetRunAnimSpeed(0); @@ -1898,7 +1926,7 @@ void Mob::AI_Event_NoLongerEngaged() { } //this gets called from InterruptSpell() for failure or SpellFinished() for success -void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, uint8 slot) { +void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot) { if (slot == 1) { uint32 recovery_time = 0; if (iCastSucceeded) { @@ -2426,7 +2454,7 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) { } //If any casting variables are defined in the current list, ignore those in the parent list. - if (spell_list->fail_recast || spell_list->engaged_no_sp_recast_min || spell_list->engaged_no_sp_recast_max + if (spell_list->fail_recast || spell_list->engaged_no_sp_recast_min || spell_list->engaged_no_sp_recast_max || spell_list->engaged_beneficial_self_chance || spell_list->engaged_beneficial_other_chance || spell_list->engaged_detrimental_chance || spell_list->pursue_no_sp_recast_min || spell_list->pursue_no_sp_recast_max || spell_list->pursue_detrimental_chance || spell_list->idle_no_sp_recast_min || spell_list->idle_no_sp_recast_max || spell_list->idle_beneficial_chance) { @@ -2477,7 +2505,7 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) { AISpellVar.idle_no_sp_recast_min = (_idle_no_sp_recast_min) ? _idle_no_sp_recast_min : RuleI(Spells, AI_IdleNoSpellMinRecast); AISpellVar.idle_no_sp_recast_max = (_idle_no_sp_recast_max) ? _idle_no_sp_recast_max : RuleI(Spells, AI_IdleNoSpellMaxRecast); AISpellVar.idle_beneficial_chance = (_idle_beneficial_chance) ? _idle_beneficial_chance : RuleI(Spells, AI_IdleBeneficialChance); - + if (AIspells.size() == 0) AIautocastspell_timer->Disable(); else @@ -2489,12 +2517,12 @@ bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) { npc_spells_effects_id = iDBSpellsEffectsID; AIspellsEffects.clear(); - - if (iDBSpellsEffectsID == 0) + + if (iDBSpellsEffectsID == 0) return false; - + DBnpcspellseffects_Struct* spell_effects_list = database.GetNPCSpellsEffects(iDBSpellsEffectsID); - + if (!spell_effects_list) { return false; } @@ -2522,9 +2550,9 @@ bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) { if (parentlist) { for (i=0; inumentries; i++) { if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spelleffectid > 0) { - if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base, + if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base, parentlist->entries[i].limit, parentlist->entries[i].max)) - { + { AddSpellEffectToNPCList(parentlist->entries[i].spelleffectid, parentlist->entries[i].base, parentlist->entries[i].limit, parentlist->entries[i].max); @@ -2548,10 +2576,10 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon) { if (!AI_HasSpellsEffects()) return; - + for(int i=0; i < AIspellsEffects.size(); i++) { - ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1, + ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1, true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max); } @@ -2561,10 +2589,10 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon) // adds a spell to the list, taking into account priority and resorting list as needed. void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max) { - + if(!iSpellEffectID) return; - + HasAISpellEffects = true; AISpellsEffects_Struct t; @@ -2647,7 +2675,7 @@ void NPC::AISpellsList(Client *c) DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) { if (iDBSpellsID == 0) - return 0; + return nullptr; if (!npc_spells_cache) { npc_spells_maxid = GetMaxNPCSpellsID(); @@ -2660,144 +2688,134 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) { } if (iDBSpellsID > npc_spells_maxid) - return 0; + return nullptr; if (npc_spells_cache[iDBSpellsID]) { // it's in the cache, easy =) return npc_spells_cache[iDBSpellsID]; } else if (!npc_spells_loadtried[iDBSpellsID]) { // no reason to ask the DB again if we have failed once already npc_spells_loadtried[iDBSpellsID] = true; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list, attack_proc, proc_chance, range_proc, rproc_chance, defensive_proc, dproc_chance, fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, pursue_no_sp_recast_min, pursue_no_sp_recast_max, pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, idle_b_chance from npc_spells where id=%d", iDBSpellsID), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 tmpparent_list = atoi(row[1]); - uint16 tmpattack_proc = atoi(row[2]); - uint8 tmpproc_chance = atoi(row[3]); - uint16 tmprange_proc = atoi(row[4]); - int16 tmprproc_chance = atoi(row[5]); - uint16 tmpdefensive_proc = atoi(row[6]); - int16 tmpdproc_chance = atoi(row[7]); - uint32 tmppfail_recast = atoi(row[8]); - uint32 tmpengaged_no_sp_recast_min = atoi(row[9]); - uint32 tmpengaged_no_sp_recast_max = atoi(row[10]); - uint8 tmpengaged_b_self_chance = atoi(row[11]); - uint8 tmpengaged_b_other_chance = atoi(row[12]); - uint8 tmpengaged_d_chance = atoi(row[13]); - uint32 tmppursue_no_sp_recast_min = atoi(row[14]); - uint32 tmppursue_no_sp_recast_max = atoi(row[15]); - uint8 tmppursue_d_chance = atoi(row[16]); - uint32 tmpidle_no_sp_recast_min = atoi(row[17]); - uint32 tmpidle_no_sp_recast_max = atoi(row[18]); - uint8 tmpidle_b_chance = atoi(row[19]); - mysql_free_result(result); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT spellid, type, minlevel, maxlevel, manacost, recast_delay, priority, resist_adjust from npc_spells_entries where npc_spells_id=%d ORDER BY minlevel", iDBSpellsID), errbuf, &result)) { - safe_delete_array(query); - uint32 tmpSize = sizeof(DBnpcspells_Struct) + (sizeof(DBnpcspells_entries_Struct) * mysql_num_rows(result)); - npc_spells_cache[iDBSpellsID] = (DBnpcspells_Struct*) new uchar[tmpSize]; - memset(npc_spells_cache[iDBSpellsID], 0, tmpSize); - npc_spells_cache[iDBSpellsID]->parent_list = tmpparent_list; - npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc; - npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance; - npc_spells_cache[iDBSpellsID]->range_proc = tmprange_proc; - npc_spells_cache[iDBSpellsID]->rproc_chance = tmpdproc_chance; - npc_spells_cache[iDBSpellsID]->defensive_proc = tmpdefensive_proc; - npc_spells_cache[iDBSpellsID]->dproc_chance = tmpdproc_chance; - npc_spells_cache[iDBSpellsID]->fail_recast = tmppfail_recast; - npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_min = tmpengaged_no_sp_recast_min; - npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_max = tmpengaged_no_sp_recast_max; - npc_spells_cache[iDBSpellsID]->engaged_beneficial_self_chance = tmpengaged_b_self_chance; - npc_spells_cache[iDBSpellsID]->engaged_beneficial_other_chance = tmpengaged_b_other_chance; - npc_spells_cache[iDBSpellsID]->engaged_detrimental_chance = tmpengaged_d_chance; - npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_min = tmppursue_no_sp_recast_min; - npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_max = tmppursue_no_sp_recast_max; - npc_spells_cache[iDBSpellsID]->pursue_detrimental_chance = tmppursue_d_chance; - npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_min = tmpidle_no_sp_recast_min; - npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_max = tmpidle_no_sp_recast_max; - npc_spells_cache[iDBSpellsID]->idle_beneficial_chance = tmpidle_b_chance; - npc_spells_cache[iDBSpellsID]->numentries = mysql_num_rows(result); - int j = 0; - while ((row = mysql_fetch_row(result))) { - int spell_id = atoi(row[0]); - npc_spells_cache[iDBSpellsID]->entries[j].spellid = spell_id; - npc_spells_cache[iDBSpellsID]->entries[j].type = atoi(row[1]); - npc_spells_cache[iDBSpellsID]->entries[j].minlevel = atoi(row[2]); - npc_spells_cache[iDBSpellsID]->entries[j].maxlevel = atoi(row[3]); - npc_spells_cache[iDBSpellsID]->entries[j].manacost = atoi(row[4]); - npc_spells_cache[iDBSpellsID]->entries[j].recast_delay = atoi(row[5]); - npc_spells_cache[iDBSpellsID]->entries[j].priority = atoi(row[6]); - if(row[7]) - { - npc_spells_cache[iDBSpellsID]->entries[j].resist_adjust = atoi(row[7]); - } - else - { - if(IsValidSpell(spell_id)) - { - npc_spells_cache[iDBSpellsID]->entries[j].resist_adjust = spells[spell_id].ResistDiff; - } - } - j++; - } - mysql_free_result(result); - return npc_spells_cache[iDBSpellsID]; - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - } - else { - mysql_free_result(result); - } - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } + std::string query = StringFormat("SELECT id, parent_list, attack_proc, proc_chance, " + "range_proc, rproc_chance, defensive_proc, dproc_chance, " + "fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, " + "engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, " + "pursue_no_sp_recast_min, pursue_no_sp_recast_max, " + "pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, " + "idle_b_chance FROM npc_spells WHERE id=%d", iDBSpellsID); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << results.ErrorMessage() << std::endl; + return nullptr; + } - return 0; - } - return 0; + if (results.RowCount() != 1) + return nullptr; + + auto row = results.begin(); + uint32 tmpparent_list = atoi(row[1]); + uint16 tmpattack_proc = atoi(row[2]); + uint8 tmpproc_chance = atoi(row[3]); + uint16 tmprange_proc = atoi(row[4]); + int16 tmprproc_chance = atoi(row[5]); + uint16 tmpdefensive_proc = atoi(row[6]); + int16 tmpdproc_chance = atoi(row[7]); + uint32 tmppfail_recast = atoi(row[8]); + uint32 tmpengaged_no_sp_recast_min = atoi(row[9]); + uint32 tmpengaged_no_sp_recast_max = atoi(row[10]); + uint8 tmpengaged_b_self_chance = atoi(row[11]); + uint8 tmpengaged_b_other_chance = atoi(row[12]); + uint8 tmpengaged_d_chance = atoi(row[13]); + uint32 tmppursue_no_sp_recast_min = atoi(row[14]); + uint32 tmppursue_no_sp_recast_max = atoi(row[15]); + uint8 tmppursue_d_chance = atoi(row[16]); + uint32 tmpidle_no_sp_recast_min = atoi(row[17]); + uint32 tmpidle_no_sp_recast_max = atoi(row[18]); + uint8 tmpidle_b_chance = atoi(row[19]); + + query = StringFormat("SELECT spellid, type, minlevel, maxlevel, " + "manacost, recast_delay, priority, resist_adjust " + "FROM npc_spells_entries " + "WHERE npc_spells_id=%d ORDER BY minlevel", iDBSpellsID); + results = QueryDatabase(query); + + if (!results.Success()) + { + std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << results.ErrorMessage() << std::endl; + return nullptr; + } + + uint32 tmpSize = sizeof(DBnpcspells_Struct) + (sizeof(DBnpcspells_entries_Struct) * results.RowCount()); + npc_spells_cache[iDBSpellsID] = (DBnpcspells_Struct*) new uchar[tmpSize]; + memset(npc_spells_cache[iDBSpellsID], 0, tmpSize); + npc_spells_cache[iDBSpellsID]->parent_list = tmpparent_list; + npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc; + npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance; + npc_spells_cache[iDBSpellsID]->range_proc = tmprange_proc; + npc_spells_cache[iDBSpellsID]->rproc_chance = tmpdproc_chance; + npc_spells_cache[iDBSpellsID]->defensive_proc = tmpdefensive_proc; + npc_spells_cache[iDBSpellsID]->dproc_chance = tmpdproc_chance; + npc_spells_cache[iDBSpellsID]->fail_recast = tmppfail_recast; + npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_min = tmpengaged_no_sp_recast_min; + npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_max = tmpengaged_no_sp_recast_max; + npc_spells_cache[iDBSpellsID]->engaged_beneficial_self_chance = tmpengaged_b_self_chance; + npc_spells_cache[iDBSpellsID]->engaged_beneficial_other_chance = tmpengaged_b_other_chance; + npc_spells_cache[iDBSpellsID]->engaged_detrimental_chance = tmpengaged_d_chance; + npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_min = tmppursue_no_sp_recast_min; + npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_max = tmppursue_no_sp_recast_max; + npc_spells_cache[iDBSpellsID]->pursue_detrimental_chance = tmppursue_d_chance; + npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_min = tmpidle_no_sp_recast_min; + npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_max = tmpidle_no_sp_recast_max; + npc_spells_cache[iDBSpellsID]->idle_beneficial_chance = tmpidle_b_chance; + npc_spells_cache[iDBSpellsID]->numentries = results.RowCount(); + + int entryIndex = 0; + for (row = results.begin(); row != results.end(); ++row, ++entryIndex) + { + int spell_id = atoi(row[0]); + npc_spells_cache[iDBSpellsID]->entries[entryIndex].spellid = spell_id; + npc_spells_cache[iDBSpellsID]->entries[entryIndex].type = atoi(row[1]); + npc_spells_cache[iDBSpellsID]->entries[entryIndex].minlevel = atoi(row[2]); + npc_spells_cache[iDBSpellsID]->entries[entryIndex].maxlevel = atoi(row[3]); + npc_spells_cache[iDBSpellsID]->entries[entryIndex].manacost = atoi(row[4]); + npc_spells_cache[iDBSpellsID]->entries[entryIndex].recast_delay = atoi(row[5]); + npc_spells_cache[iDBSpellsID]->entries[entryIndex].priority = atoi(row[6]); + + if(row[7]) + npc_spells_cache[iDBSpellsID]->entries[entryIndex].resist_adjust = atoi(row[7]); + else if(IsValidSpell(spell_id)) + npc_spells_cache[iDBSpellsID]->entries[entryIndex].resist_adjust = spells[spell_id].ResistDiff; + } + + return npc_spells_cache[iDBSpellsID]; + } + + return nullptr; } uint32 ZoneDatabase::GetMaxNPCSpellsID() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells"), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 ret = 0; - if (row[0]) - ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetMaxNPCSpellsID query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = "SELECT max(id) from npc_spells"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetMaxNPCSpellsID query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + if (!row[0]) + return 0; + + return atoi(row[0]); } DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEffectsID) { if (iDBSpellsEffectsID == 0) - return 0; + return nullptr; if (!npc_spellseffects_cache) { npc_spellseffects_maxid = GetMaxNPCSpellsEffectsID(); @@ -2810,89 +2828,74 @@ DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEff } if (iDBSpellsEffectsID > npc_spellseffects_maxid) - return 0; - if (npc_spellseffects_cache[iDBSpellsEffectsID]) { // it's in the cache, easy =) + return nullptr; + + if (npc_spellseffects_cache[iDBSpellsEffectsID]) // it's in the cache, easy =) return npc_spellseffects_cache[iDBSpellsEffectsID]; - } - else if (!npc_spellseffects_loadtried[iDBSpellsEffectsID]) { // no reason to ask the DB again if we have failed once already - npc_spellseffects_loadtried[iDBSpellsEffectsID] = true; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + if (npc_spellseffects_loadtried[iDBSpellsEffectsID]) + return nullptr; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list from npc_spells_effects where id=%d", iDBSpellsEffectsID), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 tmpparent_list = atoi(row[1]); - mysql_free_result(result); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_effect_id, minlevel, maxlevel,se_base, se_limit, se_max from npc_spells_effects_entries where npc_spells_effects_id=%d ORDER BY minlevel", iDBSpellsEffectsID), errbuf, &result)) { - safe_delete_array(query); - uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_entries_Struct) * mysql_num_rows(result)); - npc_spellseffects_cache[iDBSpellsEffectsID] = (DBnpcspellseffects_Struct*) new uchar[tmpSize]; - memset(npc_spellseffects_cache[iDBSpellsEffectsID], 0, tmpSize); - npc_spellseffects_cache[iDBSpellsEffectsID]->parent_list = tmpparent_list; - npc_spellseffects_cache[iDBSpellsEffectsID]->numentries = mysql_num_rows(result); - int j = 0; - while ((row = mysql_fetch_row(result))) { - int spell_effect_id = atoi(row[0]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].spelleffectid = spell_effect_id; - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].minlevel = atoi(row[1]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].maxlevel = atoi(row[2]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].base = atoi(row[3]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].limit = atoi(row[4]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].max = atoi(row[5]); - j++; - } - mysql_free_result(result); - return npc_spellseffects_cache[iDBSpellsEffectsID]; - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - } - else { - mysql_free_result(result); - } - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - return 0; - } - return 0; + npc_spellseffects_loadtried[iDBSpellsEffectsID] = true; + + std::string query = StringFormat("SELECT id, parent_list FROM npc_spells_effects WHERE id=%d", iDBSpellsEffectsID); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << results.ErrorMessage() << std::endl; + return nullptr; + } + + if (results.RowCount() != 1) + return nullptr; + + auto row = results.begin(); + uint32 tmpparent_list = atoi(row[1]); + + query = StringFormat("SELECT spell_effect_id, minlevel, " + "maxlevel,se_base, se_limit, se_max " + "FROM npc_spells_effects_entries " + "WHERE npc_spells_effects_id = %d ORDER BY minlevel", iDBSpellsEffectsID); + results = QueryDatabase(query); + if (!results.Success()) + return nullptr; + + uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_entries_Struct) * results.RowCount()); + npc_spellseffects_cache[iDBSpellsEffectsID] = (DBnpcspellseffects_Struct*) new uchar[tmpSize]; + memset(npc_spellseffects_cache[iDBSpellsEffectsID], 0, tmpSize); + npc_spellseffects_cache[iDBSpellsEffectsID]->parent_list = tmpparent_list; + npc_spellseffects_cache[iDBSpellsEffectsID]->numentries = results.RowCount(); + + int entryIndex = 0; + for (row = results.begin(); row != results.end(); ++row, ++entryIndex) + { + int spell_effect_id = atoi(row[0]); + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].spelleffectid = spell_effect_id; + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].minlevel = atoi(row[1]); + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].maxlevel = atoi(row[2]); + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].base = atoi(row[3]); + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].limit = atoi(row[4]); + npc_spellseffects_cache[iDBSpellsEffectsID]->entries[entryIndex].max = atoi(row[5]); + } + + return npc_spellseffects_cache[iDBSpellsEffectsID]; } uint32 ZoneDatabase::GetMaxNPCSpellsEffectsID() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells_effects"), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 ret = 0; - if (row[0]) - ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetMaxNPCSpellsEffectsID query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = "SELECT max(id) FROM npc_spells_effects"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetMaxNPCSpellsEffectsID query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + if (!row[0]) + return 0; + + return atoi(row[0]); } diff --git a/zone/mod_functions.cpp b/zone/mod_functions.cpp index 0a9a66537..6ae0eddad 100644 --- a/zone/mod_functions.cpp +++ b/zone/mod_functions.cpp @@ -1,22 +1,15 @@ -#include "../common/debug.h" -#include "../common/timer.h" -#include -#include -#include "spawn2.h" -#include "entity.h" -#include "masterentity.h" -#include "zone.h" -#include "spawngroup.h" -#include "zonedb.h" -#include "npc.h" -#include "mob.h" #include "client.h" +#include "entity.h" +#include "mob.h" +#include "npc.h" #include "worldserver.h" -#include "QuestParserCollection.h" -#include "event_codes.h" -#include "embparser.h" -#include -#include +#include "zone.h" + +class ItemInst; +class Spawn2; +struct Consider_Struct; +struct DBTradeskillRecipe_Struct; +struct Item_Struct; extern EntityList entity_list; extern Zone* zone; diff --git a/zone/mod_functions_base.cpp b/zone/mod_functions_base.cpp index 8beb6cd79..e9e172997 100644 --- a/zone/mod_functions_base.cpp +++ b/zone/mod_functions_base.cpp @@ -12,7 +12,7 @@ #include "mob.h" #include "client.h" #include "worldserver.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "event_codes.h" #include "embparser.h" #include diff --git a/zone/net.cpp b/zone/net.cpp index f89fa165a..95ddca6d0 100644 --- a/zone/net.cpp +++ b/zone/net.cpp @@ -22,20 +22,20 @@ #include "../common/features.h" #include "../common/queue.h" #include "../common/timer.h" -#include "../common/EQStream.h" -#include "../common/EQStreamFactory.h" +#include "../common/eq_stream.h" +#include "../common/eq_stream_factory.h" #include "../common/eq_packet_structs.h" -#include "../common/Mutex.h" +#include "../common/mutex.h" #include "../common/version.h" -#include "../common/EQEMuError.h" +#include "../common/eqemu_error.h" #include "../common/packet_dump_file.h" #include "../common/opcodemgr.h" #include "../common/guilds.h" -#include "../common/EQStreamIdent.h" +#include "../common/eq_stream_ident.h" #include "../common/patches/patches.h" #include "../common/rulesys.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "../common/platform.h" #include "../common/crash.h" #include "../common/ipc_mutex.h" @@ -43,17 +43,18 @@ #include "../common/eqemu_exception.h" #include "../common/spdat.h" -#include "ZoneConfig.h" +#include "zone_config.h" #include "masterentity.h" #include "worldserver.h" #include "net.h" #include "zone.h" +#include "queryserv.h" #include "command.h" -#include "ZoneConfig.h" +#include "zone_config.h" #include "titles.h" #include "guild_mgr.h" #include "tasks.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "embparser.h" #include "lua_parser.h" #include "client_logs.h" @@ -97,8 +98,7 @@ 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; @@ -116,6 +116,8 @@ int main(int argc, char** argv) { const char *zone_name; + QServ = new QueryServ; + if(argc == 3) { worldserver.SetLauncherName(argv[2]); worldserver.SetLaunchedName(argv[1]); @@ -166,8 +168,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; @@ -446,10 +446,6 @@ int main(int argc, char** argv) { } } } - DBAsyncWork* dbaw = 0; - while ((dbaw = MTdbafq.Pop())) { - DispatchFinishedDBAsync(dbaw); - } if (InterserverTimer.Check()) { InterserverTimer.Start(); database.ping(); @@ -509,8 +505,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); @@ -628,7 +622,7 @@ void LoadSpells(EQEmu::MemoryMappedFile **mmf) { SPDAT_RECORDS = records; } - +/* Update Window Title with relevant information */ void UpdateWindowTitle(char* iNewTitle) { #ifdef _WINDOWS char tmp[500]; @@ -640,7 +634,7 @@ void UpdateWindowTitle(char* iNewTitle) { #if defined(GOTFRAGS) || defined(_EQDEBUG) snprintf(tmp, sizeof(tmp), "%i: %s, %i clients, %i", ZoneConfig::get()->ZonePort, zone->GetShortName(), numclients, getpid()); #else - snprintf(tmp, sizeof(tmp), "%i: %s, %i clients", ZoneConfig::get()->ZonePort, zone->GetShortName(), numclients); + snprintf(tmp, sizeof(tmp), "%s :: clients: %i inst_id: %i inst_ver: %i :: port: %i", zone->GetShortName(), numclients, zone->GetInstanceID(), zone->GetInstanceVersion(), ZoneConfig::get()->ZonePort); #endif } else { @@ -654,4 +648,3 @@ void UpdateWindowTitle(char* iNewTitle) { SetConsoleTitle(tmp); #endif } - diff --git a/zone/net.h b/zone/net.h index e81dd6d51..c49e1ec8e 100644 --- a/zone/net.h +++ b/zone/net.h @@ -26,9 +26,6 @@ #include #endif - -#include -#include #include "../common/types.h" #include "../common/timer.h" void CatchSignal(int); diff --git a/zone/npc.cpp b/zone/npc.cpp index f4e307bbc..17ab9ed48 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -15,15 +15,34 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#include "../common/bodytypes.h" +#include "../common/classes.h" #include "../common/debug.h" -#include -#include -#include -#include -#include "../common/moremath.h" -#include -#include "../common/packet_dump_file.h" +#include "../common/misc_functions.h" +#include "../common/rulesys.h" +#include "../common/seperator.h" +#include "../common/spdat.h" +#include "../common/string_util.h" +#include "../common/clientversions.h" +#include "../common/features.h" +#include "../common/item.h" +#include "../common/item_struct.h" +#include "../common/linked_list.h" +#include "../common/servertalk.h" + +#include "aa.h" +#include "client.h" +#include "entity.h" +#include "npc.h" +#include "string_ids.h" +#include "spawn2.h" #include "zone.h" + +#include +#include +#include + #ifdef _WINDOWS #define snprintf _snprintf #define strncasecmp _strnicmp @@ -33,27 +52,10 @@ #include #endif -#include "npc.h" -#include "map.h" -#include "entity.h" -#include "masterentity.h" -#include "../common/spdat.h" -#include "../common/bodytypes.h" -#include "spawngroup.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" -#include "../common/rulesys.h" -#include "StringIDs.h" - -//#define SPELLQUEUE //Use only if you want to be spammed by spell testing - - extern Zone* zone; extern volatile bool ZoneLoaded; extern EntityList entity_list; -#include "QuestParserCollection.h" - NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float heading, int iflymode, bool IsCorpse) : Mob(d->name, d->lastname, @@ -248,6 +250,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float delaytimer = false; combat_event = false; attack_speed = d->attack_speed; + attack_delay = d->attack_delay; slow_mitigation = d->slow_mitigation; EntityList::RemoveNumbers(name); @@ -404,8 +407,8 @@ void NPC::SetTarget(Mob* mob) { if(mob == GetTarget()) //dont bother if they are allready our target return; - //our target is already set, do not turn from the course, unless our current target is dead. - if(GetSwarmInfo() && GetTarget() && (GetTarget()->GetHP() > 0)) { + //This is not the default behavior for swarm pets, must be specified from quest functions or rules value. + if(GetSwarmInfo() && GetSwarmInfo()->target && GetTarget() && (GetTarget()->GetHP() > 0)) { Mob *targ = entity_list.GetMob(GetSwarmInfo()->target); if(targ != mob){ return; @@ -428,7 +431,7 @@ ServerLootItem_Struct* NPC::GetItem(int slot_id) { end = itemlist.end(); for(; cur != end; ++cur) { ServerLootItem_Struct* item = *cur; - if (item->equipSlot == slot_id) { + if (item->equip_slot == slot_id) { return item; } } @@ -445,7 +448,7 @@ void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot) { itemlist.erase(cur); return; } - else if (item->item_id == item_id && item->equipSlot == slot && quantity >= 1) { + else if (item->item_id == item_id && item->equip_slot == slot && quantity >= 1) { //std::cout<<"NPC::RemoveItem"<<" equipSlot:"<equipSlot<<" quantity:"<< quantity<charges <= quantity) itemlist.erase(cur); @@ -470,9 +473,9 @@ void NPC::CheckMinMaxLevel(Mob *them) if(!(*cur)) return; - if(themlevel < (*cur)->minlevel || themlevel > (*cur)->maxlevel) + if(themlevel < (*cur)->min_level || themlevel > (*cur)->max_level) { - material = Inventory::CalcMaterialFromSlot((*cur)->equipSlot); + material = Inventory::CalcMaterialFromSlot((*cur)->equip_slot); if(material != 0xFF) SendWearChange(material); @@ -507,15 +510,15 @@ void NPC::QueryLoot(Client* to) { if (item) if (to->GetClientVersion() >= EQClientRoF) { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X0000000000000000000000000000000000000000000000000%s%c",(*cur)->minlevel, (*cur)->maxlevel, (int) item->ID,0x12, item->ID, item->Name, 0x12); + to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X0000000000000000000000000000000000000000000000000%s%c",(*cur)->min_level, (*cur)->max_level, (int) item->ID,0x12, item->ID, item->Name, 0x12); } else if (to->GetClientVersion() >= EQClientSoF) { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X00000000000000000000000000000000000000000000%s%c",(*cur)->minlevel, (*cur)->maxlevel, (int) item->ID,0x12, item->ID, item->Name, 0x12); + to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X00000000000000000000000000000000000000000000%s%c",(*cur)->min_level, (*cur)->max_level, (int) item->ID,0x12, item->ID, item->Name, 0x12); } else { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X000000000000000000000000000000000000000%s%c",(*cur)->minlevel, (*cur)->maxlevel, (int) item->ID,0x12, item->ID, item->Name, 0x12); + to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X000000000000000000000000000000000000000%s%c",(*cur)->min_level, (*cur)->max_level, (int) item->ID,0x12, item->ID, item->Name, 0x12); } else LogFile->write(EQEMuLog::Error, "Database error, invalid item"); @@ -667,8 +670,7 @@ bool NPC::Process() viral_timer_counter = 0; } - if(projectile_timer.Check()) - SpellProjectileEffect(); + ProjectileAttack(); if(spellbonuses.GravityEffect == 1) { if(gravity_timer.Check()) @@ -935,6 +937,8 @@ NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, npc_type->WIS = 150; npc_type->CHA = 150; + npc_type->attack_delay = 30; + npc_type->prim_melee_type = 28; npc_type->sec_melee_type = 28; @@ -962,240 +966,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; @@ -1726,53 +1842,64 @@ bool Mob::HasNPCSpecialAtk(const char* parse) { void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) { Mob::FillSpawnStruct(ns, ForWho); + PetOnSpawn(ns); + ns->spawn.is_npc = 1; +} +void NPC::PetOnSpawn(NewSpawn_Struct* ns) +{ //Basic settings to make sure swarm pets work properly. - if (GetSwarmOwner()) { - Client *c = entity_list.GetClientByID(GetSwarmOwner()); - if(c) { - SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed. - //This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'! - if (RuleB(Pets, SwarmPetNotTargetableWithHotKey)) - ns->spawn.IsMercenary = 1; - } - //NPC cast swarm pets should still be targetable with F8. - else - ns->spawn.IsMercenary = 0; + Mob *swarmOwner = nullptr; + if (GetSwarmOwner()) + { + swarmOwner = entity_list.GetMobID(GetSwarmOwner()); } - //Not recommended if using above (However, this will work better on older clients). - if (RuleB(Pets, UnTargetableSwarmPet)) { - if(GetOwnerID() || GetSwarmOwner()) { - ns->spawn.is_pet = 1; - if (!IsCharmed() && GetOwnerID()) { - Client *c = entity_list.GetClientByID(GetOwnerID()); - if(c) - sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); - } - else if (GetSwarmOwner()) { - ns->spawn.bodytype = 11; - if(!IsCharmed()) - { - Client *c = entity_list.GetClientByID(GetSwarmOwner()); - if(c) - sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); - } + if (swarmOwner != nullptr) + { + if(swarmOwner->IsClient()) + { + SetPetOwnerClient(true); //Simple flag to determine if pet belongs to a client + SetAllowBeneficial(1);//Allow temp pets to receive buffs and heals if owner is client. + //This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'! + if (RuleB(Pets, SwarmPetNotTargetableWithHotKey)) + ns->spawn.IsMercenary = 1; + } + else + { + //NPC cast swarm pets should still be targetable with F8. + ns->spawn.IsMercenary = 0; + } + + SetTempPet(true); //Simple mob flag for checking if temp pet + swarmOwner->SetTempPetsActive(true); //Necessary fail safe flag set if mob ever had a swarm pet to ensure they are removed. + swarmOwner->SetTempPetCount(swarmOwner->GetTempPetCount() + 1); + + //Not recommended if using above (However, this will work better on older clients). + if (RuleB(Pets, UnTargetableSwarmPet)) + { + ns->spawn.bodytype = 11; + if(!IsCharmed() && swarmOwner->IsClient()) + sprintf(ns->spawn.lastName, "%s's Pet", swarmOwner->GetName()); + } + } + else if(GetOwnerID()) + { + ns->spawn.is_pet = 1; + if (!IsCharmed()) + { + Client *client = entity_list.GetClientByID(GetOwnerID()); + if(client) + { + SetPetOwnerClient(true); + sprintf(ns->spawn.lastName, "%s's Pet", client->GetName()); } } - } else { - if(GetOwnerID()) { - ns->spawn.is_pet = 1; - if (!IsCharmed() && GetOwnerID()) { - Client *c = entity_list.GetClientByID(GetOwnerID()); - if(c) - sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); - } - } else - ns->spawn.is_pet = 0; } - - ns->spawn.is_npc = 1; + else + { + ns->spawn.is_pet = 0; + } } void NPC::SetLevel(uint8 in_level, bool command) @@ -1787,252 +1914,50 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue) { std::string id = identifier; std::string val = newValue; - for(int i = 0; i < id.length(); ++i) - { + for(int i = 0; i < id.length(); ++i) { id[i] = std::tolower(id[i]); } - if(id == "ac") - { - AC = atoi(val.c_str()); - return; - } - - if(id == "str") - { - STR = atoi(val.c_str()); - return; - } - - if(id == "sta") - { - STA = atoi(val.c_str()); - return; - } - - if(id == "agi") - { - AGI = atoi(val.c_str()); - return; - } - - if(id == "dex") - { - DEX = atoi(val.c_str()); - return; - } - - if(id == "wis") - { - WIS = atoi(val.c_str()); - CalcMaxMana(); - return; - } - - if(id == "int" || id == "_int") - { - INT = atoi(val.c_str()); - CalcMaxMana(); - return; - } - - if(id == "cha") - { - CHA = atoi(val.c_str()); - return; - } - - if(id == "max_hp") - { - base_hp = atoi(val.c_str()); - CalcMaxHP(); - if(cur_hp > max_hp) - cur_hp = max_hp; - return; - } - - if(id == "max_mana") - { - npc_mana = atoi(val.c_str()); - CalcMaxMana(); - if(cur_mana > max_mana) - cur_mana = max_mana; - return; - } - - if(id == "mr") - { - MR = atoi(val.c_str()); - return; - } - - if(id == "fr") - { - FR = atoi(val.c_str()); - return; - } - - if(id == "cr") - { - CR = atoi(val.c_str()); - return; - } - - if(id == "pr") - { - PR = atoi(val.c_str()); - return; - } - - if(id == "dr") - { - DR = atoi(val.c_str()); - return; - } - - if(id == "PhR") - { - PhR = atoi(val.c_str()); - return; - } - - if(id == "runspeed") - { - runspeed = (float)atof(val.c_str()); - CalcBonuses(); - return; - } - - if(id == "special_attacks") - { - //Added reset flag. - NPCSpecialAttacks(val.c_str(), 0, 1); - return; - } - - if(id == "attack_speed") - { - attack_speed = (float)atof(val.c_str()); - CalcBonuses(); - return; - } - - if(id == "atk") - { - ATK = atoi(val.c_str()); - return; - } - - if(id == "accuracy") - { - accuracy_rating = atoi(val.c_str()); - return; - } - - if(id == "avoidance") - { - avoidance_rating = atoi(val.c_str()); - return; - } - - if(id == "trackable") - { - trackable = atoi(val.c_str()); - return; - } - - if(id == "min_hit") - { - min_dmg = atoi(val.c_str()); - return; - } - - if(id == "max_hit") - { - max_dmg = atoi(val.c_str()); - return; - } - - if(id == "attack_count") - { - attack_count = atoi(val.c_str()); - return; - } - - if(id == "see_invis") - { - see_invis = atoi(val.c_str()); - return; - } - - if(id == "see_invis_undead") - { - see_invis_undead = atoi(val.c_str()); - return; - } - - if(id == "see_hide") - { - see_hide = atoi(val.c_str()); - return; - } - - if(id == "see_improved_hide") - { - see_improved_hide = atoi(val.c_str()); - return; - } - - if(id == "hp_regen") - { - hp_regen = atoi(val.c_str()); - return; - } - - if(id == "mana_regen") - { - mana_regen = atoi(val.c_str()); - return; - } - - if(id == "level") - { - SetLevel(atoi(val.c_str())); - return; - } - - if(id == "aggro") - { - pAggroRange = atof(val.c_str()); - return; - } - - if(id == "assist") - { - pAssistRange = atof(val.c_str()); - return; - } - - if(id == "slow_mitigation") - { - slow_mitigation = atoi(val.c_str()); - return; - } - if(id == "loottable_id") - { - loottable_id = atof(val.c_str()); - return; - } - if(id == "healscale") - { - healscale = atof(val.c_str()); - return; - } - if(id == "spellscale") - { - spellscale = atof(val.c_str()); - return; - } + if(id == "ac") { AC = atoi(val.c_str()); return; } + else if(id == "str") { STR = atoi(val.c_str()); return; } + else if(id == "sta") { STA = atoi(val.c_str()); return; } + else if(id == "agi") { AGI = atoi(val.c_str()); return; } + else if(id == "dex") { DEX = atoi(val.c_str()); return; } + else if(id == "wis") { WIS = atoi(val.c_str()); CalcMaxMana(); return; } + else if(id == "int" || id == "_int") { INT = atoi(val.c_str()); CalcMaxMana(); return; } + else if(id == "cha") { CHA = atoi(val.c_str()); return; } + else if(id == "max_hp") { base_hp = atoi(val.c_str()); CalcMaxHP(); if (cur_hp > max_hp) { cur_hp = max_hp; } return; } + else if(id == "max_mana") { npc_mana = atoi(val.c_str()); CalcMaxMana(); if (cur_mana > max_mana){ cur_mana = max_mana; } return; } + else if(id == "mr") { MR = atoi(val.c_str()); return; } + else if(id == "fr") { FR = atoi(val.c_str()); return; } + else if(id == "cr") { CR = atoi(val.c_str()); return; } + else if(id == "pr") { PR = atoi(val.c_str()); return; } + else if(id == "dr") { DR = atoi(val.c_str()); return; } + else if(id == "PhR") { PhR = atoi(val.c_str()); return; } + else if(id == "runspeed") { runspeed = (float)atof(val.c_str()); CalcBonuses(); return; } + else if(id == "special_attacks") { NPCSpecialAttacks(val.c_str(), 0, 1); return; } + else if(id == "special_abilities") { ProcessSpecialAbilities(val.c_str()); return; } + else if(id == "attack_speed") { attack_speed = (float)atof(val.c_str()); CalcBonuses(); return; } + else if(id == "atk") { ATK = atoi(val.c_str()); return; } + else if(id == "accuracy") { accuracy_rating = atoi(val.c_str()); return; } + else if(id == "avoidance") { avoidance_rating = atoi(val.c_str()); return; } + else if(id == "trackable") { trackable = atoi(val.c_str()); return; } + else if(id == "min_hit") { min_dmg = atoi(val.c_str()); return; } + else if(id == "max_hit") { max_dmg = atoi(val.c_str()); return; } + else if(id == "attack_count") { attack_count = atoi(val.c_str()); return; } + else if(id == "see_invis") { see_invis = atoi(val.c_str()); return; } + else if(id == "see_invis_undead") { see_invis_undead = atoi(val.c_str()); return; } + else if(id == "see_hide") { see_hide = atoi(val.c_str()); return; } + else if(id == "see_improved_hide") { see_improved_hide = atoi(val.c_str()); return; } + else if(id == "hp_regen") { hp_regen = atoi(val.c_str()); return; } + else if(id == "mana_regen") { mana_regen = atoi(val.c_str()); return; } + else if(id == "level") { SetLevel(atoi(val.c_str())); return; } + else if(id == "aggro") { pAggroRange = atof(val.c_str()); return; } + else if(id == "assist") { pAssistRange = atof(val.c_str()); return; } + else if(id == "slow_mitigation") { slow_mitigation = atoi(val.c_str()); return; } + else if(id == "loottable_id") { loottable_id = atof(val.c_str()); return; } + else if(id == "healscale") { healscale = atof(val.c_str()); return; } + else if(id == "spellscale") { spellscale = atof(val.c_str()); return; } } void NPC::LevelScale() { @@ -2186,7 +2111,7 @@ void NPC::CalcNPCDamage() { max_dmg = (GetLevel()*2)*AC_adjust/10; } - int clfact = GetClassLevelFactor(); + int32 clfact = GetClassLevelFactor(); min_dmg = (min_dmg * clfact) / 220; max_dmg = (max_dmg * clfact) / 220; @@ -2498,3 +2423,30 @@ void NPC::DoQuestPause(Mob *other) { } } + +void NPC::DepopSwarmPets() +{ + if (GetSwarmInfo()) { + if (GetSwarmInfo()->duration->Check(false)){ + Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); + if (owner) + owner->SetTempPetCount(owner->GetTempPetCount() - 1); + + Depop(); + return; + } + + //This is only used for optional quest or rule derived behavior now if you force a temp pet on a specific target. + if (GetSwarmInfo()->target) { + Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target); + if(!targMob || (targMob && targMob->IsCorpse())){ + Mob* owner = entity_list.GetMobID(GetSwarmInfo()->owner_id); + if (owner) + owner->SetTempPetCount(owner->GetTempPetCount() - 1); + + Depop(); + return; + } + } + } +} \ No newline at end of file diff --git a/zone/npc.h b/zone/npc.h index 5c8fd43bb..cccf3ecf0 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -18,20 +18,17 @@ #ifndef NPC_H #define NPC_H -class NPC; -#include "zonedb.h" -#include "mob.h" -//#include "spawn.h" - -#include -#include - -#include "spawn2.h" -#include "../common/loottable.h" -#include "zonedump.h" -#include "QGlobals.h" #include "../common/rulesys.h" +#include "mob.h" +#include "qglobals.h" +#include "zonedb.h" +#include "zonedump.h" + +#include +#include + + #ifdef _WINDOWS #define M_PI 3.141592 #endif @@ -88,8 +85,12 @@ struct AISpellsVar_Struct { uint8 idle_beneficial_chance; }; - class AA_SwarmPetInfo; +class Client; +class Group; +class Raid; +class Spawn2; +struct Item_Struct; class NPC : public Mob { @@ -127,20 +128,15 @@ public: virtual bool AI_PursueCastCheck(); virtual bool AI_IdleCastCheck(); - virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint8 slot); + virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); void LevelScale(); void CalcNPCResists(); void CalcNPCRegen(); void CalcNPCDamage(); - int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr); int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr); - inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;} - 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; } @@ -158,6 +154,7 @@ public: virtual void InitializeBuffSlots(); virtual void UninitializeBuffSlots(); + virtual void SetAttackTimer(); virtual void RangedAttack(Mob* other); virtual void ThrowingAttack(Mob* other) { } int32 GetNumberOfAttacks() const { return attack_count; } @@ -247,6 +244,8 @@ public: uint32 GetSwarmOwner(); uint32 GetSwarmTarget(); void SetSwarmTarget(int target_id = 0); + void DepopSwarmPets(); + void PetOnSpawn(NewSpawn_Struct* ns); void SignalNPC(int _signal_id); @@ -361,8 +360,9 @@ public: const bool GetCombatEvent() const { return combat_event; } void SetCombatEvent(bool b) { combat_event = b; } - //The corpse we make can only be looted by people who got credit for the kill + /* Only allows players that killed corpse to loot */ const bool HasPrivateCorpse() const { return NPCTypedata->private_corpse; } + const bool IsUnderwaterOnly() const { return NPCTypedata->underwater; } const char* GetRawNPCTypeName() const { return NPCTypedata->name; } @@ -387,15 +387,23 @@ 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;} - uint32 GetSpawnKillCount(); - int GetScore(); - void mod_prespawn(Spawn2 *sp); - int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other); + inline void SetSpellFocusHeal(int32 NewSpellFocusHeal) {SpellFocusHeal = NewSpellFocusHeal;} + inline int32 GetSpellFocusHeal() const {return SpellFocusHeal;} + + uint32 GetSpawnKillCount(); + int GetScore(); + void SetMerchantProbability(uint8 amt) { probability = amt; } + uint8 GetMerchantProbability() { return probability; } + void mod_prespawn(Spawn2 *sp); + int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other); void mod_npc_killed_merit(Mob* c); void mod_npc_killed(Mob* oos); - void AISpellsList(Client *c); - + void AISpellsList(Client *c); + bool IsRaidTarget() const { return raid_target; }; protected: @@ -450,6 +458,8 @@ protected: uint32 npc_mana; float spellscale; float healscale; + int32 SpellFocusDMG; + int32 SpellFocusHeal; //pet crap: uint16 pet_spell_id; @@ -504,6 +514,7 @@ protected: std::list mercDataList; bool raid_target; + uint8 probability; private: uint32 loottable_id; diff --git a/zone/NpcAI.cpp b/zone/npc_ai.cpp similarity index 100% rename from zone/NpcAI.cpp rename to zone/npc_ai.cpp diff --git a/zone/NpcAI.h b/zone/npc_ai.h similarity index 100% rename from zone/NpcAI.h rename to zone/npc_ai.h diff --git a/zone/Object.cpp b/zone/object.cpp similarity index 83% rename from zone/Object.cpp rename to zone/object.cpp index 44459bab0..0201434c0 100644 --- a/zone/Object.cpp +++ b/zone/object.cpp @@ -24,12 +24,12 @@ #include "zonedb.h" #include "../common/packet_functions.h" #include "../common/packet_dump.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "../common/features.h" -#include "StringIDs.h" +#include "string_ids.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" const char DEFAULT_OBJECT_NAME[] = "IT63_ACTORDEF"; const char DEFAULT_OBJECT_NAME_SUFFIX[] = "_ACTORDEF"; @@ -318,6 +318,14 @@ void Object::Delete(bool reset_state) } } +const ItemInst* Object::GetItem(uint8 index) { + if (index < EmuConstants::MAP_WORLD_SIZE) { + return m_inst->GetItem(index); + } + + return nullptr; +} + // Add item to object (only logical for world tradeskill containers void Object::PutItem(uint8 index, const ItemInst* inst) { @@ -465,7 +473,7 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) char buf[10]; snprintf(buf, 9, "%u", m_inst->GetItem()->ID); buf[9] = '\0'; - std::vector args; + std::vector args; args.push_back(m_inst); parse->EventPlayer(EVENT_PLAYER_PICKUP, sender, buf, 0, &args); @@ -560,9 +568,6 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) // Add new Zone Object (theoretically only called for items dropped to ground) uint32 ZoneDatabase::AddObject(uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 database_id = 0; uint32 item_id = 0; int16 charges = 0; @@ -577,35 +582,30 @@ uint32 ZoneDatabase::AddObject(uint32 type, uint32 icon, const Object_Struct& ob char* object_name = new char[len]; DoEscapeString(object_name, object.object_name, strlen(object.object_name)); - // Construct query - uint32 len_query = MakeAnyLenString(&query, - "insert into object (zoneid, xpos, ypos, zpos, heading, itemid, charges, objectname, " - "type, icon) values (%i, %f, %f, %f, %f, %i, %i, '%s', %i, %i)", - object.zone_id, object.x, object.y, object.z, object.heading, - item_id, charges, object_name, type, icon); - - // Save new record for object - if (!RunQuery(query, len_query, errbuf, nullptr, nullptr, &database_id)) { - LogFile->write(EQEMuLog::Error, "Unable to insert object: %s", errbuf); - } - else { - // Save container contents, if container - if (inst && inst->IsType(ItemClassContainer)) { - SaveWorldContainer(object.zone_id, database_id, inst); - } + // Save new record for object + std::string query = StringFormat("INSERT INTO object " + "(zoneid, xpos, ypos, zpos, heading, " + "itemid, charges, objectname, type, icon) " + "values (%i, %f, %f, %f, %f, %i, %i, '%s', %i, %i)", + object.zone_id, object.x, object.y, object.z, object.heading, + item_id, charges, object_name, type, icon); + safe_delete_array(object_name); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Unable to insert object: %s", results.ErrorMessage().c_str()); + return 0; } - safe_delete_array(object_name); - safe_delete_array(query); + // Save container contents, if container + if (inst && inst->IsType(ItemClassContainer)) + SaveWorldContainer(object.zone_id, database_id, inst); + return database_id; } // Update information about existing object in database void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Object_Struct& object, const ItemInst* inst) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 item_id = 0; int16 charges = 0; @@ -619,77 +619,63 @@ void ZoneDatabase::UpdateObject(uint32 id, uint32 type, uint32 icon, const Objec char* object_name = new char[len]; DoEscapeString(object_name, object.object_name, strlen(object.object_name)); - // Construct query - uint32 len_query = MakeAnyLenString(&query, - "update object set zoneid=%i, xpos=%f, ypos=%f, zpos=%f, heading=%f, " - "itemid=%i, charges=%i, objectname='%s', type=%i, icon=%i where id=%i", - object.zone_id, object.x, object.y, object.z, object.heading, - item_id, charges, object_name, type, icon, id); - // Save new record for object - if (!RunQuery(query, len_query, errbuf)) { - LogFile->write(EQEMuLog::Error, "Unable to update object: %s", errbuf); - } - else { - // Save container contents, if container - if (inst && inst->IsType(ItemClassContainer)) { - SaveWorldContainer(object.zone_id, id, inst); - } + std::string query = StringFormat("UPDATE object SET " + "zoneid = %i, xpos = %f, ypos = %f, zpos = %f, heading = %f, " + "itemid = %i, charges = %i, objectname = '%s', type = %i, icon = %i " + "WHERE id = %i", + object.zone_id, object.x, object.y, object.z, object.heading, + item_id, charges, object_name, type, icon, id); + safe_delete_array(object_name); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Unable to update object: %s", results.ErrorMessage().c_str()); + return; } - safe_delete_array(object_name); - safe_delete_array(query); + // Save container contents, if container + if (inst && inst->IsType(ItemClassContainer)) + SaveWorldContainer(object.zone_id, id, inst); } -Ground_Spawns* ZoneDatabase::LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT max_x,max_y,max_z,min_x,min_y,heading,name,item,max_allowed,respawn_timer from ground_spawns where zoneid=%i and (version=%u OR version=-1) limit 50", zone_id, version), errbuf, &result)) - { - safe_delete_array(query); - int i=0; - while( (row=mysql_fetch_row(result) ) ) { - gs->spawn[i].max_x=atof(row[0]); - gs->spawn[i].max_y=atof(row[1]); - gs->spawn[i].max_z=atof(row[2]); - gs->spawn[i].min_x=atof(row[3]); - gs->spawn[i].min_y=atof(row[4]); - gs->spawn[i].heading=atof(row[5]); - strcpy(gs->spawn[i].name,row[6]); - gs->spawn[i].item=atoi(row[7]); - gs->spawn[i].max_allowed=atoi(row[8]); - gs->spawn[i].respawntimer=atoi(row[9]); - i++; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in LoadGroundSpawns query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); +Ground_Spawns* ZoneDatabase::LoadGroundSpawns(uint32 zone_id, int16 version, Ground_Spawns* gs) { + + std::string query = StringFormat("SELECT max_x, max_y, max_z, " + "min_x, min_y, heading, name, " + "item, max_allowed, respawn_timer " + "FROM ground_spawns " + "WHERE zoneid = %i AND (version = %u OR version = -1) " + "LIMIT 50", zone_id, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in LoadGroundSpawns query '" << query << "' " << results.ErrorMessage() << std::endl; + return gs; } + + int spawnIndex=0; + for (auto row = results.begin(); row != results.end(); ++row, ++spawnIndex) { + gs->spawn[spawnIndex].max_x=atof(row[0]); + gs->spawn[spawnIndex].max_y=atof(row[1]); + gs->spawn[spawnIndex].max_z=atof(row[2]); + gs->spawn[spawnIndex].min_x=atof(row[3]); + gs->spawn[spawnIndex].min_y=atof(row[4]); + gs->spawn[spawnIndex].heading=atof(row[5]); + strcpy(gs->spawn[spawnIndex].name,row[6]); + gs->spawn[spawnIndex].item=atoi(row[7]); + gs->spawn[spawnIndex].max_allowed=atoi(row[8]); + gs->spawn[spawnIndex].respawntimer=atoi(row[9]); + } return gs; } + void ZoneDatabase::DeleteObject(uint32 id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - // Construct query - uint32 len_query = MakeAnyLenString(&query, - "delete from object where id=%i", id); - - // Save new record for object - if (!RunQuery(query, len_query, errbuf)) { - LogFile->write(EQEMuLog::Error, "Unable to delete object: %s", errbuf); + // delete record of object + std::string query = StringFormat("DELETE FROM object WHERE id = %i", id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Unable to delete object: %s", results.ErrorMessage().c_str()); } - //else { - // Delete contained items, if any - // DeleteWorldContainer(id); - //} - - safe_delete_array(query); } uint32 Object::GetDBID() @@ -812,6 +798,42 @@ void Object::SetModelName(const char* modelname) safe_delete(app2); } +void Object::SetSize(uint16 size) +{ + m_data.unknown008 = size; + EQApplicationPacket* app = new EQApplicationPacket(); + EQApplicationPacket* app2 = new EQApplicationPacket(); + this->CreateDeSpawnPacket(app); + this->CreateSpawnPacket(app2); + entity_list.QueueClients(0, app); + entity_list.QueueClients(0, app2); + safe_delete(app); + safe_delete(app2); +} + +void Object::SetSolidType(uint16 solidtype) +{ + m_data.unknown010 = solidtype; + EQApplicationPacket* app = new EQApplicationPacket(); + EQApplicationPacket* app2 = new EQApplicationPacket(); + this->CreateDeSpawnPacket(app); + this->CreateSpawnPacket(app2); + entity_list.QueueClients(0, app); + entity_list.QueueClients(0, app2); + safe_delete(app); + safe_delete(app2); +} + +uint16 Object::GetSize() +{ + return m_data.unknown008; +} + +uint16 Object::GetSolidType() +{ + return m_data.unknown010; +} + const char* Object::GetModelName() { return this->m_data.object_name; diff --git a/zone/object.h b/zone/object.h index 1f0955c1f..9f5cb8cb3 100644 --- a/zone/object.h +++ b/zone/object.h @@ -25,7 +25,7 @@ #include "../common/linked_list.h" #include "../common/emu_opcodes.h" #include "../common/eq_packet_structs.h" -#include "../common/Item.h" +#include "../common/item.h" #include "client.h" #include "mob.h" #include "npc.h" @@ -121,6 +121,7 @@ public: void StartDecay() {decay_timer.Start();} // Container functions + const ItemInst* GetItem(uint8 index); void PutItem(uint8 index, const ItemInst* inst); void DeleteItem(uint8 index); // Item inside container ItemInst* PopItem(uint8 index); // Pop item out of container @@ -156,6 +157,10 @@ public: void SetZ(float pos); void SetModelName(const char* modelname); const char* GetModelName(); + uint16 GetSize(); + void SetSize(uint16 size); + uint16 GetSolidType(); + void SetSolidType(uint16 size); const char* GetEntityVariable(const char *id); void SetEntityVariable(const char *id, const char *m_var); diff --git a/zone/oldcode.cpp b/zone/oldcode.cpp index 031e2492f..30adb7f2f 100644 --- a/zone/oldcode.cpp +++ b/zone/oldcode.cpp @@ -67,12 +67,12 @@ extern volatile bool ZoneLoaded; #include "../common/queue.h" #include "../common/timer.h" -#include "../common/EQStream.h" +#include "../common/eq_stream.h" #include "../common/eq_packet_structs.h" -#include "../common/Mutex.h" +#include "../common/mutex.h" #include "../common/version.h" #include "../common/files.h" -#include "../common/EQEMuError.h" +#include "../common/eqemu_error.h" #include "../common/packet_dump_file.h" #include "masterentity.h" diff --git a/zone/pathing.cpp b/zone/pathing.cpp index 54e1f06a6..c860913fe 100644 --- a/zone/pathing.cpp +++ b/zone/pathing.cpp @@ -7,7 +7,7 @@ #include #include "pathing.h" #include "water_map.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "doors.h" #include "client.h" #include "zone.h" diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 816956d86..2a0c055af 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1023,11 +1023,12 @@ XS(XS_Client_SetBindPoint); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_SetBindPoint) { dXSARGS; - if (items < 1 || items > 5) - Perl_croak(aTHX_ "Usage: Client::SetBindPoint(THIS, to_zone= -1, new_x= 0.0f, new_y= 0.0f, new_z= 0.0f)"); + if (items < 1 || items > 6) + Perl_croak(aTHX_ "Usage: Client::SetBindPoint(THIS, to_zone= -1, to_instance = 0, new_x= 0.0f, new_y= 0.0f, new_z= 0.0f)"); { Client * THIS; int to_zone; + int to_instance; float new_x; float new_y; float new_z; @@ -1047,25 +1048,31 @@ XS(XS_Client_SetBindPoint) to_zone = (int)SvIV(ST(1)); } - if (items < 3) - new_x = 0.0f; + if(items < 3) + to_instance = 0; else { - new_x = (float)SvNV(ST(2)); + to_instance = (int)SvIV(ST(2)); } if (items < 4) - new_y = 0.0f; + new_x = 0.0f; else { - new_y = (float)SvNV(ST(3)); + new_x = (float)SvNV(ST(3)); } if (items < 5) - new_z = 0.0f; + new_y = 0.0f; else { - new_z = (float)SvNV(ST(4)); + new_y = (float)SvNV(ST(4)); } - THIS->SetBindPoint(to_zone, new_x, new_y, new_z); + if (items < 6) + new_z = 0.0f; + else { + new_z = (float)SvNV(ST(5)); + } + + THIS->SetBindPoint(to_zone, to_instance, new_x, new_y, new_z); } XSRETURN_EMPTY; } @@ -1259,7 +1266,24 @@ XS(XS_Client_MovePC) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - THIS->MovePC(zoneID, x, y, z, heading); + if (THIS->IsClient()) { + THIS->MovePC(zoneID, x, y, z, heading); + } + else { + if (THIS->IsMerc()) + _log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process a type Merc reference"); + else if (THIS->IsNPC()) + _log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process a type NPC reference"); + #ifdef BOTS + else if (THIS->IsBot()) + _log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process a type Bot reference"); + #endif + else + _log(CLIENT__ERROR, "Perl(XS_Client_MovePC) attempted to process an Unknown type reference"); + + Perl_croak(aTHX_ "THIS is not of type Client"); + } + } XSRETURN_EMPTY; } @@ -1288,7 +1312,25 @@ XS(XS_Client_MovePCInstance) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - THIS->MovePC(zoneID, instanceID, x, y, z, heading); + if (THIS->IsClient()) { + THIS->MovePC(zoneID, instanceID, x, y, z, heading); + } + else { + if (THIS->IsMerc()) + _log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process a type Merc reference"); + else if (THIS->IsNPC()) + _log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process a type NPC reference"); + #ifdef BOTS + else if (THIS->IsBot()) + _log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process a type Bot reference"); + #endif + else + _log(CLIENT__ERROR, "Perl(XS_Client_MovePCInstance) attempted to process an Unknown type reference"); + + Perl_croak(aTHX_ "THIS is not of type Client"); + + Perl_croak(aTHX_ "THIS is not of type Client"); + } } XSRETURN_EMPTY; } @@ -5037,6 +5079,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) { @@ -5273,6 +5344,30 @@ XS(XS_Client_AssignToInstance) XSRETURN_EMPTY; } +XS(XS_Client_RemoveFromInstance); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Client_RemoveFromInstance) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Client::RemoveFromInstance(THIS, instance_id)"); + { + Client * THIS; + uint16 instance_id = (uint16)SvUV(ST(1)); + + 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."); + + THIS->RemoveFromInstance(instance_id); + } + XSRETURN_EMPTY; +} + XS(XS_Client_Freeze); XS(XS_Client_Freeze) { @@ -5956,6 +6051,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 @@ -6015,7 +6162,7 @@ XS(boot_Client) newXSproto(strcpy(buf, "SetDeity"), XS_Client_SetDeity, file, "$$"); newXSproto(strcpy(buf, "AddEXP"), XS_Client_AddEXP, file, "$$;$$"); newXSproto(strcpy(buf, "SetEXP"), XS_Client_SetEXP, file, "$$$;$"); - newXSproto(strcpy(buf, "SetBindPoint"), XS_Client_SetBindPoint, file, "$;$$$$"); + newXSproto(strcpy(buf, "SetBindPoint"), XS_Client_SetBindPoint, file, "$;$$$$$"); newXSproto(strcpy(buf, "GetBindX"), XS_Client_GetBindX, file, "$$"); newXSproto(strcpy(buf, "GetBindY"), XS_Client_GetBindY, file, "$$"); newXSproto(strcpy(buf, "GetBindZ"), XS_Client_GetBindZ, file, "$$"); @@ -6164,6 +6311,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, "$$$"); @@ -6194,6 +6342,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 14d84a70e..c47b463d3 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -1517,14 +1517,15 @@ XS(XS_Mob_MakeTempPet); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_MakeTempPet) { dXSARGS; - if (items < 2 || items > 5) - Perl_croak(aTHX_ "Usage: Mob::MakeTempPet(THIS, spell_id, name=nullptr, duration=0, target=nullptr)"); + if (items < 2 || items > 6) + Perl_croak(aTHX_ "Usage: Mob::MakeTempPet(THIS, spell_id, name=nullptr, duration=0, target=nullptr, sticktarg=0)"); { Mob * THIS; uint16 spell_id = (uint16)SvUV(ST(1)); char * name; uint32 duration; Mob * target; + bool sticktarg; if (sv_derived_from(ST(0), "Mob")) { IV tmp = SvIV((SV*)SvRV(ST(0))); @@ -1554,7 +1555,13 @@ XS(XS_Mob_MakeTempPet) else Perl_croak(aTHX_ "owner is not of type Mob"); - THIS->TemporaryPets(spell_id, target, name, duration); + if (items < 6) + sticktarg = false; + else { + sticktarg = (bool)SvTRUE(ST(5)); + } + + THIS->TemporaryPets(spell_id, target, name, duration, true, sticktarg); } XSRETURN_EMPTY; } @@ -1563,15 +1570,16 @@ XS(XS_Mob_TypesTempPet); /* prototype to pass -Wmissing-prototypes */ XS(XS_Mob_TypesTempPet) { dXSARGS; - if (items < 2 || items > 6) - Perl_croak(aTHX_ "Usage: Mob::TypesTempPet(THIS, typesid, name=nullptr, duration=0, target=nullptr, follow=0)"); + if (items < 2 || items > 7) + Perl_croak(aTHX_ "Usage: Mob::TypesTempPet(THIS, typesid, name=nullptr, duration=0, follow=0, target=nullptr, sticktarg=0,)"); { Mob * THIS; uint32 typesid = (uint32)SvUV(ST(1)); char * name; uint32 duration; - Mob * target; bool follow; + Mob * target; + bool sticktarg; if (sv_derived_from(ST(0), "Mob")) { IV tmp = SvIV((SV*)SvRV(ST(0))); @@ -1593,21 +1601,28 @@ XS(XS_Mob_TypesTempPet) duration = (uint32)SvUV(ST(3)); if (items < 5) + follow = true; + else { + follow = (bool)SvTRUE(ST(4)); + } + + if (items < 6) target = nullptr; - else if (sv_derived_from(ST(4), "Mob")) { - IV tmp = SvIV((SV*)SvRV(ST(4))); + else if (sv_derived_from(ST(5), "Mob")) { + IV tmp = SvIV((SV*)SvRV(ST(5))); target = INT2PTR(Mob *,tmp); } else Perl_croak(aTHX_ "target is not of type Mob"); - if (items < 6) - follow = false; + + if (items < 7) + sticktarg = false; else { - follow = (bool)SvTRUE(ST(5)); + sticktarg = (bool)SvTRUE(ST(6)); } - THIS->TypesTemporaryPets(typesid, target, name, duration, follow); + THIS->TypesTemporaryPets(typesid, target, name, duration, follow, sticktarg); } XSRETURN_EMPTY; } @@ -2536,7 +2551,7 @@ XS(XS_Mob_GetAC) Perl_croak(aTHX_ "Usage: Mob::GetAC(THIS)"); { Mob * THIS; - uint16 RETVAL; + uint32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2562,7 +2577,7 @@ XS(XS_Mob_GetATK) Perl_croak(aTHX_ "Usage: Mob::GetATK(THIS)"); { Mob * THIS; - uint16 RETVAL; + uint32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2588,7 +2603,7 @@ XS(XS_Mob_GetSTR) Perl_croak(aTHX_ "Usage: Mob::GetSTR(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2614,7 +2629,7 @@ XS(XS_Mob_GetSTA) Perl_croak(aTHX_ "Usage: Mob::GetSTA(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2640,7 +2655,7 @@ XS(XS_Mob_GetDEX) Perl_croak(aTHX_ "Usage: Mob::GetDEX(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2666,7 +2681,7 @@ XS(XS_Mob_GetAGI) Perl_croak(aTHX_ "Usage: Mob::GetAGI(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2692,7 +2707,7 @@ XS(XS_Mob_GetINT) Perl_croak(aTHX_ "Usage: Mob::GetINT(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2718,7 +2733,7 @@ XS(XS_Mob_GetWIS) Perl_croak(aTHX_ "Usage: Mob::GetWIS(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2744,7 +2759,7 @@ XS(XS_Mob_GetCHA) Perl_croak(aTHX_ "Usage: Mob::GetCHA(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2770,7 +2785,7 @@ XS(XS_Mob_GetMR) Perl_croak(aTHX_ "Usage: Mob::GetMR(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2796,7 +2811,7 @@ XS(XS_Mob_GetFR) Perl_croak(aTHX_ "Usage: Mob::GetFR(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2822,7 +2837,7 @@ XS(XS_Mob_GetDR) Perl_croak(aTHX_ "Usage: Mob::GetDR(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2848,7 +2863,7 @@ XS(XS_Mob_GetPR) Perl_croak(aTHX_ "Usage: Mob::GetPR(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2874,7 +2889,7 @@ XS(XS_Mob_GetCR) Perl_croak(aTHX_ "Usage: Mob::GetCR(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2900,7 +2915,7 @@ XS(XS_Mob_GetCorruption) Perl_croak(aTHX_ "Usage: Mob::GetCorruption(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2926,7 +2941,7 @@ XS(XS_Mob_GetMaxSTR) Perl_croak(aTHX_ "Usage: Mob::GetMaxSTR(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2952,7 +2967,7 @@ XS(XS_Mob_GetMaxSTA) Perl_croak(aTHX_ "Usage: Mob::GetMaxSTA(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -2978,7 +2993,7 @@ XS(XS_Mob_GetMaxDEX) Perl_croak(aTHX_ "Usage: Mob::GetMaxDEX(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -3004,7 +3019,7 @@ XS(XS_Mob_GetMaxAGI) Perl_croak(aTHX_ "Usage: Mob::GetMaxAGI(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -3030,7 +3045,7 @@ XS(XS_Mob_GetMaxINT) Perl_croak(aTHX_ "Usage: Mob::GetMaxINT(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -3056,7 +3071,7 @@ XS(XS_Mob_GetMaxWIS) Perl_croak(aTHX_ "Usage: Mob::GetMaxWIS(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -3082,7 +3097,7 @@ XS(XS_Mob_GetMaxCHA) Perl_croak(aTHX_ "Usage: Mob::GetMaxCHA(THIS)"); { Mob * THIS; - int16 RETVAL; + int32 RETVAL; dXSTARG; if (sv_derived_from(ST(0), "Mob")) { @@ -3956,7 +3971,10 @@ XS(XS_Mob_CastSpell) resist_adjust = (int16)SvIV(ST(6)); } - THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0, &resist_adjust); + if (resist_adjust == 0)//If you do not pass resist adjust as nullptr it will ignore the spells default resist adjust + THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0); + else + THIS->CastSpell(spell_id, target_id, slot, casttime, mana_cost, 0, 0xFFFFFFFF, 0xFFFFFFFF, 0, 0, &resist_adjust); } XSRETURN_EMPTY; } @@ -6259,7 +6277,7 @@ XS(XS_Mob_CheckAggroAmount) Perl_croak(aTHX_ "Usage: Mob::CheckAggroAmount(THIS, spellid)"); { Mob * THIS; - uint16 RETVAL; + uint32 RETVAL; dXSTARG; uint16 spellid = (uint16)SvUV(ST(1)); @@ -6286,7 +6304,7 @@ XS(XS_Mob_CheckHealAggroAmount) Perl_croak(aTHX_ "Usage: Mob::CheckHealAggroAmount(THIS, spellid, possible_heal_amt)"); { Mob * THIS; - uint16 RETVAL; + uint32 RETVAL; dXSTARG; uint16 spellid = (uint16)SvUV(ST(1)); uint32 possible = 0; @@ -7140,7 +7158,7 @@ XS(XS_Mob_SendIllusion) uint32 drakkin_heritage = 0xFFFFFFFF; uint32 drakkin_tattoo = 0xFFFFFFFF; uint32 drakkin_details = 0xFFFFFFFF; - float size = 0xFFFFFFFF; + float size = -1.0f; if (sv_derived_from(ST(0), "Mob")) { IV tmp = SvIV((SV*)SvRV(ST(0))); @@ -8137,6 +8155,191 @@ 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); +} + +XS(XS_Mob_GetSpecialAbility); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSpecialAbility) +{ + dXSARGS; + if(items != 2) + Perl_croak(aTHX_ "Usage: Mob::GetSpecialAbility(THIS, special_ability)"); + { + int RETVAL; + Mob* THIS; + int ability = SvIV(ST(1)); + 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."); + + RETVAL = THIS->GetSpecialAbility(ability); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_GetSpecialAbilityParam); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_GetSpecialAbilityParam) +{ + dXSARGS; + if(items != 3) + Perl_croak(aTHX_ "Usage: Mob::GetSpecialAbilityParam(THIS, special_ability, param)"); + { + int RETVAL; + Mob* THIS; + int ability = SvIV(ST(1)); + int param = SvIV(ST(2)); + 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."); + + RETVAL = THIS->GetSpecialAbilityParam(ability, param); + XSprePUSH; PUSHi((IV)RETVAL); + } + XSRETURN(1); +} + +XS(XS_Mob_SetSpecialAbility); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetSpecialAbility) +{ + dXSARGS; + if(items != 3) + Perl_croak(aTHX_ "Usage: Mob::SetSpecialAbility(THIS, ability, value)"); + { + Mob* THIS; + int ability = SvIV(ST(1)); + int value = SvIV(ST(2)); + + 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."); + + THIS->SetSpecialAbility(ability, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_SetSpecialAbilityParam); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_SetSpecialAbilityParam) +{ + dXSARGS; + if(items != 4) + Perl_croak(aTHX_ "Usage: Mob::SetSpecialAbilityParam(THIS, ability, param, value)"); + { + Mob* THIS; + int ability = SvIV(ST(1)); + int param = SvIV(ST(2)); + int value = SvIV(ST(3)); + + 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."); + + THIS->SetSpecialAbilityParam(ability, param, value); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_ClearSpecialAbilities); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ClearSpecialAbilities) +{ + dXSARGS; + if(items != 1) + Perl_croak(aTHX_ "Usage: Mob::ClearSpecialAbilities(THIS)"); + { + Mob* THIS; + + 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."); + + THIS->ClearSpecialAbilities(); + } + XSRETURN_EMPTY; +} + +XS(XS_Mob_ProcessSpecialAbilities); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Mob_ProcessSpecialAbilities) +{ + dXSARGS; + if(items != 2) + Perl_croak(aTHX_ "Usage: Mob::ProcessSpecialAbilities(THIS, str)"); + { + Mob* THIS; + const char *str = (const char*)SvPV_nolen(ST(1)); + + 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."); + + THIS->ProcessSpecialAbilities(str); + } + XSRETURN_EMPTY; +} + #ifdef __cplusplus extern "C" #endif @@ -8400,7 +8603,8 @@ XS(boot_Mob) newXSproto(strcpy(buf, "SetRace"), XS_Mob_SetRace, file, "$$"); newXSproto(strcpy(buf, "SetGender"), XS_Mob_SetGender, file, "$$"); newXSproto(strcpy(buf, "SendIllusion"), XS_Mob_SendIllusion, file, "$$;$$$$$$$$$$$$"); - newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$"); + newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$"); + newXSproto(strcpy(buf, "TypesTempPet"), XS_Mob_TypesTempPet, file, "$$;$$$$$"); newXSproto(strcpy(buf, "QuestReward"), XS_Mob_QuestReward, file, "$$;$$$"); newXSproto(strcpy(buf, "CameraEffect"), XS_Mob_CameraEffect, file, "$$;$$$"); newXSproto(strcpy(buf, "SpellEffect"), XS_Mob_SpellEffect, file, "$$;$$$$$$"); @@ -8433,10 +8637,16 @@ XS(boot_Mob) newXSproto(strcpy(buf, "DoArcheryAttackDmg"), XS_Mob_DoArcheryAttackDmg, file, "$$$$$$$"); newXSproto(strcpy(buf, "DoThrowingAttackDmg"), XS_Mob_DoThrowingAttackDmg, file, "$$$$$$$"); newXSproto(strcpy(buf, "SetDisableMelee"), XS_Mob_SetDisableMelee, file, "$$"); - newXSproto(strcpy(buf, "IsMeleeDisabled"), XS_Mob_IsMeleeDisabled, file, "$$"); + 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, "$$$$"); + newXSproto(strcpy(buf, "GetSpecialAbility"), XS_Mob_GetSpecialAbility, file, "$$"); + newXSproto(strcpy(buf, "GetSpecialAbilityParam"), XS_Mob_GetSpecialAbilityParam, file, "$$$"); + newXSproto(strcpy(buf, "SetSpecialAbility"), XS_Mob_SetSpecialAbility, file, "$$$"); + newXSproto(strcpy(buf, "SetSpecialAbilityParam"), XS_Mob_SetSpecialAbilityParam, file, "$$$$"); + newXSproto(strcpy(buf, "ClearSpecialAbilities"), XS_Mob_ClearSpecialAbilities, file, "$"); + newXSproto(strcpy(buf, "ProcessSpecialAbilities"), XS_Mob_ProcessSpecialAbilities, file, "$$"); XSRETURN_YES; } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 891709113..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) { @@ -2145,6 +2197,54 @@ XS(XS_NPC_GetScore) XSRETURN(1); } +XS(XS_NPC_SetMerchantProbability); +XS(XS_NPC_SetMerchantProbability) { + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: NPC::SetMerchantProbability(THIS, Probability)"); + { + NPC *THIS; + uint8 Probability = (uint8)SvIV(ST(1)); + + 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."); + + THIS->SetMerchantProbability(Probability); + } + XSRETURN_EMPTY; +} + +XS(XS_NPC_GetMerchantProbability); +XS(XS_NPC_GetMerchantProbability) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: NPC::GetMerchantProbability(THIS)"); + { + NPC *THIS; + uint8 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 == NULL) + Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); + + RETVAL = THIS->GetMerchantProbability(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + #ifdef __cplusplus extern "C" #endif @@ -2238,11 +2338,15 @@ 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, "$"); + newXSproto(strcpy(buf, "SetMerchantProbability"), XS_NPC_SetMerchantProbability, file, "$$"); + newXSproto(strcpy(buf, "GetMerchantProbability"), XS_NPC_GetMerchantProbability, file, "$"); XSRETURN_YES; } diff --git a/zone/perl_object.cpp b/zone/perl_object.cpp index 9c71ec6ed..036d555a1 100644 --- a/zone/perl_object.cpp +++ b/zone/perl_object.cpp @@ -909,6 +909,107 @@ XS(XS_Object_SetEntityVariable) XSRETURN_EMPTY; } +XS(XS_Object_GetSolidType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetSolidType) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetSolidType(THIS)"); + { + Object * THIS; + uint16 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Object")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Object *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Object"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetSolidType(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Object_SetSolidType); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetSolidType) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetSolidType(THIS, type)"); + { + Object * THIS; + uint16 type = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Object")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Object *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Object"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + THIS->SetSolidType(type); + } + XSRETURN_EMPTY; +} + +XS(XS_Object_GetSize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_GetSize) +{ + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: Object::GetSize(THIS)"); + { + Object * THIS; + uint16 RETVAL; + dXSTARG; + + if (sv_derived_from(ST(0), "Object")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Object *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Object"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + RETVAL = THIS->GetSize(); + XSprePUSH; PUSHu((UV)RETVAL); + } + XSRETURN(1); +} + + +XS(XS_Object_SetSize); /* prototype to pass -Wmissing-prototypes */ +XS(XS_Object_SetSize) +{ + dXSARGS; + if (items != 2) + Perl_croak(aTHX_ "Usage: Object::SetSize(THIS, type)"); + { + Object * THIS; + uint16 size = (uint16)SvUV(ST(1)); + + if (sv_derived_from(ST(0), "Object")) { + IV tmp = SvIV((SV*)SvRV(ST(0))); + THIS = INT2PTR(Object *,tmp); + } + else + Perl_croak(aTHX_ "THIS is not of type Object"); + if(THIS == nullptr) + Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); + + THIS->SetSize(size); + } + XSRETURN_EMPTY; +} #ifdef __cplusplus extern "C" @@ -961,6 +1062,10 @@ XS(boot_Object) newXSproto(strcpy(buf, "GetEntityVariable"), XS_Object_GetEntityVariable, file, "$$"); newXSproto(strcpy(buf, "SetEntityVariable"), XS_Object_SetEntityVariable, file, "$$$"); newXSproto(strcpy(buf, "EntityVariableExists"), XS_Object_EntityVariableExists, file, "$$"); + newXSproto(strcpy(buf, "SetSolidType"),XS_Object_SetSolidType, file, "$$"); + newXSproto(strcpy(buf, "GetSolidType"),XS_Object_GetSolidType, file, "$"); + newXSproto(strcpy(buf, "SetSize"),XS_Object_SetSize, file, "$$"); + newXSproto(strcpy(buf, "GetSize"),XS_Object_GetSize, file, "$"); XSRETURN_YES; } #endif //EMBPERL_XS_CLASSES diff --git a/zone/perl_PlayerCorpse.cpp b/zone/perl_player_corpse.cpp similarity index 99% rename from zone/perl_PlayerCorpse.cpp rename to zone/perl_player_corpse.cpp index 366299ea2..675ee9697 100644 --- a/zone/perl_PlayerCorpse.cpp +++ b/zone/perl_player_corpse.cpp @@ -208,7 +208,7 @@ XS(XS_Corpse_GetDBID) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - RETVAL = THIS->GetDBID(); + RETVAL = THIS->GetCorpseDBID(); XSprePUSH; PUSHu((UV)RETVAL); } XSRETURN(1); @@ -662,7 +662,7 @@ XS(XS_Corpse_CompleteRezz) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - THIS->CompleteRezz(); + THIS->CompleteResurrection(); } XSRETURN_EMPTY; } @@ -687,7 +687,7 @@ XS(XS_Corpse_CanMobLoot) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - RETVAL = THIS->CanMobLoot(charid); + RETVAL = THIS->CanPlayerLoot(charid); ST(0) = boolSV(RETVAL); sv_2mortal(ST(0)); } @@ -723,7 +723,7 @@ XS(XS_Corpse_AllowMobLoot) if(them == nullptr) Perl_croak(aTHX_ "them is nullptr, avoiding crash."); - THIS->AllowMobLoot(them, slot); + THIS->AllowPlayerLoot(them, slot); } XSRETURN_EMPTY; } @@ -780,7 +780,7 @@ XS(XS_Corpse_IsRezzed) if(THIS == nullptr) Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - RETVAL = THIS->Rezzed(); + RETVAL = THIS->IsRezzed(); ST(0) = boolSV(RETVAL); sv_2mortal(ST(0)); } diff --git a/zone/perl_questitem.cpp b/zone/perl_questitem.cpp index f9128718b..7738b12fb 100644 --- a/zone/perl_questitem.cpp +++ b/zone/perl_questitem.cpp @@ -26,7 +26,7 @@ #undef seed #endif -#include "../common/Item.h" +#include "../common/item.h" #ifdef THIS /* this macro seems to leak out on some systems */ #undef THIS diff --git a/zone/perlpacket.cpp b/zone/perlpacket.cpp index 262c2a278..1475ff448 100644 --- a/zone/perlpacket.cpp +++ b/zone/perlpacket.cpp @@ -22,7 +22,7 @@ #include "entity.h" #include "../common/opcodemgr.h" #include "../common/packet_dump.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" PerlPacket::PerlPacket(const char *opcode, uint32 length) { SetOpcode(opcode); diff --git a/zone/petitions.cpp b/zone/petitions.cpp index 51910a052..ad38bc04d 100644 --- a/zone/petitions.cpp +++ b/zone/petitions.cpp @@ -16,10 +16,6 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include -#include -#include -#include #include #ifdef _WINDOWS #include @@ -32,16 +28,12 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #define strncasecmp _strnicmp #define strcasecmp _stricmp #endif -#include "../common/StringUtil.h" -#include "../common/packet_functions.h" -#include "../common/packet_dump.h" -#include "../common/packet_dump_file.h" -#include "../common/emu_opcodes.h" + + #include "../common/eq_packet_structs.h" #include "../common/servertalk.h" +#include "../common/string_util.h" #include "entity.h" -#include "masterentity.h" - #include "petitions.h" #include "worldserver.h" @@ -49,7 +41,6 @@ PetitionList petition_list; extern WorldServer worldserver; - void Petition::SendPetitionToPlayer(Client* clientto) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_PetitionCheckout,sizeof(Petition_Struct)); Petition_Struct* pet = (Petition_Struct*) outapp->pBuffer; @@ -216,100 +207,95 @@ void PetitionList::UpdatePetition(Petition* pet) { } void ZoneDatabase::DeletePetitionFromDB(Petition* wpet) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - uint8 checkedout = 0; - if (wpet->CheckedOut()) checkedout = 0; - else checkedout = 1; - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE from petitions where petid = %i", wpet->GetID()), errbuf, 0, &affected_rows)) { - LogFile->write(EQEMuLog::Error, "Error in DeletePetitionFromDB query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return; + std::string query = StringFormat("DELETE FROM petitions WHERE petid = %i", wpet->GetID()); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in DeletePetitionFromDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + } void ZoneDatabase::UpdatePetitionToDB(Petition* wpet) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - uint8 checkedout = 0; - if (wpet->CheckedOut()) checkedout = 1; - else checkedout = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE petitions set gmtext = '%s', lastgm = '%s', urgency = %i, checkouts = %i, unavailables = %i, ischeckedout = %i where petid = %i", wpet->GetGMText(), wpet->GetLastGM(), wpet->GetUrgency(), wpet->GetCheckouts(), wpet->GetUnavails(), checkedout, wpet->GetID()), errbuf, 0, &affected_rows)) { - LogFile->write(EQEMuLog::Error, "Error in UpdatePetitionToDB query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return; + + std::string query = StringFormat("UPDATE petitions SET gmtext = '%s', lastgm = '%s', urgency = %i, " + "checkouts = %i, unavailables = %i, ischeckedout = %i " + "WHERE petid = %i", + wpet->GetGMText(), wpet->GetLastGM(), wpet->GetUrgency(), + wpet->GetCheckouts(), wpet->GetUnavails(), + wpet->CheckedOut() ? 1: 0, wpet->GetID()); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdatePetitionToDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + } - - void ZoneDatabase::InsertPetitionToDB(Petition* wpet) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - uint8 checkedout = 0; - if (wpet->CheckedOut()) - checkedout = 1; - else - checkedout = 0; uint32 len = strlen(wpet->GetPetitionText()); char* petitiontext = new char[2*len+1]; memset(petitiontext, 0, 2*len+1); DoEscapeString(petitiontext, wpet->GetPetitionText(), len); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO petitions (petid, charname, accountname, lastgm, petitiontext, zone, urgency, charclass, charrace, charlevel, checkouts, unavailables, ischeckedout, senttime, gmtext) values (%i,'%s','%s','%s','%s',%i,%i,%i,%i,%i,%i,%i,%i,%i, '%s')", wpet->GetID(), wpet->GetCharName(), wpet->GetAccountName(), wpet->GetLastGM(), petitiontext, wpet->GetZone(), wpet->GetUrgency(), wpet->GetCharClass(), wpet->GetCharRace(), wpet->GetCharLevel(), wpet->GetCheckouts(), wpet->GetUnavails(), checkedout, wpet->GetSentTime(), wpet->GetGMText()), errbuf, 0, &affected_rows)) { - LogFile->write(EQEMuLog::Error, "Error in InsertPetitionToDB query '%s': %s", query, errbuf); + + std::string query = StringFormat("INSERT INTO petitions " + "(petid, charname, accountname, lastgm, " + "petitiontext, zone, urgency, charclass, " + "charrace, charlevel, checkouts, unavailables, " + "ischeckedout, senttime, gmtext) " + "VALUES (%i, '%s', '%s', '%s', '%s', " + "%i, %i, %i, %i, %i, " + "%i, %i, %i, %i, '%s')", + wpet->GetID(), wpet->GetCharName(), wpet->GetAccountName(), wpet->GetLastGM(), + petitiontext, wpet->GetZone(), wpet->GetUrgency(), wpet->GetCharClass(), + wpet->GetCharRace(), wpet->GetCharLevel(), wpet->GetCheckouts(), wpet->GetUnavails(), + wpet->CheckedOut()? 1: 0, wpet->GetSentTime(), wpet->GetGMText()); + safe_delete_array(petitiontext); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in InsertPetitionToDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return; } - safe_delete_array(petitiontext); - safe_delete_array(query); #if EQDEBUG >= 5 LogFile->write(EQEMuLog::Debug, "New petition created"); #endif - return; + } void ZoneDatabase::RefreshPetitionsFromDB() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; Petition* newpet; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT petid, charname, accountname, lastgm, petitiontext, zone, urgency, charclass, charrace, charlevel, checkouts, unavailables, ischeckedout, senttime, gmtext from petitions order by petid"), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - newpet = new Petition(atoi(row[0])); - newpet->SetCName(row[1]); - newpet->SetAName(row[2]); - newpet->SetLastGM(row[3]); - newpet->SetPetitionText(row[4]); - newpet->SetZone(atoi(row[5])); - newpet->SetUrgency(atoi(row[6])); - newpet->SetClass(atoi(row[7])); - newpet->SetRace(atoi(row[8])); - newpet->SetLevel(atoi(row[9])); - newpet->SetCheckouts(atoi(row[10])); - newpet->SetUnavails(atoi(row[11])); - newpet->SetSentTime2(atol(row[13])); - newpet->SetGMText(row[14]); - std::cout << "Petition " << row[0] << " pettime = " << newpet->GetSentTime() << std::endl; - if (atoi(row[12]) == 1) newpet->SetCheckedOut(true); - else newpet->SetCheckedOut(false); - petition_list.AddPetition(newpet); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in RefreshPetitionsFromDB query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = "SELECT petid, charname, accountname, lastgm, petitiontext, " + "zone, urgency, charclass, charrace, charlevel, checkouts, " + "unavailables, ischeckedout, senttime, gmtext " + "FROM petitions ORDER BY petid"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in RefreshPetitionsFromDB query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - return; + for (auto row = results.begin(); row != results.end(); ++row) { + newpet = new Petition(atoi(row[0])); + newpet->SetCName(row[1]); + newpet->SetAName(row[2]); + newpet->SetLastGM(row[3]); + newpet->SetPetitionText(row[4]); + newpet->SetZone(atoi(row[5])); + newpet->SetUrgency(atoi(row[6])); + newpet->SetClass(atoi(row[7])); + newpet->SetRace(atoi(row[8])); + newpet->SetLevel(atoi(row[9])); + newpet->SetCheckouts(atoi(row[10])); + newpet->SetUnavails(atoi(row[11])); + newpet->SetSentTime2(atol(row[13])); + newpet->SetGMText(row[14]); + + if (atoi(row[12]) == 1) + newpet->SetCheckedOut(true); + else + newpet->SetCheckedOut(false); + petition_list.AddPetition(newpet); + } + } diff --git a/zone/petitions.h b/zone/petitions.h index 76db9b006..604d4ddcc 100644 --- a/zone/petitions.h +++ b/zone/petitions.h @@ -19,11 +19,13 @@ #define PETITIONS_H #include "../common/linked_list.h" +#include "../common/misc_functions.h" +#include "../common/mutex.h" #include "../common/types.h" -#include "zonedb.h" #include "client.h" -#include "../common/Mutex.h" -#include "../common/MiscFunctions.h" +#include "zonedb.h" + +class Client; class Petition { diff --git a/zone/pets.cpp b/zone/pets.cpp index c7b4b1eac..cf9ecab8c 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -15,30 +15,26 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "../common/debug.h" +#include "../common/misc_functions.h" #include "../common/spdat.h" -#include "masterentity.h" -#include "../common/packet_dump.h" -#include "../common/moremath.h" -#include "../common/Item.h" -#include "zonedb.h" -#include "worldserver.h" -#include "../common/skills.h" -#include "../common/bodytypes.h" -#include "../common/classes.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" +#include "../common/types.h" + +#include "entity.h" +#include "client.h" +#include "mob.h" + #include "pets.h" -#include -#include +#include "worldserver.h" +#include "zonedb.h" + #ifndef WIN32 #include #include "../common/unix.h" #endif -#include "StringIDs.h" - -/////////////////////////////////////////////////////////////////////////////// -// pet related functions const char *GetRandPetName() { @@ -362,31 +358,34 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, // handle monster summoning pet appearance if(record.monsterflag) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result = nullptr; - MYSQL_ROW row = nullptr; - uint32 monsterid; + + uint32 monsterid = 0; // get a random npc id from the spawngroups assigned to this zone - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT npcID FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) " - "INNER JOIN npc_types ON npc_types.id = spawnentry.npcID " - "WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) " - "AND npc_types.race NOT IN (0,1,2,3,4,5,6,7,8,9,10,11,12,44,55,67,71,72,73,77,78,81,90,92,93,94,106,112,114,127,128,130,139,141,183,236,237,238,239,254,266,329,330,378,379,380,381,382,383,404,522) " - "ORDER BY RAND() LIMIT 1", zone->GetShortName()), errbuf, &result)) - { - row = mysql_fetch_row(result); - if (row) - monsterid = atoi(row[0]); - else - monsterid = 567; // since we don't have any monsters, just make it look like an earth pet for now - } - else { // if the database query failed - LogFile->write(EQEMuLog::Error, "Error querying database for monster summoning pet in zone %s (%s)", zone->GetShortName(), errbuf); - monsterid = 567; + auto query = StringFormat("SELECT npcID " + "FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) " + "INNER JOIN npc_types ON npc_types.id = spawnentry.npcID " + "WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) " + "AND npc_types.race NOT IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 44, " + "55, 67, 71, 72, 73, 77, 78, 81, 90, 92, 93, 94, 106, 112, 114, 127, 128, " + "130, 139, 141, 183, 236, 237, 238, 239, 254, 266, 329, 330, 378, 379, " + "380, 381, 382, 383, 404, 522) " + "ORDER BY RAND() LIMIT 1", zone->GetShortName()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + // if the database query failed + LogFile->write(EQEMuLog::Error, "Error querying database for monster summoning pet in zone %s (%s)", zone->GetShortName(), results.ErrorMessage().c_str()); } + if (results.RowCount() != 0) { + auto row = results.begin(); + monsterid = atoi(row[0]); + } + + // since we don't have any monsters, just make it look like an earth pet for now + if (monsterid == 0) + monsterid = 567; + // give the summoned pet the attributes of the monster we found const NPCType* monster = database.GetNPCType(monsterid); if(monster) { @@ -396,12 +395,9 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower, npc_type->gender = monster->gender; npc_type->luclinface = monster->luclinface; npc_type->helmtexture = monster->helmtexture; - } - else { + } else LogFile->write(EQEMuLog::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid); - } - safe_delete_array(query); } //this takes ownership of the npc_type data @@ -450,46 +446,35 @@ bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) { } bool ZoneDatabase::GetPoweredPetEntry(const char *pet_type, int16 petpower, PetRecord *into) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 querylen = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query; - if (petpower <= 0) { - querylen = MakeAnyLenString(&query, - "SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset FROM pets " - "WHERE type='%s' AND petpower<=0", pet_type); - } - else { - querylen = MakeAnyLenString(&query, - "SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset FROM pets " - "WHERE type='%s' AND petpower<=%d ORDER BY petpower DESC LIMIT 1", pet_type, petpower); - } + if (petpower <= 0) + query = StringFormat("SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset " + "FROM pets WHERE type='%s' AND petpower<=0", pet_type); + else + query = StringFormat("SELECT npcID, temp, petpower, petcontrol, petnaming, monsterflag, equipmentset " + "FROM pets WHERE type='%s' AND petpower<=%d ORDER BY petpower DESC LIMIT 1", + pet_type, petpower); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetPoweredPetEntry query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } - if (RunQuery(query, querylen, errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); + if (results.RowCount() != 1) + return false; - into->npc_type = atoi(row[0]); - into->temporary = atoi(row[1]); - into->petpower = atoi(row[2]); - into->petcontrol = atoi(row[3]); - into->petnaming = atoi(row[4]); - into->monsterflag = atoi(row[5]); - into->equipmentset = atoi(row[6]); + auto row = results.begin(); - mysql_free_result(result); - return(true); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetPoweredPetEntry query '%s': %s", query, errbuf); - safe_delete_array(query); - } - return(false); + into->npc_type = atoi(row[0]); + into->temporary = atoi(row[1]); + into->petpower = atoi(row[2]); + into->petcontrol = atoi(row[3]); + into->petnaming = atoi(row[4]); + into->monsterflag = atoi(row[5]); + into->equipmentset = atoi(row[6]); + + return true; } Mob* Mob::GetPet() { @@ -657,11 +642,6 @@ bool ZoneDatabase::GetBasePetItems(int32 equipmentset, uint32 *items) { // A slot will only get an item put in it if it is empty. That way // an equipmentset can overload a slot for the set(s) it includes. - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 querylen = 0; - MYSQL_RES *result; - MYSQL_ROW row; int depth = 0; int32 curset = equipmentset; int32 nextset = -1; @@ -673,56 +653,43 @@ bool ZoneDatabase::GetBasePetItems(int32 equipmentset, uint32 *items) { // query pets_equipmentset_entries with the set_id and loop over // all of the result rows. Check if we have something in the slot // already. If no, add the item id to the equipment array. - while (curset >= 0 && depth < 5) { - if (RunQuery(query, - MakeAnyLenString(&query, "SELECT nested_set FROM pets_equipmentset WHERE set_id='%s'", curset), - errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - nextset = atoi(row[0]); - mysql_free_result(result); - - if (RunQuery(query, - MakeAnyLenString(&query, "SELECT slot, item_id FROM pets_equipmentset_entries WHERE set_id='%s'", curset), - errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - slot = atoi(row[0]); - if (slot >= EmuConstants::EQUIPMENT_SIZE) - continue; - if (items[slot] == 0) - items[slot] = atoi(row[1]); - } - - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query, errbuf); - safe_delete_array(query); - } - curset = nextset; - depth++; - } - else - { - // invalid set reference, it doesn't exist - LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems equipment set '%d' does not exist", curset); - mysql_free_result(result); - return false; - } - } - else - { - LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT nested_set FROM pets_equipmentset WHERE set_id = '%s'", curset); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } - } // end while + } + + if (results.RowCount() != 1) { + // invalid set reference, it doesn't exist + LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems equipment set '%d' does not exist", curset); + return false; + } + + auto row = results.begin(); + nextset = atoi(row[0]); + + query = StringFormat("SELECT slot, item_id FROM pets_equipmentset_entries WHERE set_id='%s'", curset); + results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in GetBasePetItems query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + else { + for (row = results.begin(); row != results.end(); ++row) + { + slot = atoi(row[0]); + + if (slot >= EmuConstants::EQUIPMENT_SIZE) + continue; + + if (items[slot] == 0) + items[slot] = atoi(row[1]); + } + } + + curset = nextset; + depth++; + } return true; } diff --git a/zone/QGlobals.cpp b/zone/qglobals.cpp similarity index 59% rename from zone/QGlobals.cpp rename to zone/qglobals.cpp index 73faa8b2c..bc7bf680e 100644 --- a/zone/QGlobals.cpp +++ b/zone/qglobals.cpp @@ -1,6 +1,6 @@ #include "../common/debug.h" -#include "../common/StringUtil.h" -#include "QGlobals.h" +#include "../common/string_util.h" +#include "qglobals.h" #include "masterentity.h" #include "zone.h" #include "zonedb.h" @@ -51,7 +51,7 @@ void QGlobalCache::Combine(std::list &cacheA, std::list cacheB void QGlobalCache::GetQGlobals(std::list &globals, NPC *n, Client *c, Zone *z) { globals.clear(); - + QGlobalCache *npc_c = nullptr; QGlobalCache *char_c = nullptr; QGlobalCache *zone_c = nullptr; @@ -139,75 +139,38 @@ void QGlobalCache::PurgeExpiredGlobals() void QGlobalCache::LoadByNPCID(uint32 npcID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate" - " from quest_globals where npcid = %d", npcID), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF)); - } - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("SELECT name, charid, npcid, zoneid, value, expdate " + "FROM quest_globals WHERE npcid = %d", npcID); + LoadBy(query); } void QGlobalCache::LoadByCharID(uint32 charID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate from" - " quest_globals where charid = %d && npcid = 0", charID), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF)); - } - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("SELECT name, charid, npcid, zoneid, value, expdate " + "FROM quest_globals WHERE charid = %d && npcid = 0", charID); + LoadBy(query); } void QGlobalCache::LoadByZoneID(uint32 zoneID) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate from quest_globals" - " where zoneid = %d && npcid = 0 && charid = 0", zoneID), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF)); - } - mysql_free_result(result); - } - safe_delete_array(query); + std::string query = StringFormat("SELECT name, charid, npcid, zoneid, value, expdate " + "FROM quest_globals WHERE zoneid = %d && npcid = 0 && charid = 0", zoneID); + LoadBy(query); } + void QGlobalCache::LoadByGlobalContext() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = "SELECT name, charid, npcid, zoneid, value, expdate " + "FROM quest_globals WHERE zoneid = 0 && npcid = 0 && charid = 0"; + LoadBy(query); + } - if (database.RunQuery(query, MakeAnyLenString(&query, "select name, charid, npcid, zoneid, value, expdate from quest_globals" - " where zoneid = 0 && npcid = 0 && charid = 0"), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - AddGlobal(0, QGlobal(std::string(row[0]), atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5]?atoi(row[5]):0xFFFFFFFF)); - } - mysql_free_result(result); - } - safe_delete_array(query); +void QGlobalCache::LoadBy(const std::string &query) +{ + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; + + for (auto row = results.begin(); row != results.end(); ++row) + AddGlobal(0, QGlobal(row[0], atoi(row[1]), atoi(row[2]), atoi(row[3]), row[4], row[5] ? atoi(row[5]) : 0xFFFFFFFF)); } diff --git a/zone/QGlobals.h b/zone/qglobals.h similarity index 97% rename from zone/QGlobals.h rename to zone/qglobals.h index 73a795186..5f0938a20 100644 --- a/zone/QGlobals.h +++ b/zone/qglobals.h @@ -42,6 +42,7 @@ public: void LoadByZoneID(uint32 zoneID); //zone void LoadByGlobalContext(); //zone protected: + void LoadBy(const std::string &query); std::list qGlobalBucket; }; diff --git a/zone/queryserv.cpp b/zone/queryserv.cpp new file mode 100644 index 000000000..ad7de0424 --- /dev/null +++ b/zone/queryserv.cpp @@ -0,0 +1,52 @@ +/* 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 +*/ + +#include "../common/debug.h" +#include "../common/servertalk.h" +#include "../common/string_util.h" +#include "queryserv.h" +#include "worldserver.h" +#include "net.h" + +#include + +extern WorldServer worldserver; +extern QueryServ* QServ; + +QueryServ::QueryServ(){ +} + +QueryServ::~QueryServ(){ +} + +void QueryServ::SendQuery(std::string Query) +{ + ServerPacket* pack = new ServerPacket(ServerOP_QSSendQuery, Query.length() + 5); + pack->WriteUInt32(Query.length()); /* Pack Query String Size so it can be dynamically broken out at queryserv */ + pack->WriteString(Query.c_str()); /* Query */ + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void QueryServ::PlayerLogEvent(int Event_Type, int Character_ID, std::string Event_Desc) +{ + std::string query = StringFormat( + "INSERT INTO `qs_player_events` (event, char_id, event_desc, time) VALUES (%i, %i, '%s', UNIX_TIMESTAMP(now()))", + Event_Type, Character_ID, EscapeString(Event_Desc).c_str()); + SendQuery(query); +} diff --git a/zone/queryserv.h b/zone/queryserv.h new file mode 100644 index 000000000..8aafcafda --- /dev/null +++ b/zone/queryserv.h @@ -0,0 +1,35 @@ +#ifndef QUERYSERV_ZONE_H +#define QUERYSERV_ZONE_H + + +/* + enum PlayerGenericLogEventTypes + These Enums are for the generic logging table that are not complex and require more advanced logic +*/ + +enum PlayerGenericLogEventTypes { + Player_Log_Quest = 1, + Player_Log_Zoning, + Player_Log_Deaths, + Player_Log_Connect_State, + Player_Log_Levels, + Player_Log_Keyring_Addition, + Player_Log_QGlobal_Update, + Player_Log_Task_Updates, + Player_Log_AA_Purchases, + Player_Log_Trade_Skill_Events, + Player_Log_Issued_Commands, + Player_Log_Money_Transactions, + Player_Log_Alternate_Currency_Transactions, +}; + + +class QueryServ{ + public: + QueryServ(); + ~QueryServ(); + void SendQuery(std::string Query); + void PlayerLogEvent(int Event_Type, int Character_ID, std::string Event_Desc); +}; + +#endif /* QUERYSERV_ZONE_H */ \ No newline at end of file diff --git a/zone/Quest.cpp b/zone/quest.cpp similarity index 99% rename from zone/Quest.cpp rename to zone/quest.cpp index 9a98902ab..fb2d9f851 100644 --- a/zone/Quest.cpp +++ b/zone/quest.cpp @@ -29,7 +29,7 @@ #include "../common/unix.h" #endif -#include "Quest.h" +#include "quest.h" pquest_entry Quest::m_pQuests; int Quest::m_nQuests; diff --git a/zone/Quest.h b/zone/quest.h similarity index 100% rename from zone/Quest.h rename to zone/quest.h diff --git a/zone/QuestInterface.h b/zone/quest_interface.h similarity index 85% rename from zone/QuestInterface.h rename to zone/quest_interface.h index 8e3e3a93e..68c79923f 100644 --- a/zone/QuestInterface.h +++ b/zone/quest_interface.h @@ -20,6 +20,7 @@ #define _EQE_QUESTINTERFACE_H #include "../common/types.h" +#include "../common/any.h" #include "event_codes.h" class ItemInst; @@ -29,19 +30,19 @@ class NPC; class QuestInterface { public: virtual int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int EventGlobalNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int EventGlobalPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual bool HasQuestSub(uint32 npcid, QuestEventID evt) { return false; } virtual bool HasGlobalQuestSub(QuestEventID evt) { return false; } @@ -60,13 +61,13 @@ public: virtual void LoadEncounterScript(std::string filename, std::string encounter_name) { } virtual int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int DispatchEventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { return 0; } + std::vector *extra_pointers) { return 0; } virtual void AddVar(std::string name, std::string val) { } virtual std::string GetVar(std::string name) { return std::string(); } diff --git a/zone/QuestParserCollection.cpp b/zone/quest_parser_collection.cpp similarity index 97% rename from zone/QuestParserCollection.cpp rename to zone/quest_parser_collection.cpp index 834cf1528..10703c299 100644 --- a/zone/QuestParserCollection.cpp +++ b/zone/quest_parser_collection.cpp @@ -17,10 +17,10 @@ */ #include "../common/debug.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "../common/features.h" -#include "QuestParserCollection.h" -#include "QuestInterface.h" +#include "quest_parser_collection.h" +#include "quest_interface.h" #include "zone.h" #include "questmgr.h" @@ -234,7 +234,7 @@ bool QuestParserCollection::ItemHasQuestSub(ItemInst *itm, QuestEventID evt) { } int QuestParserCollection::EventNPC(QuestEventID evt, NPC *npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { int rd = DispatchEventNPC(evt, npc, init, data, extra_data, extra_pointers); int rl = EventNPCLocal(evt, npc, init, data, extra_data, extra_pointers); int rg = EventNPCGlobal(evt, npc, init, data, extra_data, extra_pointers); @@ -252,7 +252,7 @@ int QuestParserCollection::EventNPC(QuestEventID evt, NPC *npc, Mob *init, std:: } int QuestParserCollection::EventNPCLocal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { std::map::iterator iter = _npc_quest_status.find(npc->GetNPCTypeID()); if(iter != _npc_quest_status.end()) { //loaded or failed to load @@ -275,7 +275,7 @@ int QuestParserCollection::EventNPCLocal(QuestEventID evt, NPC* npc, Mob *init, } int QuestParserCollection::EventNPCGlobal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(_global_npc_quest_status != QuestUnloaded && _global_npc_quest_status != QuestFailedToLoad) { std::map::iterator qiter = _interfaces.find(_global_npc_quest_status); return qiter->second->EventGlobalNPC(evt, npc, init, data, extra_data, extra_pointers); @@ -294,7 +294,7 @@ int QuestParserCollection::EventNPCGlobal(QuestEventID evt, NPC* npc, Mob *init, } int QuestParserCollection::EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { int rd = DispatchEventPlayer(evt, client, data, extra_data, extra_pointers); int rl = EventPlayerLocal(evt, client, data, extra_data, extra_pointers); int rg = EventPlayerGlobal(evt, client, data, extra_data, extra_pointers); @@ -312,7 +312,7 @@ int QuestParserCollection::EventPlayer(QuestEventID evt, Client *client, std::st } int QuestParserCollection::EventPlayerLocal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(_player_quest_status == QuestUnloaded) { std::string filename; QuestInterface *qi = GetQIByPlayerQuest(filename); @@ -331,7 +331,7 @@ int QuestParserCollection::EventPlayerLocal(QuestEventID evt, Client *client, st } int QuestParserCollection::EventPlayerGlobal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { if(_global_player_quest_status == QuestUnloaded) { std::string filename; QuestInterface *qi = GetQIByGlobalPlayerQuest(filename); @@ -350,7 +350,7 @@ int QuestParserCollection::EventPlayerGlobal(QuestEventID evt, Client *client, s } int QuestParserCollection::EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { std::string item_script; if(item->GetItem()->ScriptFileID != 0) { item_script = "script_"; @@ -396,7 +396,7 @@ int QuestParserCollection::EventItem(QuestEventID evt, Client *client, ItemInst } int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { std::map::iterator iter = _spell_quest_status.find(spell_id); if(iter != _spell_quest_status.end()) { //loaded or failed to load @@ -431,7 +431,7 @@ int QuestParserCollection::EventSpell(QuestEventID evt, NPC* npc, Client *client } int QuestParserCollection::EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { auto iter = _encounter_quest_status.find(encounter_name); if(iter != _encounter_quest_status.end()) { //loaded or failed to load @@ -600,9 +600,8 @@ QuestInterface *QuestParserCollection::GetQIByNPCQuest(uint32 npcid, std::string } QuestInterface *QuestParserCollection::GetQIByPlayerQuest(std::string &filename) { - - if(!zone) - return nullptr; + if(!zone || !zone->IsLoaded()) + return nullptr; //first look for /quests/zone/player_v[instance_version].ext (precedence) filename = "quests/"; @@ -975,7 +974,7 @@ void QuestParserCollection::GetErrors(std::list &err) { } int QuestParserCollection::DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { int ret = 0; auto iter = _load_precedence.begin(); while(iter != _load_precedence.end()) { @@ -989,7 +988,7 @@ int QuestParserCollection::DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *ini } int QuestParserCollection::DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { int ret = 0; auto iter = _load_precedence.begin(); while(iter != _load_precedence.end()) { @@ -1003,7 +1002,7 @@ int QuestParserCollection::DispatchEventPlayer(QuestEventID evt, Client *client, } int QuestParserCollection::DispatchEventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, - uint32 extra_data, std::vector *extra_pointers) { + uint32 extra_data, std::vector *extra_pointers) { int ret = 0; auto iter = _load_precedence.begin(); while(iter != _load_precedence.end()) { @@ -1017,7 +1016,7 @@ int QuestParserCollection::DispatchEventItem(QuestEventID evt, Client *client, I } int QuestParserCollection::DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers) { + std::vector *extra_pointers) { int ret = 0; auto iter = _load_precedence.begin(); while(iter != _load_precedence.end()) { diff --git a/zone/QuestParserCollection.h b/zone/quest_parser_collection.h similarity index 83% rename from zone/QuestParserCollection.h rename to zone/quest_parser_collection.h index f9da8c79c..006bcfe24 100644 --- a/zone/QuestParserCollection.h +++ b/zone/quest_parser_collection.h @@ -20,10 +20,10 @@ #define _EQE_QUESTPARSERCOLLECTION_H #include "../common/types.h" -#include "../common/Item.h" +#include "../common/item.h" #include "masterentity.h" -#include "QuestInterface.h" +#include "quest_interface.h" #include #include @@ -51,15 +51,15 @@ public: bool ItemHasQuestSub(ItemInst *itm, QuestEventID evt); int EventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers = nullptr); + std::vector *extra_pointers = nullptr); int EventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers = nullptr); + std::vector *extra_pointers = nullptr); int EventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers = nullptr); + std::vector *extra_pointers = nullptr); int EventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers = nullptr); + std::vector *extra_pointers = nullptr); int EventEncounter(QuestEventID evt, std::string encounter_name, uint32 extra_data, - std::vector *extra_pointers = nullptr); + std::vector *extra_pointers = nullptr); void GetErrors(std::list &err); @@ -69,10 +69,10 @@ private: bool PlayerHasQuestSubLocal(QuestEventID evt); bool PlayerHasQuestSubGlobal(QuestEventID evt); - int EventNPCLocal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); - int EventNPCGlobal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); - int EventPlayerLocal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers); - int EventPlayerGlobal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers); + int EventNPCLocal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); + int EventNPCGlobal(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, std::vector *extra_pointers); + int EventPlayerLocal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers); + int EventPlayerGlobal(QuestEventID evt, Client *client, std::string data, uint32 extra_data, std::vector *extra_pointers); QuestInterface *GetQIByNPCQuest(uint32 npcid, std::string &filename); QuestInterface *GetQIByGlobalNPCQuest(std::string &filename); @@ -83,13 +83,13 @@ private: QuestInterface *GetQIByEncounterQuest(std::string encounter_name, std::string &filename); int DispatchEventNPC(QuestEventID evt, NPC* npc, Mob *init, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); int DispatchEventPlayer(QuestEventID evt, Client *client, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); int DispatchEventItem(QuestEventID evt, Client *client, ItemInst *item, Mob *mob, std::string data, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); int DispatchEventSpell(QuestEventID evt, NPC* npc, Client *client, uint32 spell_id, uint32 extra_data, - std::vector *extra_pointers); + std::vector *extra_pointers); std::map _interfaces; std::map _extensions; diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 3d3dfd225..2ce1dbc37 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -16,78 +16,37 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* - -Assuming you want to add a new perl quest function named joe -that takes 1 integer argument.... - -1. Add the prototype to the quest manager: -questmgr.h: add (~line 50) - void joe(int arg); - -2. Define the actual function in questmgr.cpp: -void QuestManager::joe(int arg) { - //... do something -} - -3. Copy one of the XS routines in perlparser.cpp, preferably - one with the same number of arguments as your routine. Rename - as needed. - Finally, add your routine to the list at the bottom of perlparser.cpp - - -4. -If you want it to work in old mode perl and .qst, edit parser.cpp -Parser::ExCommands (~line 777) - else if (!strcmp(command,"joe")) { - quest_manager.joe(atoi(arglist[0])); - } - -And then at then end of embparser.cpp, add: -"sub joe{push(@cmd_queue,{func=>'joe',args=>join(',',@_)});}" - - - -*/ - -#include "../common/debug.h" -#include "entity.h" -#include "masterentity.h" -#include - -#include -#include -#include - -#include "worldserver.h" -#include "net.h" -#include "../common/skills.h" #include "../common/classes.h" -#include "../common/races.h" -#include "zonedb.h" +#include "../common/debug.h" +#include "../common/rulesys.h" +#include "../common/skills.h" #include "../common/spdat.h" -#include "../common/packet_functions.h" -#include "../common/StringUtil.h" -#include "spawn2.h" -#include "zone.h" +#include "../common/string_util.h" +#include "entity.h" #include "event_codes.h" #include "guild_mgr.h" -#include "../common/rulesys.h" -#include "QGlobals.h" -#include "QuestParserCollection.h" +#include "net.h" +#include "qglobals.h" +#include "queryserv.h" +#include "questmgr.h" +#include "quest_parser_collection.h" +#include "spawn2.h" +#include "worldserver.h" +#include "zone.h" +#include "zonedb.h" +#include +#include +#include #ifdef BOTS #include "bot.h" #endif - +extern QueryServ* QServ; extern Zone* zone; extern WorldServer worldserver; extern EntityList entity_list; -#include "questmgr.h" - -//declare our global instance QuestManager quest_manager; #define QuestManagerCurrentQuestVars() \ @@ -95,6 +54,7 @@ QuestManager quest_manager; Client *initiator = nullptr; \ ItemInst* questitem = nullptr; \ bool depop_npc = false; \ + std::string encounter; \ do { \ if(!quests_running_.empty()) { \ running_quest e = quests_running_.top(); \ @@ -102,6 +62,7 @@ QuestManager quest_manager; initiator = e.initiator; \ questitem = e.questitem; \ depop_npc = e.depop_npc; \ + encounter = e.encounter; \ } \ } while(0) @@ -151,12 +112,13 @@ void QuestManager::Process() { } } -void QuestManager::StartQuest(Mob *_owner, Client *_initiator, ItemInst* _questitem) { +void QuestManager::StartQuest(Mob *_owner, Client *_initiator, ItemInst* _questitem, std::string encounter) { running_quest run; run.owner = _owner; run.initiator = _initiator; run.questitem = _questitem; run.depop_npc = false; + run.encounter = encounter; quests_running_.push(run); } @@ -173,7 +135,6 @@ void QuestManager::EndQuest() { else ++cur; } - run.owner->Depop(); } quests_running_.pop(); @@ -448,7 +409,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); @@ -472,7 +433,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); @@ -905,7 +866,7 @@ uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) { uint16 book_slot, count; uint16 curspell; - uint16 Char_ID = initiator->CharacterID(); + uint32 Char_ID = initiator->CharacterID(); bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals); bool SpellGlobalCheckResult = 0; @@ -947,7 +908,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; @@ -961,7 +922,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++) { @@ -975,18 +936,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 } @@ -1289,38 +1252,34 @@ void QuestManager::signal(int npc_id, int wait_ms) { void QuestManager::setglobal(const char *varname, const char *newvalue, int options, const char *duration) { QuestManagerCurrentQuestVars(); - int qgZoneid=zone->GetZoneID(); - int qgCharid=0; - int qgNpcid = owner->GetNPCTypeID(); + int qgZoneid = zone->GetZoneID(); + int qgCharid = 0; + int qgNpcid = owner ? owner->GetNPCTypeID() : 0; // encounter scripts don't have an owner /* options value determines the availability of global variables to NPCs when a quest begins - ------------------------------------------------------------------ - value npcid player zone - ------------------------------------------------------------------ - 0 this this this - 1 all this this - 2 this all this - 3 all all this - 4 this this all - 5 all this all - 6 this all all - 7 all all all + ------------------------------------------------------------------ + value npcid player zone + ------------------------------------------------------------------ + 0 this this this + 1 all this this + 2 this all this + 3 all all this + 4 this this all + 5 all this all + 6 this all all + 7 all all all */ - if (initiator && initiator->IsClient()) // some events like waypoint and spawn don't have a player involved - { + + if (initiator && initiator->IsClient()){ // some events like waypoint and spawn don't have a player involved qgCharid=initiator->CharacterID(); } - - else - { + else { qgCharid=-qgNpcid; // make char id negative npc id as a fudge } - if (options < 0 || options > 7) - { + if (options < 0 || options > 7) { std::cerr << "Invalid options for global var " << varname << " using defaults" << std::endl; } // default = 0 (only this npcid,player and zone) - else - { + else { if (options & 1) qgNpcid=0; if (options & 2) @@ -1330,143 +1289,130 @@ void QuestManager::setglobal(const char *varname, const char *newvalue, int opti } InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, newvalue, QGVarDuration(duration)); + + /* QS: PlayerLogQGlobalUpdate */ + if (RuleB(QueryServ, PlayerLogQGlobalUpdate) && qgCharid && qgCharid > 0 && initiator && initiator->IsClient()){ + std::string event_desc = StringFormat("Update :: qglobal:%s to qvalue:%s zoneid:%i instid:%i", varname, newvalue, initiator->GetZoneID(), initiator->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_QGlobal_Update, qgCharid, event_desc); + } } /* 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]; +int QuestManager::InsertQuestGlobal(int charid, int npcid, int zoneid, const char *varname, const char *varvalue, int duration) { // 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); + /* + NOTE: this should be escaping the contents of arglist + npcwise a malicious script can arbitrarily alter the DB + */ - if(zone) - { - //first delete our global - 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); - //then create a new one with the new id - 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; } -void QuestManager::targlobal(const char *varname, const char *value, const char *duration, int qgNpcid, int qgCharid, int qgZoneid) -{ +void QuestManager::targlobal(const char *varname, const char *value, const char *duration, int qgNpcid, int qgCharid, int qgZoneid) { InsertQuestGlobal(qgCharid, qgNpcid, qgZoneid, varname, value, QGVarDuration(duration)); } 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(); + int qgZoneid = zone->GetZoneID(); + int qgCharid = 0; + int qgNpcid = owner ? owner->GetNPCTypeID() : 0; // encounter scripts don't have an owner + 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()){ + std::string event_desc = StringFormat("Deleted :: qglobal:%s zoneid:%i instid:%i", varname, initiator->GetZoneID(), initiator->GetInstanceID()); + 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); - if(zone) - { - ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); - ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; + 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; - qgu->npc_id = qgNpcid; - qgu->char_id = qgCharid; - qgu->zone_id = qgZoneid; - strcpy(qgu->name, varname); + if(!zone) + return; - 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); + ServerPacket* pack = new ServerPacket(ServerOP_QGlobalDelete, sizeof(ServerQGlobalDelete_Struct)); + ServerQGlobalDelete_Struct *qgu = (ServerQGlobalDelete_Struct*)pack->pBuffer; - worldserver.SendPacket(pack); - safe_delete(pack); - } + qgu->npc_id = qgNpcid; + qgu->char_id = qgCharid; + qgu->zone_id = qgZoneid; + strcpy(qgu->name, varname); + + 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) @@ -1687,11 +1633,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; @@ -1701,25 +1642,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 @@ -1762,7 +1703,7 @@ bool QuestManager::summonburriedplayercorpse(uint32 char_id, float dest_x, float bool Result = false; if(char_id > 0) { - Corpse* PlayerCorpse = database.SummonBurriedPlayerCorpse(char_id, zone->GetZoneID(), zone->GetInstanceID(), dest_x, dest_y, dest_z, dest_heading); + Corpse* PlayerCorpse = database.SummonBuriedCharacterCorpses(char_id, zone->GetZoneID(), zone->GetInstanceID(), dest_x, dest_y, dest_z, dest_heading); if(PlayerCorpse) { Result = true; } @@ -1785,7 +1726,7 @@ uint32 QuestManager::getplayerburriedcorpsecount(uint32 char_id) { uint32 Result = 0; if(char_id > 0) { - Result = database.GetPlayerBurriedCorpseCount(char_id); + Result = database.GetCharacterBuriedCorpseCount(char_id); } return Result; } @@ -1799,12 +1740,12 @@ bool QuestManager::buryplayercorpse(uint32 char_id) uint32 PlayerCorpse = database.GetFirstCorpseID(char_id); if(PlayerCorpse > 0) { - database.BuryPlayerCorpse(PlayerCorpse); + database.BuryCharacterCorpse(PlayerCorpse); Corpse* corpse = entity_list.GetCorpseByDBID(PlayerCorpse); if(corpse) { corpse->Save(); - corpse->DepopCorpse(); + corpse->DepopPlayerCorpse(); } else { @@ -2295,19 +2236,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(); } } @@ -2566,7 +2507,7 @@ void QuestManager::DestroyInstance(uint16 instance_id) uint16 QuestManager::GetInstanceID(const char *zone, int16 version) { QuestManagerCurrentQuestVars(); - if(initiator) + if (initiator) { return database.GetInstanceID(zone, initiator->CharacterID(), version); } @@ -2576,7 +2517,7 @@ uint16 QuestManager::GetInstanceID(const char *zone, int16 version) void QuestManager::AssignToInstance(uint16 instance_id) { QuestManagerCurrentQuestVars(); - if(initiator) + if (initiator) { database.AddClientToInstance(instance_id, initiator->CharacterID()); } @@ -2585,10 +2526,10 @@ void QuestManager::AssignToInstance(uint16 instance_id) void QuestManager::AssignGroupToInstance(uint16 instance_id) { QuestManagerCurrentQuestVars(); - if(initiator) + if (initiator) { Group *g = initiator->GetGroup(); - if(g) + if (g) { uint32 gid = g->GetID(); database.AssignGroupToInstance(gid, instance_id); @@ -2599,7 +2540,7 @@ void QuestManager::AssignGroupToInstance(uint16 instance_id) void QuestManager::AssignRaidToInstance(uint16 instance_id) { QuestManagerCurrentQuestVars(); - if(initiator) + if (initiator) { Raid *r = initiator->GetRaid(); if(r) @@ -2613,36 +2554,28 @@ void QuestManager::AssignRaidToInstance(uint16 instance_id) void QuestManager::RemoveFromInstance(uint16 instance_id) { QuestManagerCurrentQuestVars(); - if(initiator) { - if(database.RemoveClientFromInstance(instance_id, initiator->CharacterID())) { + if (initiator) + { + if (database.RemoveClientFromInstance(instance_id, initiator->CharacterID())) initiator->Message(MT_Say, "Removed client from instance."); - } else { + else initiator->Message(MT_Say, "Failed to remove client from instance."); - } } } void QuestManager::RemoveAllFromInstance(uint16 instance_id) { QuestManagerCurrentQuestVars(); - if(initiator) { + if (initiator) + { std::list charid_list; - bool removed_all = true; - uint16 fail_count = 0; - database.GetCharactersInInstance(instance_id,charid_list); - auto iter = charid_list.begin(); - while(iter != charid_list.end()) { - if(!database.RemoveClientFromInstance(instance_id, *iter)) { - removed_all = false; - ++fail_count; - } - ++iter; - } - if (removed_all) { + + if (database.RemoveClientsFromInstance(instance_id)) initiator->Message(MT_Say, "Removed all players from instance."); - } else { - // once the expedition system is in, this message it not relevant - initiator->Message(MT_Say, "Failed to remove %i player(s) from instance.", fail_count); + else + { + database.GetCharactersInInstance(instance_id, charid_list); + initiator->Message(MT_Say, "Failed to remove %i player(s) from instance.", charid_list.size()); // once the expedition system is in, this message it not relevant } } } @@ -2684,11 +2617,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); @@ -2696,70 +2624,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; @@ -2951,6 +2859,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; @@ -2968,7 +2885,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; @@ -2978,6 +2895,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); } @@ -3039,3 +2968,12 @@ ItemInst *QuestManager::GetQuestItem() const { return nullptr; } + +std::string QuestManager::GetEncounter() const { + if(!quests_running_.empty()) { + running_quest e = quests_running_.top(); + return e.encounter; + } + + return ""; +} diff --git a/zone/questmgr.h b/zone/questmgr.h index 89a0eca78..dd471d74f 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -25,8 +25,10 @@ #include #include -class NPC; class Client; +class ItemInst; +class Mob; +class NPC; class QuestManager { struct running_quest { @@ -34,12 +36,13 @@ class QuestManager { Client *initiator; ItemInst* questitem; bool depop_npc; + std::string encounter; }; public: QuestManager(); virtual ~QuestManager(); - void StartQuest(Mob *_owner, Client *_initiator = nullptr, ItemInst* _questitem = nullptr); + void StartQuest(Mob *_owner, Client *_initiator = nullptr, ItemInst* _questitem = nullptr, std::string encounter = ""); void EndQuest(); bool QuestsRunning() { return !quests_running_.empty(); } @@ -239,8 +242,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); @@ -250,6 +255,7 @@ public: NPC *GetNPC() const; Mob *GetOwner() const; ItemInst *GetQuestItem() const; + std::string GetEncounter() const; inline bool ProximitySayInUse() { return HaveProximitySays; } #ifdef BOTS diff --git a/zone/raids.cpp b/zone/raids.cpp index c712fc4bd..d1e8db464 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -17,10 +17,10 @@ */ #include "../common/debug.h" #include "masterentity.h" -#include "NpcAI.h" +#include "npc_ai.h" #include "../common/packet_functions.h" #include "../common/packet_dump.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "worldserver.h" extern EntityList entity_list; extern WorldServer worldserver; @@ -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,36 @@ 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); + + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error inserting into raid members: %s", results.ErrorMessage().c_str()); } - 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 +127,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 +154,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 +175,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 +195,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 +212,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()) + LogFile->write(EQEMuLog::Error, "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()) + LogFile->write(EQEMuLog::Error, "Set Raid Leader error: %s\n", results.ErrorMessage().c_str()); strn0cpy(leadername, name, 64); @@ -250,6 +256,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 +322,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(); } @@ -424,7 +478,7 @@ void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) if(members[x].member == caster) { caster->SpellOnTarget(spellid, caster); #ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) + if(spells[spellid].targettype != ST_GroupNoPets && caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) caster->SpellOnTarget(spellid, caster->GetPet()); #endif } @@ -435,7 +489,7 @@ void Raid::CastGroupSpell(Mob* caster, uint16 spellid, uint32 gid) if(distance <= range2){ caster->SpellOnTarget(spellid, members[x].member); #ifdef GROUP_BUFF_PETS - if(members[x].member->GetPet() && members[x].member->HasPetAffinity() && !members[x].member->GetPet()->IsCharmed()) + if(spells[spellid].targettype != ST_GroupNoPets && members[x].member->GetPet() && members[x].member->HasPetAffinity() && !members[x].member->GetPet()->IsCharmed()) caster->SpellOnTarget(spellid, members[x].member->GetPet()); #endif } @@ -461,7 +515,7 @@ uint32 Raid::GetTotalRaidDamage(Mob* other) return total; } -void Raid::HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, int32 range) +void Raid::HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, float range) { if (!caster) return; @@ -504,7 +558,7 @@ void Raid::HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, int32 range) } -void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster, int32 limit) +void Raid::BalanceHP(int32 penalty, uint32 gid, float range, Mob* caster, int32 limit) { if (!caster) return; @@ -514,7 +568,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; @@ -560,14 +614,14 @@ void Raid::BalanceHP(int32 penalty, uint32 gid, int32 range, Mob* caster, int32 } } -void Raid::BalanceMana(int32 penalty, uint32 gid, int32 range, Mob* caster, int32 limit) +void Raid::BalanceMana(int32 penalty, uint32 gid, float range, Mob* caster, int32 limit) { if (!caster) return; if (!range) range = 200; - + float distance; float range2 = range*range; @@ -775,27 +829,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 +855,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 +875,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 +1106,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 +1121,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 +1184,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 +1197,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 +1299,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 +1389,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 +1494,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 +1611,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..7c3561cd2 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 @@ -147,9 +157,9 @@ public: void CastGroupSpell(Mob* caster,uint16 spellid, uint32 gid); void SplitExp(uint32 exp, Mob* other); uint32 GetTotalRaidDamage(Mob* other); - void BalanceHP(int32 penalty, uint32 gid, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); - void BalanceMana(int32 penalty, uint32 gid, int32 range = 0, Mob* caster = nullptr, int32 limit = 0); - void HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, int32 range = 0); + void BalanceHP(int32 penalty, uint32 gid, float range = 0, Mob* caster = nullptr, int32 limit = 0); + void BalanceMana(int32 penalty, uint32 gid, float range = 0, Mob* caster = nullptr, int32 limit = 0); + void HealGroup(uint32 heal_amt, Mob* caster, uint32 gid, float range = 0); void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr); void GroupBardPulse(Mob* caster, uint16 spellid, uint32 gid); @@ -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/RaycastMesh.cpp b/zone/raycast_mesh.cpp similarity index 99% rename from zone/RaycastMesh.cpp rename to zone/raycast_mesh.cpp index 404961bf0..078f225a1 100644 --- a/zone/RaycastMesh.cpp +++ b/zone/raycast_mesh.cpp @@ -1,4 +1,4 @@ -#include "RaycastMesh.h" +#include "raycast_mesh.h" #include #include #include diff --git a/zone/RaycastMesh.h b/zone/raycast_mesh.h similarity index 100% rename from zone/RaycastMesh.h rename to zone/raycast_mesh.h diff --git a/zone/remote_call.cpp b/zone/remote_call.cpp index 14386babb..96186a08b 100644 --- a/zone/remote_call.cpp +++ b/zone/remote_call.cpp @@ -1,5 +1,5 @@ #include "../common/debug.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/logsys.h" #include "../common/logtypes.h" #include "../common/md5.h" @@ -11,7 +11,7 @@ #include "entity.h" #include "mob.h" #include "npc.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "remote_call.h" #include "remote_call_subscribe.h" #include "worldserver.h" diff --git a/zone/remote_call_subscribe.cpp b/zone/remote_call_subscribe.cpp index a50ff02ba..2c73020c7 100644 --- a/zone/remote_call_subscribe.cpp +++ b/zone/remote_call_subscribe.cpp @@ -2,7 +2,7 @@ #include "../common/logsys.h" #include "../common/logtypes.h" #include "../common/md5.h" -#include "../common/EmuTCPConnection.h" +#include "../common/emu_tcp_connection.h" #include "../common/packet_functions.h" #include "../common/packet_dump.h" #include "../common/servertalk.h" diff --git a/zone/spawn2.cpp b/zone/spawn2.cpp index 8d2d47fc0..8d8562157 100644 --- a/zone/spawn2.cpp +++ b/zone/spawn2.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include #include "spawn2.h" #include "entity.h" @@ -354,96 +354,86 @@ void Spawn2::DeathReset(bool realdeath) } bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList &spawn2_list, int16 version, uint32 repopdelay) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; const char *zone_name = database.GetZoneName(zoneid); - - MakeAnyLenString(&query, "SELECT id, spawngroupID, x, y, z, heading, respawntime, variance, pathgrid, _condition, cond_value, enabled, animation FROM spawn2 WHERE zone='%s' AND version=%u", zone_name, version); - if (RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - Spawn2* newSpawn = 0; - - bool perl_enabled = atoi(row[11]) == 1 ? true : false; - uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000); - newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]), atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12])); - spawn2_list.Insert( newSpawn ); - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in PopulateZoneLists query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, " + "respawntime, variance, pathgrid, _condition, " + "cond_value, enabled, animation FROM spawn2 " + "WHERE zone = '%s' AND version = %u", + zone_name, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in PopulateZoneLists query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + Spawn2* newSpawn = 0; + + bool perl_enabled = atoi(row[11]) == 1? true: false; + uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000); + newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), + atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]), + atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12])); + + spawn2_list.Insert(newSpawn); + } return true; } Spawn2* ZoneDatabase::LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, spawngroupID, x, y, z, heading, respawntime, variance, pathgrid, _condition, cond_value, enabled, animation FROM spawn2 WHERE id=%i", spawn2id), errbuf, &result)) { - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - bool perl_enabled = atoi(row[11]) == 1 ? true : false; - Spawn2* newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atoi(row[6]), atoi(row[7]), timeleft, atoi(row[8]), atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12])); - spawn2_list.Insert( newSpawn ); - mysql_free_result(result); - safe_delete_array(query); - return newSpawn; - } - mysql_free_result(result); - } + std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, " + "respawntime, variance, pathgrid, _condition, " + "cond_value, enabled, animation FROM spawn2 " + "WHERE id = %i", spawn2id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadSpawn2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return nullptr; + } - LogFile->write(EQEMuLog::Error, "Error in LoadSpawn2 query '%s': %s", query, errbuf); - safe_delete_array(query); - return 0; + if (results.RowCount() != 1) { + LogFile->write(EQEMuLog::Error, "Error in LoadSpawn2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return nullptr; + } + + auto row = results.begin(); + + bool perl_enabled = atoi(row[11]) == 1 ? true : false; + + Spawn2* newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]), + atof(row[5]), atoi(row[6]), atoi(row[7]), timeleft, atoi(row[8]), + atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12])); + + spawn2_list.Insert(newSpawn); + + return newSpawn; } -bool ZoneDatabase::CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, float heading, float x, float y, float z, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value) +bool ZoneDatabase::CreateSpawn2(Client *client, uint32 spawngroup, const char* zone, float heading, float x, float y, float z, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - // if(GetInverseXY()==1) { - // float temp=x; - // x=y; - // y=temp; - // } - if (RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO spawn2 (spawngroupID,zone,x,y,z,heading,respawntime,variance,_condition,cond_value) Values (%i, '%s', %f, %f, %f, %f, %i, %i, %u, %i)", - spawngroup, zone, x, y, z, heading, respawn, variance, condition, cond_value - ), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - if (affected_rows == 1) { - if(c) c->LogSQL(query); - return true; - } - else { - return false; - } - } - else { - LogFile->write(EQEMuLog::Error, "Error in CreateSpawn2 query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO spawn2 (spawngroupID, zone, x, y, z, heading, " + "respawntime, variance, _condition, cond_value) " + "VALUES (%i, '%s', %f, %f, %f, %f, %i, %i, %u, %i)", + spawngroup, zone, x, y, z, heading, + respawn, variance, condition, cond_value); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in CreateSpawn2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } - return false; + if (results.RowsAffected() != 1) + return false; + + if(client) + client->LogSQL(query.c_str()); + + return true; } uint32 Zone::CountSpawn2() { @@ -671,177 +661,157 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { } void SpawnConditionManager::UpdateDBEvent(SpawnEvent &event) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int len; - SpawnCondition cond; - len = MakeAnyLenString(&query, - "UPDATE spawn_events SET " - "next_minute=%d, next_hour=%d, next_day=%d, next_month=%d, " - "next_year=%d, enabled=%d, strict=%d " - "WHERE id=%d", - event.next.minute, event.next.hour, event.next.day, event.next.month, - event.next.year, event.enabled?1:0, event.strict?1:0,event.id - ); - if(!database.RunQuery(query, len, errbuf)) { - LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query, errbuf); - } - safe_delete_array(query); + std::string query = StringFormat("UPDATE spawn_events SET " + "next_minute = %d, next_hour = %d, " + "next_day = %d, next_month = %d, " + "next_year = %d, enabled = %d, " + "strict = %d WHERE id = %d", + event.next.minute, event.next.hour, + event.next.day, event.next.month, + event.next.year, event.enabled? 1: 0, + event.strict? 1: 0, event.id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query.c_str(), results.ErrorMessage().c_str()); + } void SpawnConditionManager::UpdateDBCondition(const char* zone_name, uint32 instance_id, uint16 cond_id, int16 value) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int len; - SpawnCondition cond; - len = MakeAnyLenString(&query, - "REPLACE INTO spawn_condition_values (id, value, zone, instance_id) VALUES(%u, %u, '%s', %u)", - cond_id, value, zone_name, instance_id - ); - if(!database.RunQuery(query, len, errbuf)) { - LogFile->write(EQEMuLog::Error, "Unable to update spawn condition '%s': %s\n", query, errbuf); - } - safe_delete_array(query); + std::string query = StringFormat("REPLACE INTO spawn_condition_values " + "(id, value, zone, instance_id) " + "VALUES( %u, %u, '%s', %u)", + cond_id, value, zone_name, instance_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Unable to update spawn condition '%s': %s\n", query.c_str(), results.ErrorMessage().c_str()); + } bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std::string &zone_name) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int len; - bool ret = false; - - len = MakeAnyLenString(&query, - "SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict,zone " - "FROM spawn_events WHERE id=%d", event_id); - if (database.RunQuery(query, len, errbuf, &result)) { - safe_delete_array(query); - if((row = mysql_fetch_row(result))) { - event.id = atoi(row[0]); - event.condition_id = atoi(row[1]); - event.period = atoi(row[2]); - - event.next.minute = atoi(row[3]); - event.next.hour = atoi(row[4]); - event.next.day = atoi(row[5]); - event.next.month = atoi(row[6]); - event.next.year = atoi(row[7]); - - event.enabled = atoi(row[8])==0?false:true; - event.action = (SpawnEvent::Action) atoi(row[9]); - event.argument = atoi(row[10]); - event.strict = atoi(row[11])==0?false:true; - zone_name = row[12]; - - std::string t; - EQTime::ToString(&event.next, t); - _log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s", - event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, t.c_str()); - - ret = true; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadDBEvent query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT id, cond_id, period, " + "next_minute, next_hour, next_day, " + "next_month, next_year, enabled, " + "action, argument, strict, zone " + "FROM spawn_events WHERE id = %d", event_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadDBEvent query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - return(ret); + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + event.id = atoi(row[0]); + event.condition_id = atoi(row[1]); + event.period = atoi(row[2]); + + event.next.minute = atoi(row[3]); + event.next.hour = atoi(row[4]); + event.next.day = atoi(row[5]); + event.next.month = atoi(row[6]); + event.next.year = atoi(row[7]); + + event.enabled = atoi(row[8]) != 0; + event.action = (SpawnEvent::Action) atoi(row[9]); + event.argument = atoi(row[10]); + event.strict = atoi(row[11]) != 0; + zone_name = row[12]; + + std::string timeAsString; + EQTime::ToString(&event.next, timeAsString); + + _log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s", event.enabled? "enabled": "disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, timeAsString.c_str()); + + return true; } bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int len; - //clear out old stuff.. spawn_conditions.clear(); - //load spawn conditions - SpawnCondition cond; - len = MakeAnyLenString(&query, "SELECT id, onchange, value FROM spawn_conditions WHERE zone='%s'", zone_name); - if (database.RunQuery(query, len, errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - cond.condition_id = atoi(row[0]); - cond.value = atoi(row[2]); - cond.on_change = (SpawnCondition::OnChange) atoi(row[1]); - spawn_conditions[cond.condition_id] = cond; - - _log(SPAWNS__CONDITIONS, "Loaded spawn condition %d with value %d and on_change %d", cond.condition_id, cond.value, cond.on_change); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT id, onchange, value " + "FROM spawn_conditions " + "WHERE zone = '%s'", zone_name); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + //load spawn conditions + SpawnCondition cond; + + cond.condition_id = atoi(row[0]); + cond.value = atoi(row[2]); + cond.on_change = (SpawnCondition::OnChange) atoi(row[1]); + spawn_conditions[cond.condition_id] = cond; + + _log(SPAWNS__CONDITIONS, "Loaded spawn condition %d with value %d and on_change %d", cond.condition_id, cond.value, cond.on_change); + } //load values - len = MakeAnyLenString(&query, "SELECT id, value FROM spawn_condition_values WHERE zone='%s' and instance_id=%u", zone_name, instance_id); - if (database.RunQuery(query, len, errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - std::map::iterator iter = spawn_conditions.find(atoi(row[0])); - if(iter != spawn_conditions.end()) - { - iter->second.value = atoi(row[1]); - } - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT id, value FROM spawn_condition_values " + "WHERE zone = '%s' AND instance_id = %u", + zone_name, instance_id); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); spawn_conditions.clear(); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + auto iter = spawn_conditions.find(atoi(row[0])); + + if(iter != spawn_conditions.end()) + iter->second.value = atoi(row[1]); + } //load spawn events - SpawnEvent event; - len = MakeAnyLenString(&query, - "SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict " - "FROM spawn_events WHERE zone='%s'", zone_name); - if (database.RunQuery(query, len, errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - event.id = atoi(row[0]); - event.condition_id = atoi(row[1]); - event.period = atoi(row[2]); - if(event.period == 0) { - LogFile->write(EQEMuLog::Error, "Refusing to load spawn event #%d because it has a period of 0\n", event.id); - continue; - } - - event.next.minute = atoi(row[3]); - event.next.hour = atoi(row[4]); - event.next.day = atoi(row[5]); - event.next.month = atoi(row[6]); - event.next.year = atoi(row[7]); - - event.enabled = atoi(row[8])==0?false:true; - event.action = (SpawnEvent::Action) atoi(row[9]); - event.argument = atoi(row[10]); - event.strict = atoi(row[11])==0?false:true; - spawn_events.push_back(event); - - _log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d", - event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions events query '%s': %s", query, errbuf); - safe_delete_array(query); + query = StringFormat("SELECT id, cond_id, period, next_minute, next_hour, " + "next_day, next_month, next_year, enabled, action, argument, strict " + "FROM spawn_events WHERE zone = '%s'", zone_name); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadSpawnConditions events query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + SpawnEvent event; + + event.id = atoi(row[0]); + event.condition_id = atoi(row[1]); + event.period = atoi(row[2]); + + if(event.period == 0) { + LogFile->write(EQEMuLog::Error, "Refusing to load spawn event #%d because it has a period of 0\n", event.id); + continue; + } + + event.next.minute = atoi(row[3]); + event.next.hour = atoi(row[4]); + event.next.day = atoi(row[5]); + event.next.month = atoi(row[6]); + event.next.year = atoi(row[7]); + + event.enabled = atoi(row[8])==0?false:true; + event.action = (SpawnEvent::Action) atoi(row[9]); + event.argument = atoi(row[10]); + event.strict = atoi(row[11])==0?false:true; + + spawn_events.push_back(event); + + _log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d", event.enabled? "enabled": "disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict); + } //now we need to catch up on events that happened while we were away //and use them to alter just the condition variables. @@ -855,18 +825,14 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in TimeOfDay_Struct tod; zone->zone_time.getEQTimeOfDay(&tod); - std::vector::iterator cur,end; - cur = spawn_events.begin(); - end = spawn_events.end(); - bool ran; - for(; cur != end; ++cur) { + for(auto cur = spawn_events.begin(); cur != spawn_events.end(); ++cur) { SpawnEvent &cevent = *cur; bool StrictCheck = false; - if(cevent.strict && - cevent.next.hour == tod.hour && - cevent.next.day == tod.day && - cevent.next.month == tod.month && + if(cevent.strict && + cevent.next.hour == tod.hour && + cevent.next.day == tod.day && + cevent.next.month == tod.month && cevent.next.year == tod.year) StrictCheck = true; @@ -874,43 +840,42 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in if(!cevent.enabled || !StrictCheck) SetCondition(zone->GetShortName(), zone->GetInstanceID(),cevent.condition_id,0); - if(cevent.enabled) - { - //watch for special case of all 0s, which means to reset next to now - if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) { - _log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id); - memcpy(&cevent.next, &tod, sizeof(cevent.next)); - //add one period - EQTime::AddMinutes(cevent.period, &cevent.next); - //save it in the db. - UpdateDBEvent(cevent); - continue; //were done with this event. - } + if(!cevent.enabled) + continue; - ran = false; - while(EQTime::IsTimeBefore(&tod, &cevent.next)) { - _log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id); - //this event has been triggered. - //execute the event - if(!cevent.strict || StrictCheck) - ExecEvent(cevent, false); - - //add the period of the event to the trigger time - EQTime::AddMinutes(cevent.period, &cevent.next); - ran = true; - } - //only write it out if the event actually ran - if(ran) { - //save the event in the DB - UpdateDBEvent(cevent); - } - } + //watch for special case of all 0s, which means to reset next to now + if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) { + _log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id); + memcpy(&cevent.next, &tod, sizeof(cevent.next)); + //add one period + EQTime::AddMinutes(cevent.period, &cevent.next); + //save it in the db. + UpdateDBEvent(cevent); + continue; //were done with this event. + } + + bool ran = false; + while(EQTime::IsTimeBefore(&tod, &cevent.next)) { + _log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id); + //this event has been triggered. + //execute the event + if(!cevent.strict || StrictCheck) + ExecEvent(cevent, false); + + //add the period of the event to the trigger time + EQTime::AddMinutes(cevent.period, &cevent.next); + ran = true; + } + + //only write it out if the event actually ran + if(ran) + UpdateDBEvent(cevent); //save the event in the DB } //now our event timers are all up to date, find our closest event. FindNearestEvent(); - return(true); + return true; } void SpawnConditionManager::FindNearestEvent() { @@ -926,7 +891,7 @@ void SpawnConditionManager::FindNearestEvent() { if(cevent.enabled) { //see if this event is before our last nearest - if(EQTime::IsTimeBefore(&next_event, &cevent.next)) + if(EQTime::IsTimeBefore(&next_event, &cevent.next)) { memcpy(&next_event, &cevent.next, sizeof(next_event)); next_id = cevent.id; @@ -1162,37 +1127,28 @@ int16 SpawnConditionManager::GetCondition(const char *zone_short, uint32 instanc } SpawnCondition &cond = condi->second; - return(cond.value); - } else { - //this is a remote spawn condition, grab it from the DB - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int len; - - int16 value; - - //load spawn conditions - SpawnCondition cond; - len = MakeAnyLenString(&query, "SELECT value FROM spawn_condition_values WHERE zone='%s' AND instance_id=%u AND id=%d", - zone_short, instance_id, condition_id); - if (database.RunQuery(query, len, errbuf, &result)) { - safe_delete_array(query); - if((row = mysql_fetch_row(result))) { - value = atoi(row[0]); - } else { - _log(SPAWNS__CONDITIONS, "Unable to load remote condition %d from zone %s in Get request.", condition_id, zone_short); - value = 0; //dunno a better thing to do... - } - mysql_free_result(result); - } else { - _log(SPAWNS__CONDITIONS, "Unable to query remote condition %d from zone %s in Get request.", condition_id, zone_short); - safe_delete_array(query); - value = 0; //dunno a better thing to do... - } - return(value); + return cond.value; } + + //this is a remote spawn condition, grab it from the DB + //load spawn conditions + std::string query = StringFormat("SELECT value FROM spawn_condition_values " + "WHERE zone = '%s' AND instance_id = %u AND id = %d", + zone_short, instance_id, condition_id); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + _log(SPAWNS__CONDITIONS, "Unable to query remote condition %d from zone %s in Get request.", condition_id, zone_short); + return 0; //dunno a better thing to do... + } + + if (results.RowCount() == 0) { + _log(SPAWNS__CONDITIONS, "Unable to load remote condition %d from zone %s in Get request.", condition_id, zone_short); + return 0; //dunno a better thing to do... + } + + auto row = results.begin(); + + return atoi(row[0]); } bool SpawnConditionManager::Check(uint16 condition, int16 min_value) { diff --git a/zone/spawngroup.cpp b/zone/spawngroup.cpp index 0044b06eb..81d6c5d51 100644 --- a/zone/spawngroup.cpp +++ b/zone/spawngroup.cpp @@ -23,13 +23,12 @@ #include #include "../common/types.h" #include "zonedb.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" extern EntityList entity_list; -SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit ) -{ +SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_limit ) { NPCType = in_NPCType; chance = in_chance; npc_spawn_limit = in_npc_spawn_limit; @@ -57,7 +56,6 @@ uint32 SpawnGroup::GetNPCType() { int npcType = 0; int totalchance = 0; - //check limits on this spawn group and npc type if(!entity_list.LimitCheckGroup(id, group_spawn_limit)) return(0); @@ -68,7 +66,6 @@ uint32 SpawnGroup::GetNPCType() { for(; cur != end; ++cur) { SpawnEntry *se = *cur; - //check limits on this spawn group and npc type if(!entity_list.LimitCheckType(se->NPCType, se->npc_spawn_limit)) continue; @@ -93,7 +90,6 @@ uint32 SpawnGroup::GetNPCType() { roll -= se->chance; } } - //CODER implement random table return npcType; } @@ -144,106 +140,92 @@ bool SpawnGroupList::RemoveSpawnGroup(uint32 in_id) { } bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnGroupList* spawn_group_list) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - // CODER new spawn code - query = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11])); - spawn_group_list->AddSpawnGroup(newSpawnGroup); - } - mysql_free_result(result); - } - else - { - std::cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, " + "spawngroup.dist, spawngroup.max_x, spawngroup.min_x, " + "spawngroup.max_y, spawngroup.min_y, spawngroup.delay, " + "spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay " + "FROM spawn2, spawngroup WHERE spawn2.spawngroupID = spawngroup.ID " + "AND spawn2.version = %u and zone = '%s'", version, zone_name); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query '%s' ", query.c_str()); return false; - } + } - query = 0; - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT DISTINCT spawnentry.spawngroupID, npcid, chance, " - "npc_types.spawn_limit AS sl " - "FROM spawnentry, spawn2, npc_types " - "WHERE spawnentry.npcID=npc_types.id AND spawnentry.spawngroupID=spawn2.spawngroupID " - "AND zone='%s'", zone_name), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0); - SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0])); - if (sg) - sg->AddSpawnEntry(newSpawnEntry); - else - std::cout << "Error in SpawngroupID: " << row[0] << std::endl; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + for (auto row = results.begin(); row != results.end(); ++row) { + SpawnGroup* newSpawnGroup = new SpawnGroup(atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), + atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), + atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11])); + spawn_group_list->AddSpawnGroup(newSpawnGroup); + } + + query = StringFormat("SELECT DISTINCT spawnentry.spawngroupID, npcid, chance, " + "npc_types.spawn_limit AS sl " + "FROM spawnentry, spawn2, npc_types " + "WHERE spawnentry.npcID=npc_types.id " + "AND spawnentry.spawngroupID = spawn2.spawngroupID " + "AND zone = '%s'", zone_name); + results = QueryDatabase(query); + if (!results.Success()) { + _log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query '%'", query.c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0); + SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0])); + + if (!sg) { + _log(ZONE__SPAWNS, "Error in LoadSpawnGroups %s ", query.c_str()); + continue; + } + + sg->AddSpawnEntry(newSpawnEntry); + } - // CODER end new spawn code return true; } bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_group_list) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - // CODER new spawn code - query = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11])); - spawn_group_list->AddSpawnGroup(newSpawnGroup); - } - mysql_free_result(result); - } - else - { - std::cerr << "Error2 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT DISTINCT(spawngroup.id), spawngroup.name, spawngroup.spawn_limit, " + "spawngroup.dist, spawngroup.max_x, spawngroup.min_x, " + "spawngroup.max_y, spawngroup.min_y, spawngroup.delay, " + "spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay " + "FROM spawngroup WHERE spawngroup.ID = '%i'", spawngroupid); + auto results = QueryDatabase(query); + if (!results.Success()) { + _log(ZONE__SPAWNS, "Error2 in PopulateZoneLists query %s", query.c_str()); + return false; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + SpawnGroup* newSpawnGroup = new SpawnGroup(atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11])); + spawn_group_list->AddSpawnGroup(newSpawnGroup); + } + + query = StringFormat("SELECT DISTINCT(spawnentry.spawngroupID), spawnentry.npcid, " + "spawnentry.chance, spawngroup.spawn_limit FROM spawnentry, spawngroup " + "WHERE spawnentry.spawngroupID = '%i' AND spawngroup.spawn_limit = '0' " + "ORDER BY chance", spawngroupid); + results = QueryDatabase(query); + if (!results.Success()) { + _log(ZONE__SPAWNS, "Error3 in PopulateZoneLists query '%s'", query.c_str()); return false; } - query = 0; - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT DISTINCT spawnentry.spawngroupID, spawnentry.npcid, spawnentry.chance, spawngroup.spawn_limit FROM spawnentry,spawngroup WHERE spawnentry.spawngroupID='%i' AND spawngroup.spawn_limit='0' ORDER by chance", spawngroupid), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0); - SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0])); - if (sg) - sg->AddSpawnEntry(newSpawnEntry); - else - std::cout << "Error in SpawngroupID: " << row[0] << std::endl; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error3 in PopulateZoneLists query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } + for(auto row = results.begin(); row != results.end(); ++row) { + SpawnEntry* newSpawnEntry = new SpawnEntry( atoi(row[1]), atoi(row[2]), row[3]?atoi(row[3]):0); + SpawnGroup *sg = spawn_group_list->GetSpawnGroup(atoi(row[0])); + if (!sg) { + _log(ZONE__SPAWNS, "Error in SpawngroupID: %s ", row[0]); + continue; + } + + sg->AddSpawnEntry(newSpawnEntry); + } - // CODER end new spawn code return true; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index bd6708a88..671968350 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -14,20 +14,18 @@ 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 "../common/debug.h" -#include -#include -#include -#include - -#include "masterentity.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include "../common/rulesys.h" - - +#include "../common/string_util.h" +#include "masterentity.h" +#include "string_ids.h" +#include +#include int Mob::GetKickDamage() { @@ -64,11 +62,9 @@ int Mob::GetBashDamage() { } void Mob::ApplySpecialAttackMod(SkillUseTypes skill, int32 &dmg, int32 &mindmg) { - int item_slot = -1; //1: Apply bonus from AC (BOOT/SHIELD/HANDS) est. 40AC=6dmg if (IsClient()){ - switch (skill){ case SkillFlyingKick: @@ -112,10 +108,8 @@ void Mob::DoSpecialAttackDamage(Mob *who, SkillUseTypes skill, int32 max_damage, if(hate_override > -1) hate = hate_override; - if(skill == SkillBash) - { - if(IsClient()) - { + if(skill == SkillBash){ + if(IsClient()){ ItemInst *item = CastToClient()->GetInv().GetItem(MainSecondary); if(item) { @@ -186,6 +180,10 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer; + /* Check to see if actually have skill */ + if (!MaxSkill(static_cast(ca_atk->m_skill))) + return; + if(GetTarget()->GetID() != ca_atk->m_target) return; //invalid packet. @@ -200,7 +198,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { SetAttackTimer(); ThrowingAttack(GetTarget()); if (CheckDoubleRangedAttack()) - RangedAttack(GetTarget(), true); + ThrowingAttack(GetTarget(), true); return; } //ranged attack (archery) @@ -274,8 +272,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { return; } - if ((ca_atk->m_atk == 100) && (ca_atk->m_skill == SkillFrenzy)) - { + if ((ca_atk->m_atk == 100) && (ca_atk->m_skill == SkillFrenzy)){ CheckIncreaseSkill(SkillFrenzy, GetTarget(), 10); int AtkRounds = 3; int skillmod = 100*GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy); @@ -311,8 +308,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { return; } - switch(GetClass()) - { + switch(GetClass()){ case BERSERKER: case WARRIOR: case RANGER: @@ -350,15 +346,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) { @@ -384,8 +388,7 @@ void Client::OPCombatAbility(const EQApplicationPacket *app) { } ReuseTime = (ReuseTime*HasteMod)/100; - if(ReuseTime > 0) - { + if(ReuseTime > 0){ p_timers.Start(pTimerCombatAbility, ReuseTime); } } @@ -403,8 +406,7 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type) SkillUseTypes skill_type; //to avoid casting... even though it "would work" uint8 itemslot = MainFeet; - switch(unchecked_type) - { + switch(unchecked_type){ case SkillFlyingKick:{ skill_type = SkillFlyingKick; max_dmg = ((GetSTR()+GetSkill(skill_type)) * RuleI(Combat, FlyingKickBonus) / 100) + 35; @@ -484,8 +486,7 @@ int Mob::MonkSpecialAttack(Mob* other, uint8 unchecked_type) else ht = ndamage = MakeRandomInt(min_dmg, max_dmg); } - else - { + else{ ht = max_dmg; } } @@ -535,7 +536,6 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { RogueBackstab(other,false,ReuseTime); if (level > 54) { - if(IsClient() && CastToClient()->CheckDoubleAttack(false)) { if(other->GetHP() > 0) @@ -619,12 +619,10 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) } // determine minimum hits - if (level < 51) - { + if (level < 51) { min_hit = (level*15/10); } - else - { + else { // Trumpcard: Replaced switch statement with formula calc. This will give minhit increases all the way to 65. min_hit = (level * ( level*5 - 105)) / 100; } @@ -636,8 +634,7 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) if(min_damage){ ndamage = min_hit; } - else - { + else { if (max_hit < min_hit) max_hit = min_hit; @@ -645,7 +642,6 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) ndamage = max_hit; else ndamage = MakeRandomInt(min_hit, max_hit); - } } } @@ -769,7 +765,7 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { } } - float range = RangeItem->Range + AmmoItem->Range + 5; //Fudge it a little, client will let you hit something at 0 0 0 when you are at 205 0 0 + float range = RangeItem->Range + AmmoItem->Range + 5.0f; //Fudge it a little, client will let you hit something at 0 0 0 when you are at 205 0 0 mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", range); range *= range; if(DistNoRootNoZ(*GetTarget()) > range) { @@ -792,170 +788,221 @@ void Client::RangedAttack(Mob* other, bool CanDoubleAttack) { return; } - SendItemAnimation(GetTarget(), AmmoItem, SkillArchery); - - DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo); + //Shoots projectile and/or applies the archery damage + DoArcheryAttackDmg(GetTarget(), RangeWeapon, Ammo,0,0,0,0,0,0, AmmoItem, ammo_slot); //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; - if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && MakeRandomInt(0,99) > ChanceAvoidConsume)){ - + if (!ChanceAvoidConsume || (ChanceAvoidConsume < 100 && MakeRandomInt(0,99) > ChanceAvoidConsume)){ DeleteItemInInventory(ammo_slot, 1, true); mlog(COMBAT__RANGED, "Consumed one arrow from slot %d", ammo_slot); } else { mlog(COMBAT__RANGED, "Endless Quiver prevented ammo consumption."); } - CheckIncreaseSkill(SkillArchery, GetTarget(), -15); - + CheckIncreaseSkill(SkillArchery, GetTarget(), -15); CommonBreakInvisible(); } -void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime) -{ - if (!CanDoSpecialAttack(other)) +void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const ItemInst* Ammo, uint16 weapon_damage, int16 chance_mod, int16 focus, int ReuseTime, + uint32 range_id, uint32 ammo_id, const Item_Struct *AmmoItem, int AmmoSlot, float speed) { + + if ((other == nullptr || + ((IsClient() && CastToClient()->dead) || + (other->IsClient() && other->CastToClient()->dead)) || + HasDied() || + (!IsAttackAllowed(other)) || + (other->GetInvul() || + other->GetSpecialAbility(IMMUNE_MELEE)))) + { return; + } - if (!other->CheckHitChance(this, SkillArchery, MainPrimary, chance_mod)) { + const ItemInst* _RangeWeapon = nullptr; + const ItemInst* _Ammo = nullptr; + const Item_Struct* ammo_lost = nullptr; + + /* + If LaunchProjectile is false this function will do archery damage on target, + otherwise it will shoot the projectile at the target, once the projectile hits target + this function is then run again to do the damage portion + */ + bool LaunchProjectile = false; + bool ProjectileMiss = false; + + if (RuleB(Combat, ProjectileDmgOnImpact)){ + + if (AmmoItem) + LaunchProjectile = true; + else{ + /* + Item sync check on projectile landing. + Weapon damage is already calculated so this only affects procs! + Ammo proc check will use database to find proc if you used up your last ammo. + If you change range item mid projectile flight, you loose your chance to proc from bow (Deal with it!). + */ + + if (!RangeWeapon && !Ammo && range_id && ammo_id){ + + if (weapon_damage == 0) + ProjectileMiss = true; //This indicates that MISS was originally calculated. + + if (IsClient()){ + + _RangeWeapon = CastToClient()->m_inv[MainRange]; + if (_RangeWeapon && !_RangeWeapon->GetItem() && _RangeWeapon->GetItem()->ID == range_id) + RangeWeapon = _RangeWeapon; + + _Ammo = CastToClient()->m_inv[AmmoSlot]; + if (_Ammo && _Ammo->GetItem() && _Ammo->GetItem()->ID == ammo_id) + Ammo = _Ammo; + else + ammo_lost = database.GetItem(ammo_id); + } + } + } + } + else if (AmmoItem) + SendItemAnimation(other, AmmoItem, SkillArchery); + + if (ProjectileMiss || !other->CheckHitChance(this, SkillArchery, MainPrimary, chance_mod)) { mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); - other->Damage(this, 0, SPELL_UNKNOWN, SkillArchery); + + if (LaunchProjectile){ + TryProjectileAttack(other, AmmoItem, SkillArchery, 0, RangeWeapon, Ammo, AmmoSlot, speed); + return; + } + else + other->Damage(this, 0, SPELL_UNKNOWN, SkillArchery); } else { mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName()); + bool HeadShot = false; + uint32 HeadShot_Dmg = TryHeadShot(other, SkillArchery); + if (HeadShot_Dmg) + HeadShot = true; - bool HeadShot = false; - uint32 HeadShot_Dmg = TryHeadShot(other, SkillArchery); - if (HeadShot_Dmg) - HeadShot = true; + int32 hate = 0; + int32 TotalDmg = 0; + int16 WDmg = 0; + int16 ADmg = 0; + if (!weapon_damage){ + WDmg = GetWeaponDamage(other, RangeWeapon); + ADmg = GetWeaponDamage(other, Ammo); + } + else + WDmg = weapon_damage; - int32 TotalDmg = 0; - int16 WDmg = 0; - int16 ADmg = 0; - if (!weapon_damage){ - WDmg = GetWeaponDamage(other, RangeWeapon); - ADmg = GetWeaponDamage(other, Ammo); - } - else - WDmg = weapon_damage; + if (LaunchProjectile){//1: Shoot the Projectile once we calculate weapon damage. + TryProjectileAttack(other, AmmoItem, SkillArchery, WDmg, RangeWeapon, Ammo, AmmoSlot, speed); + return; + } - if (focus) //From FcBaseEffects - WDmg += WDmg*focus/100; + if (focus) //From FcBaseEffects + WDmg += WDmg*focus/100; - if((WDmg > 0) || (ADmg > 0)) - { - if(WDmg < 0) - WDmg = 0; - if(ADmg < 0) - ADmg = 0; - uint32 MaxDmg = (RuleR(Combat, ArcheryBaseDamageBonus)*(WDmg+ADmg)*GetDamageTable(SkillArchery)) / 100; - int32 hate = ((WDmg+ADmg)); - - if (HeadShot) - MaxDmg = HeadShot_Dmg; - - uint16 bonusArcheryDamageModifier = aabonuses.ArcheryDamageModifier + itembonuses.ArcheryDamageModifier + spellbonuses.ArcheryDamageModifier; - - MaxDmg += MaxDmg*bonusArcheryDamageModifier / 100; - - mlog(COMBAT__RANGED, "Bow DMG %d, Arrow DMG %d, Max Damage %d.", WDmg, ADmg, MaxDmg); - - bool dobonus = false; - if(GetClass() == RANGER && GetLevel() > 50) - { - int bonuschance = RuleI(Combat, ArcheryBonusChance); - - bonuschance = mod_archery_bonus_chance(bonuschance, RangeWeapon); - - if( !RuleB(Combat, UseArcheryBonusRoll) || (MakeRandomInt(1, 100) < bonuschance) ) - { - if(RuleB(Combat, ArcheryBonusRequiresStationary)) - { - if(other->IsNPC() && !other->IsMoving() && !other->IsRooted()) - { - dobonus = true; - } - } - else - { - dobonus = true; - } - } - - if(dobonus) - { - MaxDmg *= (float)2; - hate *= (float)2; - MaxDmg = mod_archery_bonus_damage(MaxDmg, RangeWeapon); - - mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg); - Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE); - } - } - - if (MaxDmg == 0) - MaxDmg = 1; - - if(RuleB(Combat, UseIntervalAC)) - TotalDmg = MaxDmg; - else - TotalDmg = MakeRandomInt(1, MaxDmg); - - int minDmg = 1; - if(GetLevel() > 25){ - //twice, for ammo and weapon - TotalDmg += (2*((GetLevel()-25)/3)); - minDmg += (2*((GetLevel()-25)/3)); - minDmg += minDmg * GetMeleeMinDamageMod_SE(SkillArchery) / 100; - hate += (2*((GetLevel()-25)/3)); - } - - if (!HeadShot) - other->AvoidDamage(this, TotalDmg, false); - - other->MeleeMitigation(this, TotalDmg, minDmg); - if(TotalDmg > 0) - { - ApplyMeleeDamageBonus(SkillArchery, TotalDmg); - TotalDmg += other->GetFcDamageAmtIncoming(this, 0, true, SkillArchery); - TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(SkillArchery) / 100) + GetSkillDmgAmt(SkillArchery); - - TotalDmg = mod_archery_damage(TotalDmg, dobonus, RangeWeapon); - - TryCriticalHit(other, SkillArchery, TotalDmg); - other->AddToHateList(this, hate, 0, false); - CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess); - } - } - else - TotalDmg = -5; + if((WDmg > 0) || (ADmg > 0)) { + if(WDmg < 0) + WDmg = 0; + if(ADmg < 0) + ADmg = 0; + uint32 MaxDmg = (RuleR(Combat, ArcheryBaseDamageBonus)*(WDmg+ADmg)*GetDamageTable(SkillArchery)) / 100; + hate = ((WDmg+ADmg)); if (HeadShot) - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName()); - - other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery); - - if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && other && !other->HasDied()){ - if (ReuseTime) - TrySkillProc(other, SkillArchery, ReuseTime); - else - TrySkillProc(other, SkillArchery, 0, true, MainRange); + MaxDmg = HeadShot_Dmg; + + uint16 bonusArcheryDamageModifier = aabonuses.ArcheryDamageModifier + itembonuses.ArcheryDamageModifier + spellbonuses.ArcheryDamageModifier; + + MaxDmg += MaxDmg*bonusArcheryDamageModifier / 100; + + mlog(COMBAT__RANGED, "Bow DMG %d, Arrow DMG %d, Max Damage %d.", WDmg, ADmg, MaxDmg); + + bool dobonus = false; + if(GetClass() == RANGER && GetLevel() > 50){ + + int bonuschance = RuleI(Combat, ArcheryBonusChance); + bonuschance = mod_archery_bonus_chance(bonuschance, RangeWeapon); + + if( !RuleB(Combat, UseArcheryBonusRoll) || (MakeRandomInt(1, 100) < bonuschance)){ + if(RuleB(Combat, ArcheryBonusRequiresStationary)){ + if(other->IsNPC() && !other->IsMoving() && !other->IsRooted()) + dobonus = true; + } + else + dobonus = true; + } + + if(dobonus){ + MaxDmg *= 2; + hate *= 2; + MaxDmg = mod_archery_bonus_damage(MaxDmg, RangeWeapon); + + mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg); + Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE); + } } + + if (MaxDmg == 0) + MaxDmg = 1; + + if(RuleB(Combat, UseIntervalAC)) + TotalDmg = MaxDmg; + else + TotalDmg = MakeRandomInt(1, MaxDmg); + + int minDmg = 1; + if(GetLevel() > 25){ + //twice, for ammo and weapon + TotalDmg += (2*((GetLevel()-25)/3)); + minDmg += (2*((GetLevel()-25)/3)); + minDmg += minDmg * GetMeleeMinDamageMod_SE(SkillArchery) / 100; + hate += (2*((GetLevel()-25)/3)); + } + + if (!HeadShot) + other->AvoidDamage(this, TotalDmg, false); + + other->MeleeMitigation(this, TotalDmg, minDmg); + if(TotalDmg > 0){ + CommonOutgoingHitSuccess(other, TotalDmg, SkillArchery); + TotalDmg = mod_archery_damage(TotalDmg, dobonus, RangeWeapon); + } + } + else + TotalDmg = -5; + + if (HeadShot) + entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FATAL_BOW_SHOT, GetName()); + + other->AddToHateList(this, hate, 0, false); + other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillArchery); + + //Skill Proc Success + if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()){ + if (ReuseTime) + TrySkillProc(other, SkillArchery, ReuseTime); + else + TrySkillProc(other, SkillArchery, 0, true, MainRange); + } } - //try proc on hits and misses - if((RangeWeapon != nullptr) && GetTarget() && other && !other->HasDied()) - { + if (LaunchProjectile) + return;//Shouldn't reach this point, but just in case. + + //Weapon Proc + if(!RangeWeapon && other && !other->HasDied()) TryWeaponProc(RangeWeapon, other, MainRange); - } - //Arrow procs because why not? - if((Ammo != NULL) && GetTarget() && other && !other->HasDied()) - { - TryWeaponProc(Ammo, other, MainRange); - } + //Ammo Proc + if (ammo_lost) + TryWeaponProc(nullptr, ammo_lost, other, MainRange); + else if(Ammo && other && !other->HasDied()) + TryWeaponProc(Ammo, other, MainRange); - if (HasSkillProcs() && GetTarget() && other && !other->HasDied()){ + //Skill Proc + if (HasSkillProcs() && other && !other->HasDied()){ if (ReuseTime) TrySkillProc(other, SkillArchery, ReuseTime); else @@ -963,128 +1010,251 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item } } +bool Mob::TryProjectileAttack(Mob* other, const Item_Struct *item, SkillUseTypes skillInUse, uint16 weapon_dmg, const ItemInst* RangeWeapon, const ItemInst* Ammo, int AmmoSlot, float speed){ + + if (!other) + return false; + + int slot = -1; + + //Make sure there is an avialable slot. + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + if (ProjectileAtk[i].target_id == 0){ + slot = i; + break; + } + } + + if (slot < 0) + return false; + + float speed_mod = speed * 0.45f; + + float distance = other->CalculateDistance(GetX(), GetY(), GetZ()); + float hit = 60.0f + (distance / speed_mod); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + + ProjectileAtk[slot].increment = 1; + ProjectileAtk[slot].hit_increment = static_cast(hit); //This projected hit time if target does NOT MOVE + ProjectileAtk[slot].target_id = other->GetID(); + ProjectileAtk[slot].wpn_dmg = weapon_dmg; + ProjectileAtk[slot].origin_x = GetX(); + ProjectileAtk[slot].origin_y = GetY(); + ProjectileAtk[slot].origin_z = GetZ(); + + if (RangeWeapon && RangeWeapon->GetItem()) + ProjectileAtk[slot].ranged_id = RangeWeapon->GetItem()->ID; + + if (Ammo && Ammo->GetItem()) + ProjectileAtk[slot].ammo_id = Ammo->GetItem()->ID; + + ProjectileAtk[slot].ammo_slot = 0; + ProjectileAtk[slot].skill = skillInUse; + ProjectileAtk[slot].speed_mod = speed_mod; + + SetProjectileAttack(true); + + if(item) + SendItemAnimation(other, item, skillInUse, speed); + else if (IsNPC()) + ProjectileAnimation(other, 0,false,speed,0,0,0,CastToNPC()->GetAmmoIDfile(),skillInUse); + + return true; +} + + +void Mob::ProjectileAttack() +{ + if (!HasProjectileAttack()) + return;; + + Mob* target = nullptr; + bool disable = true; + + for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { + + if (ProjectileAtk[i].increment == 0){ + continue; + } + + disable = false; + Mob* target = entity_list.GetMobID(ProjectileAtk[i].target_id); + + if (target && target->IsMoving()){ //Only recalculate hit increment if target moving + //Due to frequency that we need to check increment the targets position variables may not be updated even if moving. Do a simple check before calculating distance. + if (ProjectileAtk[i].tlast_x != target->GetX() || ProjectileAtk[i].tlast_y != target->GetY()){ + ProjectileAtk[i].tlast_x = target->GetX(); + ProjectileAtk[i].tlast_y = target->GetY(); + float distance = target->CalculateDistance(ProjectileAtk[i].origin_x, ProjectileAtk[i].origin_y, ProjectileAtk[i].origin_z); + float hit = 60.0f + (distance / ProjectileAtk[i].speed_mod); //Calcuation: 60 = Animation Lag, 1.8 = Speed modifier for speed of (4) + ProjectileAtk[i].hit_increment = static_cast(hit); + } + } + + if (ProjectileAtk[i].hit_increment <= ProjectileAtk[i].increment){ + + if (target){ + if (ProjectileAtk[i].skill == SkillArchery) + DoArcheryAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0,ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_id, nullptr, ProjectileAtk[i].ammo_slot); + else if (ProjectileAtk[i].skill == SkillThrowing) + DoThrowingAttackDmg(target, nullptr, nullptr,ProjectileAtk[i].wpn_dmg,0,0,0, ProjectileAtk[i].ranged_id, ProjectileAtk[i].ammo_slot); + else if (ProjectileAtk[i].skill == SkillConjuration && IsValidSpell(ProjectileAtk[i].wpn_dmg)) + SpellOnTarget(ProjectileAtk[i].wpn_dmg, target, false, true, spells[ProjectileAtk[i].wpn_dmg].ResistDiff, true); + } + + ProjectileAtk[i].increment = 0; + ProjectileAtk[i].target_id = 0; + ProjectileAtk[i].wpn_dmg = 0; + ProjectileAtk[i].origin_x = 0.0f; + ProjectileAtk[i].origin_y = 0.0f; + ProjectileAtk[i].origin_z = 0.0f; + ProjectileAtk[i].tlast_x = 0.0f; + ProjectileAtk[i].tlast_y = 0.0f; + ProjectileAtk[i].ranged_id = 0; + ProjectileAtk[i].ammo_id = 0; + ProjectileAtk[i].ammo_slot = 0; + ProjectileAtk[i].skill = 0; + ProjectileAtk[i].speed_mod = 0.0f; + } + + else { + ProjectileAtk[i].increment++; + } + } + + if (disable) + SetProjectileAttack(false); +} + void NPC::RangedAttack(Mob* other) { + + if (!other) + return; //make sure the attack and ranged timers are up //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow - if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())) - { + if((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())){ mlog(COMBAT__RANGED, "Archery canceled. Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; } - //if we have SPECATK_RANGED_ATK set then we range attack without weapon or ammo - const Item_Struct* weapon = nullptr; - const Item_Struct* ammo = nullptr; - if(!GetSpecialAbility(SPECATK_RANGED_ATK)) - { - //find our bow and ammo return if we can't find them... + if(!CheckLosFN(other)) return; - } - int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0); //Min Range of NPC attack - int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack + int attacks = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0); + attacks = attacks > 0 ? attacks : 1; + for(int i = 0; i < attacks; ++i) { - float min_range = static_cast(RuleI(Combat, MinRangedAttackDist)); - float max_range = 250; // needs to be longer than 200(most spells) - - if (sa_max_range) - max_range = static_cast(sa_max_range); - - if (sa_min_range) - min_range = static_cast(sa_min_range); - - mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", max_range); - max_range *= max_range; - if(DistNoRootNoZ(*other) > max_range) { - mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*other), max_range); - //target is out of range, client does a message - return; - } - else if(DistNoRootNoZ(*other) < (min_range * min_range)) - return; - - - if(!other || !IsAttackAllowed(other) || - IsCasting() || - DivineAura() || - IsStunned() || - IsFeared() || - IsMezzed() || - (GetAppearance() == eaDead)){ - return; - } - - SkillUseTypes skillinuse = SkillArchery; - skillinuse = static_cast(GetRangedSkill()); - - if(!ammo && !GetAmmoIDfile()) - ammo = database.GetItem(8005); - - if(ammo) - SendItemAnimation(other, ammo, SkillArchery); - else - ProjectileAnimation(other, 0,false,0,0,0,0,GetAmmoIDfile(),skillinuse); - - FaceTarget(other); - - if (!other->CheckHitChance(this, skillinuse, MainRange, GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2))) - { - mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); - other->Damage(this, 0, SPELL_UNKNOWN, skillinuse); - } - else - { - int16 WDmg = GetWeaponDamage(other, weapon); - int16 ADmg = GetWeaponDamage(other, ammo); - int32 TotalDmg = 0; - if(WDmg > 0 || ADmg > 0) + //if we have SPECATK_RANGED_ATK set then we range attack without weapon or ammo + const Item_Struct* weapon = nullptr; + const Item_Struct* ammo = nullptr; + if(!GetSpecialAbility(SPECATK_RANGED_ATK)) { - mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName()); - - int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types - int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier); - - if(RuleB(Combat, UseIntervalAC)) - TotalDmg = MaxDmg; - else - TotalDmg = MakeRandomInt(MinDmg, MaxDmg); - - TotalDmg += TotalDmg * GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3) / 100; //Damage modifier - - other->AvoidDamage(this, TotalDmg, false); - other->MeleeMitigation(this, TotalDmg, MinDmg); - if (TotalDmg > 0) - CommonOutgoingHitSuccess(other, TotalDmg, skillinuse); + //find our bow and ammo return if we can't find them... + return; } + int sa_min_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 4); //Min Range of NPC attack + int sa_max_range = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 1); //Max Range of NPC attack + + float min_range = static_cast(RuleI(Combat, MinRangedAttackDist)); + float max_range = 250; // needs to be longer than 200(most spells) + + if (sa_max_range) + max_range = static_cast(sa_max_range); + + if (sa_min_range) + min_range = static_cast(sa_min_range); + + mlog(COMBAT__RANGED, "Calculated bow range to be %.1f", max_range); + max_range *= max_range; + if(DistNoRootNoZ(*other) > max_range) { + mlog(COMBAT__RANGED, "Ranged attack out of range...%.2f vs %.2f", DistNoRootNoZ(*other), max_range); + //target is out of range, client does a message + return; + } + else if(DistNoRootNoZ(*other) < (min_range * min_range)) + return; + + + if(!other || !IsAttackAllowed(other) || + IsCasting() || + DivineAura() || + IsStunned() || + IsFeared() || + IsMezzed() || + (GetAppearance() == eaDead)){ + return; + } + + SkillUseTypes skillinuse = SkillArchery; + skillinuse = static_cast(GetRangedSkill()); + + if(!ammo && !GetAmmoIDfile()) + ammo = database.GetItem(8005); + + if(ammo) + SendItemAnimation(other, ammo, SkillArchery); + else + ProjectileAnimation(other, 0,false,0,0,0,0,GetAmmoIDfile(),skillinuse); + + FaceTarget(other); + + if (!other->CheckHitChance(this, skillinuse, MainRange, GetSpecialAbilityParam(SPECATK_RANGED_ATK, 2))) + { + mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); + other->Damage(this, 0, SPELL_UNKNOWN, skillinuse); + } else - TotalDmg = -5; + { + int16 WDmg = GetWeaponDamage(other, weapon); + int16 ADmg = GetWeaponDamage(other, ammo); + int32 TotalDmg = 0; + if(WDmg > 0 || ADmg > 0) + { + mlog(COMBAT__RANGED, "Ranged attack hit %s.", other->GetName()); + + int32 MaxDmg = max_dmg * RuleR(Combat, ArcheryNPCMultiplier); // should add a field to npc_types + int32 MinDmg = min_dmg * RuleR(Combat, ArcheryNPCMultiplier); - if (TotalDmg > 0) - other->AddToHateList(this, TotalDmg, 0, false); - else - other->AddToHateList(this, 0, 0, false); + if(RuleB(Combat, UseIntervalAC)) + TotalDmg = MaxDmg; + else + TotalDmg = MakeRandomInt(MinDmg, MaxDmg); - other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillinuse); + TotalDmg += TotalDmg * GetSpecialAbilityParam(SPECATK_RANGED_ATK, 3) / 100; //Damage modifier + + other->AvoidDamage(this, TotalDmg, false); + other->MeleeMitigation(this, TotalDmg, MinDmg); + if (TotalDmg > 0) + CommonOutgoingHitSuccess(other, TotalDmg, skillinuse); + } - if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && !other->HasDied()) - TrySkillProc(other, skillinuse, 0, true, MainRange); + else + TotalDmg = -5; + + if (TotalDmg > 0) + other->AddToHateList(this, TotalDmg, 0, false); + else + other->AddToHateList(this, 0, 0, false); + + other->Damage(this, TotalDmg, SPELL_UNKNOWN, skillinuse); + + if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && !other->HasDied()) + TrySkillProc(other, skillinuse, 0, true, MainRange); + } + + //try proc on hits and misses + if(other && !other->HasDied()) + TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange); + + if (HasSkillProcs() && other && !other->HasDied()) + TrySkillProc(other, skillinuse, 0, false, MainRange); + + CommonBreakInvisible(); } - - //try proc on hits and misses - if(other && !other->HasDied()) - TrySpellProc(nullptr, (const Item_Struct*)nullptr, other, MainRange); - - if (HasSkillProcs() && other && !other->HasDied()) - TrySkillProc(other, skillinuse, 0, false, MainRange); - - CommonBreakInvisible(); } -uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) -{ - +uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) { uint16 MaxDmg = (((2 * wDmg) * GetDamageTable(SkillThrowing)) / 100); if (MaxDmg == 0) @@ -1096,8 +1266,7 @@ uint16 Mob::GetThrownDamage(int16 wDmg, int32& TotalDmg, int& minDmg) TotalDmg = MakeRandomInt(1, MaxDmg); minDmg = 1; - if(GetLevel() > 25) - { + if(GetLevel() > 25){ TotalDmg += ((GetLevel()-25)/3); minDmg += ((GetLevel()-25)/3); minDmg += minDmg * GetMeleeMinDamageMod_SE(SkillThrowing) / 100; @@ -1183,33 +1352,87 @@ void Client::ThrowingAttack(Mob* other, bool CanDoubleAttack) { //old was 51 (GetAppearance() == eaDead)){ return; } - //send item animation, also does the throw animation - SendItemAnimation(GetTarget(), item, SkillThrowing); DoThrowingAttackDmg(GetTarget(), RangeWeapon, item); //consume ammo DeleteItemInInventory(ammo_slot, 1, true); - CheckIncreaseSkill(SkillThrowing, GetTarget()); - - CommonBreakInvisible(); + CheckIncreaseSkill(SkillThrowing, GetTarget()); + CommonBreakInvisible(); } -void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* item, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime) +void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item_Struct* AmmoItem, uint16 weapon_damage, int16 chance_mod,int16 focus, int ReuseTime, uint32 range_id, int AmmoSlot, float speed) { - if (!CanDoSpecialAttack(other)) + if ((other == nullptr || + ((IsClient() && CastToClient()->dead) || + (other->IsClient() && other->CastToClient()->dead)) || + HasDied() || + (!IsAttackAllowed(other)) || + (other->GetInvul() || + other->GetSpecialAbility(IMMUNE_MELEE)))) + { return; + } - if (!other->CheckHitChance(this, SkillThrowing, MainPrimary, chance_mod)){ + const ItemInst* _RangeWeapon = nullptr; + const Item_Struct* ammo_lost = nullptr; + + /* + If LaunchProjectile is false this function will do archery damage on target, + otherwise it will shoot the projectile at the target, once the projectile hits target + this function is then run again to do the damage portion + */ + bool LaunchProjectile = false; + bool ProjectileMiss = false; + + if (RuleB(Combat, ProjectileDmgOnImpact)){ + + if (AmmoItem) + LaunchProjectile = true; + else{ + if (!RangeWeapon && range_id){ + + if (weapon_damage == 0) + ProjectileMiss = true; //This indicates that MISS was originally calculated. + + if (IsClient()){ + + _RangeWeapon = CastToClient()->m_inv[AmmoSlot]; + if (_RangeWeapon && _RangeWeapon->GetItem() && _RangeWeapon->GetItem()->ID != range_id) + RangeWeapon = _RangeWeapon; + else + ammo_lost = database.GetItem(range_id); + } + } + } + } + else if (AmmoItem) + SendItemAnimation(other, AmmoItem, SkillThrowing); + + if (ProjectileMiss || !other->CheckHitChance(this, SkillThrowing, MainPrimary, chance_mod)){ mlog(COMBAT__RANGED, "Ranged attack missed %s.", other->GetName()); - other->Damage(this, 0, SPELL_UNKNOWN, SkillThrowing); + if (LaunchProjectile){ + TryProjectileAttack(other, AmmoItem, SkillThrowing, 0, RangeWeapon, nullptr, AmmoSlot, speed); + return; + } + else + other->Damage(this, 0, SPELL_UNKNOWN, SkillThrowing); } else { mlog(COMBAT__RANGED, "Throwing attack hit %s.", other->GetName()); int16 WDmg = 0; - if (!weapon_damage && item != nullptr) - WDmg = GetWeaponDamage(other, item); + if (!weapon_damage){ + if (IsClient() && RangeWeapon) + WDmg = GetWeaponDamage(other, RangeWeapon); + else if (AmmoItem) + WDmg = GetWeaponDamage(other, AmmoItem); + + if (LaunchProjectile){ + TryProjectileAttack(other, AmmoItem, SkillThrowing, WDmg, RangeWeapon, nullptr, AmmoSlot, speed); + return; + } + } else WDmg = weapon_damage; @@ -1222,8 +1445,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite if (GetClass() == ROGUE && (BehindMob(other, GetX(), GetY()))) Assassinate_Dmg = TryAssassinate(other, SkillThrowing, ranged_timer.GetDuration()); - if(WDmg > 0) - { + if(WDmg > 0){ int minDmg = 1; uint16 MaxDmg = GetThrownDamage(WDmg, TotalDmg, minDmg); @@ -1247,7 +1469,7 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite other->AddToHateList(this, 2*WDmg, 0, false); other->Damage(this, TotalDmg, SPELL_UNKNOWN, SkillThrowing); - if (TotalDmg > 0 && HasSkillProcSuccess() && GetTarget() && other && !other->HasDied()){ + if (TotalDmg > 0 && HasSkillProcSuccess() && other && !other->HasDied()){ if (ReuseTime) TrySkillProc(other, SkillThrowing, ReuseTime); else @@ -1255,19 +1477,24 @@ void Mob::DoThrowingAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Ite } } - if((RangeWeapon != nullptr) && GetTarget() && other && (other->GetHP() > -10)) + if (LaunchProjectile) + return; + + //Throwing item Proc + if (ammo_lost) + TryWeaponProc(nullptr, ammo_lost, other, MainRange); + else if(RangeWeapon && other && !other->HasDied()) TryWeaponProc(RangeWeapon, other, MainRange); - if (HasSkillProcs() && GetTarget() && other && !other->HasDied()){ + if (HasSkillProcs() && other && !other->HasDied()){ if (ReuseTime) TrySkillProc(other, SkillThrowing, ReuseTime); else TrySkillProc(other, SkillThrowing, 0, false, MainRange); } - } -void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse) { +void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skillInUse, float velocity) { EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct)); Arrow_Struct *as = (Arrow_Struct *) outapp->pBuffer; as->type = 1; @@ -1295,7 +1522,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil Arc causes the object to form an arc in motion. A value too high will */ - as->velocity = 4.0; + as->velocity = velocity; //these angle and tilt used together seem to make the arrow/knife throw as straight as I can make it @@ -1313,8 +1540,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil safe_delete(outapp); } -void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) { - +void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile, SkillUseTypes skillInUse) { if (!to) return; @@ -1426,13 +1652,7 @@ void NPC::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 @@ -1448,17 +1668,11 @@ void NPC::DoClassAttacks(Mob *target) { break; case MONK: case MONKGM: { uint8 satype = SkillKick; - if(level > 29) { - satype = SkillFlyingKick; - } else if(level > 24) { - satype = SkillDragonPunch; - } else if(level > 19) { - satype = SkillEagleStrike; - } else if(level > 9) { - satype = SkillTigerClaw; - } else if(level > 4) { - satype = SkillRoundKick; - } + if(level > 29) { satype = SkillFlyingKick; } + else if(level > 24) { satype = SkillDragonPunch; } + else if(level > 19) { satype = SkillEagleStrike; } + else if(level > 9) { satype = SkillTigerClaw; } + else if(level > 4) { satype = SkillRoundKick; } reuse = MonkSpecialAttack(target, satype); reuse *= 1000; @@ -1467,8 +1681,7 @@ void NPC::DoClassAttacks(Mob *target) { } case WARRIOR: case WARRIORGM:{ if(level >= RuleI(Combat, NPCBashKickLevel)){ - if(MakeRandomInt(0, 100) > 25) //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. - { + if(MakeRandomInt(0, 100) > 25){ //tested on live, warrior mobs both kick and bash, kick about 75% of the time, casting doesn't seem to make a difference. DoAnim(animKick); int32 dmg = 0; @@ -1485,12 +1698,11 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = KickReuseTime * 1000; + reuse = (KickReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); did_attack = true; } - else - { + else { DoAnim(animTailRake); int32 dmg = 0; @@ -1506,15 +1718,14 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = BashReuseTime * 1000; + reuse = (BashReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); did_attack = true; } } break; } - case BERSERKER: case BERSERKERGM: - { + case BERSERKER: case BERSERKERGM:{ int AtkRounds = 3; int32 max_dmg = 26 + ((GetLevel()-6) * 2); int32 min_dmg = 0; @@ -1530,8 +1741,7 @@ void NPC::DoClassAttacks(Mob *target) { reuse = FrenzyReuseTime * 1000; - while(AtkRounds > 0) { - + while(AtkRounds > 0) { if (GetTarget() && (AtkRounds == 1 || MakeRandomInt(0,100) < 75)){ DoSpecialAttackDamage(GetTarget(), SkillFrenzy, max_dmg, min_dmg, -1 , reuse, true); } @@ -1561,7 +1771,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = KickReuseTime * 1000; + reuse = (KickReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillKick, dmg, 1, -1, reuse); did_attack = true; } @@ -1569,8 +1779,7 @@ void NPC::DoClassAttacks(Mob *target) { } case CLERIC: case CLERICGM: //clerics can bash too. case SHADOWKNIGHT: case SHADOWKNIGHTGM: - case PALADIN: case PALADINGM: - { + case PALADIN: case PALADINGM:{ if(level >= RuleI(Combat, NPCBashKickLevel)){ DoAnim(animTailRake); int32 dmg = 0; @@ -1587,7 +1796,7 @@ void NPC::DoClassAttacks(Mob *target) { } } - reuse = BashReuseTime * 1000; + reuse = (BashReuseTime + 3) * 1000; DoSpecialAttackDamage(target, SkillBash, dmg, 1, -1, reuse); did_attack = true; } @@ -1595,7 +1804,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) @@ -1610,8 +1819,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) return; //check range for all these abilities, they are all close combat stuff - if(!CombatRange(ca_target)) - { + if(!CombatRange(ca_target)){ return; } @@ -1620,23 +1828,14 @@ 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){ - - switch(GetClass()) - { + switch(GetClass()){ case WARRIOR: case RANGER: case BEASTLORD: @@ -1687,14 +1886,11 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) if(skill_to_use == -1) return; - if(skill_to_use == SkillBash) - { - if (ca_target!=this) - { + if(skill_to_use == SkillBash) { + if (ca_target!=this) { DoAnim(animTailRake); - if(GetWeaponDamage(ca_target, GetInv().GetItem(MainSecondary)) <= 0 && - GetWeaponDamage(ca_target, GetInv().GetItem(MainShoulders)) <= 0){ + if(GetWeaponDamage(ca_target, GetInv().GetItem(MainSecondary)) <= 0 && GetWeaponDamage(ca_target, GetInv().GetItem(MainShoulders)) <= 0){ dmg = -5; } else{ @@ -1710,21 +1906,18 @@ 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); - if(ReuseTime > 0 && !IsRiposte) - { + if(ReuseTime > 0 && !IsRiposte) { p_timers.Start(pTimerCombatAbility, ReuseTime); } } return; } - if(skill_to_use == SkillFrenzy) - { + if(skill_to_use == SkillFrenzy){ CheckIncreaseSkill(SkillFrenzy, GetTarget(), 10); int AtkRounds = 3; int skillmod = 100*GetSkill(SkillFrenzy)/MaxSkill(SkillFrenzy); @@ -1740,8 +1933,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) { @@ -1758,10 +1950,8 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) return; } - if(skill_to_use == SkillKick) - { - if(ca_target!=this) - { + if(skill_to_use == SkillKick){ + if(ca_target!=this){ DoAnim(animKick); if(GetWeaponDamage(ca_target, GetInv().GetItem(MainFeet)) <= 0){ @@ -1785,12 +1975,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) } } - if(skill_to_use == SkillFlyingKick || - skill_to_use == SkillDragonPunch || - skill_to_use == SkillEagleStrike || - skill_to_use == SkillTigerClaw || - skill_to_use == SkillRoundKick) - { + if(skill_to_use == SkillFlyingKick || skill_to_use == SkillDragonPunch || skill_to_use == SkillEagleStrike || skill_to_use == SkillTigerClaw || skill_to_use == SkillRoundKick) { ReuseTime = MonkSpecialAttack(ca_target, skill_to_use) - 1; MonkSpecialAttack(ca_target, skill_to_use); @@ -1798,25 +1983,26 @@ 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--; + } } } } - if(skill_to_use == SkillBackstab) - { + if(skill_to_use == SkillBackstab){ ReuseTime = BackstabReuseTime-1; if (IsRiposte) @@ -1825,9 +2011,8 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte) TryBackstab(ca_target,ReuseTime); } - ReuseTime = (ReuseTime*HasteMod)/100; - if(ReuseTime > 0 && !IsRiposte) - { + ReuseTime = ReuseTime / HasteMod; + if(ReuseTime > 0 && !IsRiposte){ p_timers.Start(pTimerCombatAbility, ReuseTime); } } @@ -1848,7 +2033,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { Mob *hate_top = who->GetHateMost(); - float level_difference = GetLevel() - who->GetLevel(); + int level_difference = GetLevel() - who->GetLevel(); bool Success = false; //Support for how taunt worked pre 2000 on LIVE - Can not taunt NPC over your level. @@ -1869,13 +2054,13 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { else { if (level_difference < 0){ - tauntchance += level_difference*3; + tauntchance += static_cast(level_difference)*3.0f; if (tauntchance < 20) tauntchance = 20.0f; } else { - tauntchance += level_difference*5; + tauntchance += static_cast(level_difference)*5.0f; if (tauntchance > 65) tauntchance = 65.0f; } @@ -1894,8 +2079,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { tauntchance /= 100.0f; - if (tauntchance > MakeRandomFloat(0, 1)) { - + if (tauntchance > MakeRandomFloat(0, 1)) { if (hate_top && hate_top != this){ newhate = (who->GetNPCHate(hate_top) - who->GetNPCHate(this)) + 1; who->CastToNPC()->AddToHateList(this, newhate); @@ -1917,8 +2101,7 @@ void Mob::Taunt(NPC* who, bool always_succeed, float chance_bonus) { if (HasSkillProcs()) TrySkillProc(who, SkillTaunt, TauntReuseTime*1000); - - + if (Success && HasSkillProcSuccess()) TrySkillProc(who, SkillTaunt, TauntReuseTime*1000, true); } @@ -1939,8 +2122,7 @@ void Mob::InstillDoubt(Mob *who) { if(!CombatRange(who)) return; - if(IsClient()) - { + if(IsClient()) { CastToClient()->CheckIncreaseSkill(SkillIntimidation, who, 10); } @@ -1970,14 +2152,12 @@ void Mob::InstillDoubt(Mob *who) { } } -uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { - +uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { //Only works on YOUR target. if(defender && (defender->GetBodyType() == BT_Humanoid) && !defender->IsClient() && (skillInUse == SkillArchery) && (GetTarget() == defender)) { - uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1]; - + uint32 HeadShot_Dmg = aabonuses.HeadShot[1] + spellbonuses.HeadShot[1] + itembonuses.HeadShot[1]; uint8 HeadShot_Level = 0; //Get Highest Headshot Level HeadShot_Level = aabonuses.HSLevel; if (HeadShot_Level < spellbonuses.HSLevel) @@ -1985,8 +2165,7 @@ uint32 Mob::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) { else if (HeadShot_Level < itembonuses.HSLevel) HeadShot_Level = itembonuses.HSLevel; - if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){ - + if(HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)){ float ProcChance = GetSpecialProcChances(MainRange); if(ProcChance > MakeRandomFloat(0,1)) return HeadShot_Dmg; @@ -2079,7 +2258,7 @@ float Mob::GetAssassinateProcChances(uint16 ReuseTime) ProcChance += ProcChance * ProcBonus / 100.0f; } else { - /*Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up.*/ + /* Kayen: Unable to find data on old proc rate of assassinate, no idea if our formula is real or made up. */ ProcChance = (10 + (static_cast(mydex/10) + static_cast(itembonuses.HeroicDEX /10)))/100.0f; } @@ -2092,8 +2271,10 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if (!CanDoSpecialAttack(other)) return; - //For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically. - //Kayen: This is unlikely to be completely accurate but use OFFENSE skill value for these effects. + /* + For spells using skill value 98 (feral swipe ect) server sets this to 67 automatically. + Kayen: This is unlikely to be completely accurate but use OFFENSE skill value for these effects. + */ if (skillinuse == SkillBegging) skillinuse = SkillOffense; @@ -2102,8 +2283,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes int Hand = MainPrimary; if (hate == 0 && weapon_damage > 1) hate = weapon_damage; - if(weapon_damage > 0){ - + if(weapon_damage > 0){ if (focus) //From FcBaseEffects weapon_damage += weapon_damage*focus/100; @@ -2113,12 +2293,10 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } int32 min_hit = 1; - int32 max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; - - if(GetLevel() >= 28 && IsWarriorClass() ) - { - int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr ); + int32 max_hit = (2 * weapon_damage*GetDamageTable(skillinuse)) / 100; + if(GetLevel() >= 28 && IsWarriorClass() ) { + int ucDamageBonus = GetWeaponDamageBonus((const Item_Struct*) nullptr ); min_hit += (int) ucDamageBonus; max_hit += (int) ucDamageBonus; hate += ucDamageBonus; @@ -2137,8 +2315,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes } } - ApplySpecialAttackMod(skillinuse, max_hit, min_hit); - + ApplySpecialAttackMod(skillinuse, max_hit, min_hit); min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; if(max_hit < min_hit) @@ -2182,8 +2359,6 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes if (HasDied()) return; - CheckNumHitsRemaining(NUMHIT_OutgoingHitSuccess); - if(aabonuses.SpecialAttackKBProc[0] && aabonuses.SpecialAttackKBProc[1] == skillinuse){ int kb_chance = 25; kb_chance += kb_chance*(100-aabonuses.SpecialAttackKBProc[0])/100; @@ -2199,8 +2374,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob* other, uint16 weapon_damage, SkillUseTypes TrySkillProc(other, skillinuse, ReuseTime, true); } -bool Mob::CanDoSpecialAttack(Mob *other) -{ +bool Mob::CanDoSpecialAttack(Mob *other) { //Make sure everything is valid before doing any attacks. if (!other) { SetTarget(nullptr); @@ -2210,8 +2384,7 @@ bool Mob::CanDoSpecialAttack(Mob *other) if(!GetTarget()) SetTarget(other); - if ((other == nullptr || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) - || HasDied() || (!IsAttackAllowed(other)))) { + if ((other == nullptr || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) || HasDied() || (!IsAttackAllowed(other)))) { return false; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 8a45cf391..d3ef1145d 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -15,26 +15,24 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "../common/debug.h" -#include "../common/spdat.h" -#include "masterentity.h" -#include "../common/packet_dump.h" -#include "../common/moremath.h" -#include "../common/Item.h" -#include "worldserver.h" -#include "../common/skills.h" + #include "../common/bodytypes.h" #include "../common/classes.h" +#include "../common/debug.h" +#include "../common/item.h" #include "../common/rulesys.h" +#include "../common/skills.h" +#include "../common/spdat.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "worldserver.h" #include -#include + #ifndef WIN32 #include #include "../common/unix.h" #endif -#include "StringIDs.h" -#include "QuestParserCollection.h" extern Zone* zone; extern volatile bool ZoneLoaded; @@ -130,6 +128,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) buffs[buffslot].magic_rune = 0; buffs[buffslot].numhits = 0; + if (spells[spell_id].EndurUpkeep > 0) + SetEndurUpkeep(true); + if(IsClient() && CastToClient()->GetClientVersionBit() & BIT_UnderfootAndLater) { EQApplicationPacket *outapp = MakeBuffsPacket(false); @@ -139,7 +140,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(IsNPC()) { - std::vector args; + std::vector args; args.push_back(&buffslot); int i = parse->EventSpell(EVENT_SPELL_EFFECT_NPC, CastToNPC(), nullptr, spell_id, caster ? caster->GetID() : 0, &args); if(i != 0){ @@ -149,7 +150,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) } else if(IsClient()) { - std::vector args; + std::vector args; args.push_back(&buffslot); int i = parse->EventSpell(EVENT_SPELL_EFFECT_CLIENT, nullptr, CastToClient(), spell_id, caster ? caster->GetID() : 0, &args); if(i != 0){ @@ -188,6 +189,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++) @@ -202,7 +205,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) effect_value = GetMaxHP(); if (GetSpellPowerDistanceMod()) - effect_value = effect_value*(GetSpellPowerDistanceMod()/100); + effect_value = effect_value*GetSpellPowerDistanceMod()/100; #ifdef SPELL_EFFECT_SPAM effect_desc[0] = 0; @@ -297,7 +300,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) //This effect can also do damage by percent. if (val < 0) { - if (-val > spell.max[i]) + if (spell.max[i] && -val > spell.max[i]) val = -spell.max[i]; if (caster) @@ -307,7 +310,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) else { - if (val > spell.max[i]) + if (spell.max[i] && val > spell.max[i]) val = spell.max[i]; if(caster) @@ -334,7 +337,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) int i; bool inuse = false; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid == spell_id && i != buffslot) { Message(0, "You must wait before you can be affected by this spell again."); @@ -426,10 +429,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) float x, y, z, heading; const char *target_zone; - x = spell.base[1]; - y = spell.base[0]; - z = spell.base[2]; - heading = spell.base[3]; + x = static_cast(spell.base[1]); + y = static_cast(spell.base[0]); + z = static_cast(spell.base[2]); + heading = static_cast(spell.base[3]); if(!strcmp(spell.teleport_zone, "same")) { @@ -498,10 +501,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) float x, y, z, heading; const char *target_zone; - x = spell.base[1]; - y = spell.base[0]; - z = spell.base[2]; - heading = spell.base[3]; + x = static_cast(spell.base[1]); + y = static_cast(spell.base[0]); + z = static_cast(spell.base[2]); + heading = static_cast(spell.base[3]); if(!strcmp(spell.teleport_zone, "same")) { @@ -799,7 +802,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (!bBreak) { - int resistMod = partial + (GetCHA()/25); + int resistMod = static_cast(partial) + (GetCHA()/25); resistMod = resistMod > 100 ? 100 : resistMod; buffs[buffslot].ticsremaining = resistMod * buffs[buffslot].ticsremaining / 100; } @@ -875,7 +878,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(IsClient()) { AI_Start(); - animation = GetRunspeed() * 21; //set our animation to match our speed about + animation = static_cast(GetRunspeed() * 21.0f); //set our animation to match our speed about } CalculateNewFearpoint(); @@ -909,7 +912,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = (GetHeading() * 12345 / 2); + action->sequence = static_cast((GetHeading() * 12345 / 2)); action->type = 231; action->spell = spell_id; action->buff_unknown = 4; @@ -958,7 +961,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = (GetHeading() * 12345 / 2); + action->sequence = static_cast((GetHeading() * 12345 / 2)); action->type = 231; action->spell = spell_id; action->buff_unknown = 4; @@ -994,7 +997,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) action->source = caster ? caster->GetID() : GetID(); action->level = 65; action->instrument_mod = 10; - action->sequence = (GetHeading() * 12345 / 2); + action->sequence = static_cast((GetHeading() * 12345 / 2)); action->type = 231; action->spell = spell_id; action->buff_unknown = 4; @@ -1048,7 +1051,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if( buffs[slot].spellid != SPELL_UNKNOWN && spells[buffs[slot].spellid].dispel_flag == 0 && @@ -1073,7 +1076,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && IsDetrimentalSpell(buffs[slot].spellid) && @@ -1098,7 +1101,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) break; } - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && IsBeneficialSpell(buffs[slot].spellid) && @@ -1116,7 +1119,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_Purify: { //Attempt to remove all Deterimental buffs. - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(int slot = 0; slot < buff_count; slot++) { if (buffs[slot].spellid != SPELL_UNKNOWN && IsDetrimentalSpell(buffs[slot].spellid)) @@ -1265,10 +1268,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; } @@ -1457,7 +1461,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) caster->GetTarget()->GetGender(), caster->GetTarget()->GetTexture() ); - caster->SendAppearancePacket(AT_Size, caster->GetTarget()->GetSize()); + caster->SendAppearancePacket(AT_Size, static_cast(caster->GetTarget()->GetSize())); for(int x = EmuConstants::MATERIAL_BEGIN; x <= EmuConstants::MATERIAL_TINT_END; x++) caster->SendWearChange(x); @@ -1659,7 +1663,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Model Size: %d%%", effect_value); #endif - ChangeSize(GetSize() * (effect_value / 100.0)); + // Only allow 2 size changes from Base Size + float modifyAmount = (static_cast(effect_value) / 100.0f); + float maxModAmount = GetBaseSize() * modifyAmount * modifyAmount; + if ((GetSize() <= GetBaseSize() && GetSize() > maxModAmount) || + (GetSize() >= GetBaseSize() && GetSize() < maxModAmount) || + (GetSize() <= GetBaseSize() && maxModAmount > 1.0f) || + (GetSize() >= GetBaseSize() && maxModAmount < 1.0f)) + { + ChangeSize(GetSize() * modifyAmount); + } break; } @@ -1878,13 +1891,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (effect_value < 0) { effect_value = 0 - effect_value; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j=0; j < buff_count; j++) { - if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + if (!IsValidSpell(buffs[j].spellid)) continue; if (CalculatePoisonCounters(buffs[j].spellid) == 0) continue; - if (effect_value >= buffs[j].counters) { + if (effect_value >= static_cast(buffs[j].counters)) { if (caster) caster->Message(MT_Spells,"You have cured your target of %s!",spells[buffs[j].spellid].name); caster->CastOnCurer(buffs[j].spellid); @@ -1910,13 +1923,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (effect_value < 0) { effect_value = 0 - effect_value; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j=0; j < buff_count; j++) { - if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + if (!IsValidSpell(buffs[j].spellid)) continue; if (CalculateDiseaseCounters(buffs[j].spellid) == 0) continue; - if (effect_value >= buffs[j].counters) + if (effect_value >= static_cast(buffs[j].counters)) { if (caster) caster->Message(MT_Spells,"You have cured your target of %s!",spells[buffs[j].spellid].name); @@ -1945,13 +1958,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (effect_value < 0) { effect_value = 0 - effect_value; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j=0; j < buff_count; j++) { - if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + if (!IsValidSpell(buffs[j].spellid)) continue; if (CalculateCurseCounters(buffs[j].spellid) == 0) continue; - if (effect_value >= buffs[j].counters) + if (effect_value >= static_cast(buffs[j].counters)) { if (caster) caster->Message(MT_Spells,"You have cured your target of %s!",spells[buffs[j].spellid].name); @@ -1980,13 +1993,13 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if (effect_value < 0) { effect_value = -effect_value; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j=0; j < buff_count; j++) { - if (buffs[j].spellid >= (uint16)SPDAT_RECORDS) + if (!IsValidSpell(buffs[j].spellid)) continue; if (CalculateCorruptionCounters(buffs[j].spellid) == 0) continue; - if (effect_value >= buffs[j].counters) { + if (effect_value >= static_cast(buffs[j].counters)) { if (caster) caster->Message(MT_Spells,"You have cured your target of %s!",spells[buffs[j].spellid].name); caster->CastOnCurer(buffs[j].spellid); @@ -2029,7 +2042,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(IsNPC()) { - Stun(toss_amt); + Stun(static_cast(toss_amt)); } toss_amt = sqrt(toss_amt)-2.0; @@ -2157,7 +2170,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_TemporaryPets: //Dook- swarms and wards: { - // EverHood - this makes necro epic 1.5/2.0 proc work properly + // this makes necro epic 1.5/2.0 proc work properly if((spell_id != 6882) && (spell_id != 6884)) // Chaotic Jester/Steadfast Servant { char pet_name[64]; @@ -2258,15 +2271,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) switch(spells[spell_id].skill) { case SkillThrowing: - caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i], focus, ReuseTime); + caster->DoThrowingAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i], focus, ReuseTime); break; case SkillArchery: - caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i],focus,ReuseTime); + caster->DoArcheryAttackDmg(this, nullptr, nullptr, spells[spell_id].base[i],spells[spell_id].base2[i],focus, ReuseTime); break; default: - caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base[i], spells[spell_id].skill, spells[spell_id].base2[i], focus, ReuseTime); + caster->DoMeleeSkillAttackDmg(this, spells[spell_id].base[i], spells[spell_id].skill, spells[spell_id].base2[i], focus, false, ReuseTime); break; } break; @@ -2482,7 +2495,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) if(!caster->IsClient()) break; - uint32 max_mana = spell.base[i]; + int32 max_mana = spell.base[i]; int ratio = spell.base2[i]; uint32 heal_amt = 0; @@ -2614,7 +2627,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_ManaBurn: { - uint32 max_mana = spell.base[i]; + int32 max_mana = spell.base[i]; int ratio = spell.base2[i]; int32 dmg = 0; @@ -2644,7 +2657,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial) case SE_Taunt: { if (IsNPC()){ - caster->Taunt(this->CastToNPC(), false, spell.base[i]); + caster->Taunt(this->CastToNPC(), false, static_cast(spell.base[i])); if (spell.base2[i] > 0) CastToNPC()->SetHate(caster, (CastToNPC()->GetHateAmount(caster) + spell.base2[i])); @@ -2729,6 +2742,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 +2871,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: @@ -3045,7 +3075,7 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, int Mob::CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining) { /* -neotokyo: i need those formulas checked!!!! +i need those formulas checked!!!! 0 = base 1 - 99 = base + level * formulaID @@ -3127,26 +3157,42 @@ snare has both of them negative, yet their range should work the same: case 110: // confirmed 2/6/04 //is there a reason we dont use updownsign here??? - result = ubase + (caster_level / 5); break; + result = ubase + (caster_level / 6); + break; case 111: - result = updownsign * (ubase + 6 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 6 * (caster_level - 16)); + break; case 112: - result = updownsign * (ubase + 8 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 8 * (caster_level - 24)); + break; case 113: - result = updownsign * (ubase + 10 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 10 * (caster_level - 34)); + break; case 114: - result = updownsign * (ubase + 15 * (caster_level - GetMinLevel(spell_id))); break; + result = updownsign * (ubase + 15 * (caster_level - 44)); + break; - //these formula were updated according to lucy 10/16/04 case 115: // this is only in symbol of transal - result = ubase + 6 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 15) + result += 7 * (caster_level - 15); + break; case 116: // this is only in symbol of ryltan - result = ubase + 8 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 24) + result += 10 * (caster_level - 24); + break; case 117: // this is only in symbol of pinzarn - result = ubase + 12 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 34) + result += 13 * (caster_level - 34); + break; case 118: // used in naltron and a few others - result = ubase + 20 * (caster_level - GetMinLevel(spell_id)); break; + result = ubase; + if (caster_level > 44) + result += 20 * (caster_level - 44); + break; case 119: // confirmed 2/6/04 result = ubase + (caster_level / 8); break; @@ -3166,6 +3212,93 @@ snare has both of them negative, yet their range should work the same: result = MakeRandomInt(ubase, abs(max)); break; + case 124: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * (caster_level - 50); + break; + + case 125: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 2 * (caster_level - 50); + break; + + case 126: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 3 * (caster_level - 50); + break; + + case 127: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 4 * (caster_level - 50); + break; + + case 128: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 5 * (caster_level - 50); + break; + + case 129: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 10 * (caster_level - 50); + break; + + case 130: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 15 * (caster_level - 50); + break; + + case 131: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 20 * (caster_level - 50); + break; + + case 132: // check sign + result = ubase; + if (caster_level > 50) + result += updownsign * 25 * (caster_level - 50); + break; + + case 137: // used in berserker AA desperation + result = ubase - static_cast((ubase * (GetHPRatio() / 100.0f))); + break; + + case 138: { // unused on live? + int maxhps = GetMaxHP() / 2; + if (GetHP() <= maxhps) + result = -(ubase * GetHP() / maxhps); + else + result = -ubase; + break; + } + + case 139: // check sign + result = ubase + (caster_level > 30 ? (caster_level - 30) / 2 : 0); + break; + + case 140: // check sign + result = ubase + (caster_level > 30 ? caster_level - 30 : 0); + break; + + case 141: // check sign + result = ubase + (caster_level > 30 ? (3 * caster_level - 90) / 2 : 0); + break; + + case 142: // check sign + result = ubase + (caster_level > 30 ? 2 * caster_level - 60 : 0); + break; + + case 143: // check sign + result = ubase + (3 * caster_level / 4); + break; + //these are used in stacking effects... formula unknown case 201: case 203: @@ -3224,7 +3357,7 @@ snare has both of them negative, yet their range should work the same: void Mob::BuffProcess() { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int buffs_i = 0; buffs_i < buff_count; ++buffs_i) { @@ -3272,7 +3405,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; } } @@ -3293,7 +3429,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste if(IsNPC()) { - std::vector args; + std::vector args; args.push_back(&ticsremaining); args.push_back(&caster_level); args.push_back(&slot); @@ -3304,7 +3440,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste } else { - std::vector args; + std::vector args; args.push_back(&ticsremaining); args.push_back(&caster_level); args.push_back(&slot); @@ -3432,6 +3568,8 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste case SE_WipeHateList: { + if (IsMezSpell(spell_id)) + break; int wipechance = spells[spell_id].base[i]; int bonus = 0; @@ -3654,12 +3792,12 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) } if(IsClient()) { - std::vector args; + std::vector args; args.push_back(&buffs[slot].casterid); parse->EventSpell(EVENT_SPELL_FADE, nullptr, CastToClient(), buffs[slot].spellid, slot, &args); } else if(IsNPC()) { - std::vector args; + std::vector args; args.push_back(&buffs[slot].casterid); parse->EventSpell(EVENT_SPELL_FADE, CastToNPC(), nullptr, buffs[slot].spellid, slot, &args); @@ -3873,6 +4011,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)){ @@ -4016,6 +4159,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) if (!found_numhits) Numhits(false); } + + if (spells[buffs[slot].spellid].NimbusEffect > 0) + RemoveNimbusEffect(spells[buffs[slot].spellid].NimbusEffect); buffs[slot].spellid = SPELL_UNKNOWN; if(IsPet() && GetOwner() && GetOwner()->IsClient()) { @@ -4027,13 +4173,19 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses) EQApplicationPacket *outapp = MakeBuffsPacket(); entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater); - if(GetTarget() == this) { + if(IsClient() && GetTarget() == this) { CastToClient()->QueuePacket(outapp); } 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); @@ -4203,12 +4355,12 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) break; case SE_LimitCastTimeMin: - if (spell.cast_time < base1) + if (static_cast(spell.cast_time) < base1) LimitFailure = true; break; case SE_LimitCastTimeMax: - if (spell.cast_time > base1) + if (static_cast(spell.cast_time) > base1) LimitFailure = true; break; @@ -4307,24 +4459,24 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id) case SE_LimitSpellClass: if(base1 < 0) { //Exclude - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)); + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) return(0); } else { LimitInclude[12] = true; - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)); //Include + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellClass)) //Include LimitInclude[13] = true; } break; case SE_LimitSpellSubclass: if(base1 < 0) { //Exclude - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)); + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) return(0); } else { LimitInclude[14] = true; - if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)); //Include + if (CheckSpellCategory(spell_id, base1, SE_LimitSpellSubclass)) //Include LimitInclude[15] = true; } break; @@ -4761,24 +4913,24 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo case SE_LimitSpellClass: if(focus_spell.base[i] < 0) { //Exclude - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)); + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) return(0); - } + } else { LimitInclude[12] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)); //Include + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellClass)) //Include LimitInclude[13] = true; } break; case SE_LimitSpellSubclass: if(focus_spell.base[i] < 0) { //Exclude - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)); + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) return(0); - } + } else { LimitInclude[14] = true; - if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)); //Include + if (CheckSpellCategory(spell_id, focus_spell.base[i], SE_LimitSpellSubclass)) //Include LimitInclude[15] = true; } break; @@ -5093,7 +5245,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if (IsValidSpell(proc_spellid)){ - ProcChance = GetSympatheticProcChances(spell_id, spells[TempItem->Focus.Effect].base[0], TempItemAug->ProcRate); + ProcChance = GetSympatheticProcChances(spell_id, spells[TempItemAug->Focus.Effect].base[0], TempItemAug->ProcRate); if(MakeRandomFloat(0, 1) <= ProcChance) SympatheticProcList.push_back(proc_spellid); @@ -5108,7 +5260,7 @@ int16 Client::GetSympatheticFocusEffect(focusType type, uint16 spell_id) { if (spellbonuses.FocusEffects[type]){ int buff_slot = 0; uint16 focusspellid = 0; - uint32 buff_max = GetMaxTotalSlots(); + int buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { if (SympatheticProcList.size() > MAX_SYMPATHETIC) @@ -5326,7 +5478,7 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { int buff_slot = 0; uint16 focusspellid = 0; uint16 focusspell_tracker = 0; - uint32 buff_max = GetMaxTotalSlots(); + int buff_max = GetMaxTotalSlots(); for (buff_slot = 0; buff_slot < buff_max; buff_slot++) { focusspellid = buffs[buff_slot].spellid; if (focusspellid == 0 || focusspellid >= SPDAT_RECORDS) @@ -5404,7 +5556,7 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) { return realTotal + realTotal2 + realTotal3; } -void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id) +void Mob::CheckNumHitsRemaining(uint8 type, int32 buff_slot, uint16 spell_id) { /* Field 175 = numhits type @@ -5425,95 +5577,88 @@ void Mob::CheckNumHitsRemaining(uint8 type, uint32 buff_slot, uint16 spell_id) return; bool bDepleted = false; - uint32 buff_max = GetMaxTotalSlots(); + int 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 (int 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 == NUMHIT_MatchingSpells) { + 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 (int 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() @@ -5531,7 +5676,7 @@ bool Mob::TryDivineSave() { SetHP(1); - int16 EffectsToTry[] = + int32 EffectsToTry[] = { aabonuses.DivineSaveChance[1], itembonuses.DivineSaveChance[1], @@ -5574,8 +5719,8 @@ bool Mob::TryDeathSave() { int SuccessChance = 0; int buffSlot = spellbonuses.DeathSave[1]; - int16 UD_HealMod = 0; - uint32 HealAmt = 300; //Death Pact max Heal + int32 UD_HealMod = 0; + int HealAmt = 300; //Death Pact max Heal if(buffSlot >= 0){ @@ -5680,7 +5825,7 @@ float Mob::GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 I if (total_cast_time > 0 && total_cast_time <= 2500) cast_time_mod = 0.25f; else if (total_cast_time > 2500 && total_cast_time < 7000) - cast_time_mod = 0.167*((static_cast(total_cast_time) - 1000.0f)/1000.0f); + cast_time_mod = 0.167f*((static_cast(total_cast_time) - 1000.0f)/1000.0f); else cast_time_mod = static_cast(total_cast_time) / 7000.0f; @@ -5715,7 +5860,7 @@ int32 Mob::GetFcDamageAmtIncoming(Mob *caster, uint32 spell_id, bool use_skill, return 0; if (spellbonuses.FocusEffects[focusFcDamageAmtIncoming]){ - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(int i = 0; i < buff_count; i++){ if( (IsValidSpell(buffs[i].spellid) && (IsEffectInSpell(buffs[i].spellid, SE_FcDamageAmtIncoming))) ){ @@ -6255,17 +6400,16 @@ bool Mob::PassCastRestriction(bool UseCastRestriction, int16 value, bool IsDama return false; } -bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ +bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id, float speed){ /*For mage 'Bolt' line and other various spells. -This is mostly accurate for how the modern clients handle this effect. -It was changed at some point to use an actual projectile as done here (opposed to a particle effect in classic) - -The projectile graphic appears to be that of 'Ball of Sunlight' ID 80648 and will be visible to anyone in SoF+ -There is no LOS check to prevent a bolt from being cast. If you don't have LOS your bolt simply goes into whatever barrier and you lose your mana. If there is LOS the bolt will lock onto your target and the damage is applied when it hits the target. -If your target moves the bolt moves with it in any direction or angle (consistent with other projectiles). - -The way this is written once a bolt is cast a timer checks the distance from the initial cast to the target repeatedly - and calculates at what predicted time the bolt should hit that target in client_process (therefore accounting for any target movement). + -The way this is written once a bolt is cast a the distance from the initial cast to the target repeatedly + check and if target is moving recalculates at what predicted time the bolt should hit that target in client_process When bolt hits its predicted point the damage is then done to target. Note: Projectile speed of 1 takes 3 seconds to go 100 distance units. Calculations are based on this constant. Live Bolt speed: Projectile speed of X takes 5 seconds to go 300 distance units. @@ -6277,31 +6421,41 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ return false; uint8 anim = spells[spell_id].CastingAnim; - int bolt_id = -1; + int slot = -1; //Make sure there is an avialable bolt to be cast. for (int i = 0; i < MAX_SPELL_PROJECTILE; i++) { - if (projectile_spell_id[i] == 0){ - bolt_id = i; + if (ProjectileAtk[i].target_id == 0){ + slot = i; break; } } - if (bolt_id < 0) + if (slot < 0) return false; - + if (CheckLosFN(spell_target)) { - projectile_spell_id[bolt_id] = spell_id; - projectile_target_id[bolt_id] = spell_target->GetID(); - projectile_x[bolt_id] = GetX(), projectile_y[bolt_id] = GetY(), projectile_z[bolt_id] = GetZ(); - projectile_increment[bolt_id] = 1; - projectile_timer.Start(250); + float speed_mod = speed * 0.45f; //Constant for adjusting speeds to match calculated impact time. + float distance = spell_target->CalculateDistance(GetX(), GetY(), GetZ()); + float hit = 60.0f + (distance / speed_mod); + + ProjectileAtk[slot].increment = 1; + ProjectileAtk[slot].hit_increment = static_cast(hit); //This projected hit time if target does NOT MOVE + ProjectileAtk[slot].target_id = spell_target->GetID(); + ProjectileAtk[slot].wpn_dmg = spell_id; //Store spell_id in weapon damage field + ProjectileAtk[slot].origin_x = GetX(); + ProjectileAtk[slot].origin_y = GetY(); + ProjectileAtk[slot].origin_z = GetZ(); + ProjectileAtk[slot].skill = SkillConjuration; + ProjectileAtk[slot].speed_mod = speed_mod; + + SetProjectileAttack(true); } //This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files. if (RuleB(Spells, UseLiveSpellProjectileGFX)) { - ProjectileAnimation(spell_target,0, false, 1.5,0,0,0, spells[spell_id].player_1); + ProjectileAnimation(spell_target,0, false, speed,0,0,0, spells[spell_id].player_1); } //This allows limited support for server using older spell files that do not contain data for bolt graphics. @@ -6311,19 +6465,17 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){ if (IsClient()){ if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic. - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5); + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, speed); else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5); + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, speed); } else - ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5); - + ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, speed); } - //Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results) else - ProjectileAnimation(spell_target,0, 1, 1.5); + ProjectileAnimation(spell_target,0, 1, speed); } if (spells[spell_id].CastingAnim == 64) @@ -6347,8 +6499,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); @@ -6470,4 +6626,4 @@ void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster) SetSpellPowerDistanceMod(static_cast(mod)); } -} \ No newline at end of file +} diff --git a/zone/spells.cpp b/zone/spells.cpp index 5d891629e..3bdc3557e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -66,20 +66,19 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) and not SpellFinished(). */ -#include "../common/debug.h" -#include "../common/spdat.h" -#include "masterentity.h" -#include "../common/packet_dump.h" -#include "../common/moremath.h" -#include "../common/Item.h" -#include "worldserver.h" -#include "../common/skills.h" #include "../common/bodytypes.h" #include "../common/classes.h" +#include "../common/debug.h" +#include "../common/item.h" #include "../common/rulesys.h" -#include "../common/StringUtil.h" -#include +#include "../common/skills.h" +#include "../common/spdat.h" +#include "../common/string_util.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "worldserver.h" #include +#include #ifndef WIN32 #include @@ -90,8 +89,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include "../common/packet_dump_file.h" #endif -#include "StringIDs.h" -#include "QuestParserCollection.h" + extern Zone* zone; extern volatile bool ZoneLoaded; @@ -122,17 +120,7 @@ void Mob::SpellProcess() void NPC::SpellProcess() { Mob::SpellProcess(); - - if (GetSwarmInfo()) { - if (GetSwarmInfo()->duration->Check(false)) - Depop(); - - Mob *targMob = entity_list.GetMob(GetSwarmInfo()->target); - if (GetSwarmInfo()->target != 0) { - if(!targMob || (targMob && targMob->IsCorpse())) - Depop(); - } - } + DepopSwarmPets(); } /////////////////////////////////////////////////////////////////////////////// @@ -254,7 +242,7 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, } //Added to prevent MQ2 exploitation of equipping normally-unequippable/clickable items with effects and clicking them for benefits. - if(item_slot && IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) + if(item_slot && IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT))) { ItemInst *itm = CastToClient()->GetInv().GetItem(item_slot); int bitmask = 1; @@ -307,6 +295,10 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, uint16 slot, sprintf(temp, "%d", spell_id); parse->EventNPC(EVENT_CAST_BEGIN, CastToNPC(), nullptr, temp, 0); } + + //To prevent NPC ghosting when spells are cast from scripts + if (IsNPC() && IsMoving() && cast_time > 0) + SendPosition(); if(resist_adjust) { @@ -364,6 +356,8 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot, if((IsGroupSpell(spell_id) || spell.targettype == ST_Self || spell.targettype == ST_AECaster || + spell.targettype == ST_Ring || + spell.targettype == ST_Beam || spell.targettype == ST_TargetOptional) && target_id == 0) { mlog(SPELLS__CASTING, "Spell %d auto-targeted the caster. Group? %d, target type %d", spell_id, IsGroupSpell(spell_id), spell.targettype); @@ -704,7 +698,7 @@ bool Client::CheckFizzle(uint16 spell_id) break; } if(((specialize/6.0f) + 15.0f) < MakeRandomFloat(0, 100)) { - specialize *= SPECIALIZE_FIZZLE / 200; + specialize *= SPECIALIZE_FIZZLE / 200.0f; } else { specialize = 0.0f; } @@ -714,7 +708,7 @@ bool Client::CheckFizzle(uint16 spell_id) // > 0 --> skill is lower, higher chance of fizzle // < 0 --> skill is better, lower chance of fizzle // the max that diff can be is +- 235 - float diff = par_skill + spells[spell_id].basediff - act_skill; + float diff = par_skill + static_cast(spells[spell_id].basediff) - act_skill; // if you have high int/wis you fizzle less, you fizzle more if you are stupid if(GetClass() == BARD) @@ -880,7 +874,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, { bool IsFromItem = false; - if(IsClient() && slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && spells[spell_id].recast_time > 1000) { // 10 is item + if(IsClient() && slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && slot != TARGET_RING_SPELL_SLOT && spells[spell_id].recast_time > 1000) { // 10 is item if(!CastToClient()->GetPTimers().Expired(&database, pTimerSpellStart + spell_id, false)) { //should we issue a message or send them a spell gem packet? Message_StringID(13, SPELL_RECAST); @@ -890,7 +884,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, } } - if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) + if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT))) { IsFromItem = true; ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot); @@ -1186,7 +1180,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, int16 DeleteChargeFromSlot = -1; - if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT)) + if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT)) && inventory_slot != 0xFFFFFFFF) // 10 is an item { bool fromaug = false; @@ -1252,7 +1246,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, } } - if(IsClient()) { + if(IsClient()) { CheckNumHitsRemaining(NUMHIT_MatchingSpells); TrySympatheticProc(target, spell_id); } @@ -1378,7 +1372,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce mlog(AA__MESSAGE, "Project Illusion overwrote target caster: %s spell id: %d was ON", GetName(), spell_id); targetType = ST_GroupClientAndPet; } - + if (spell_target && !spell_target->PassCastRestriction(true, spells[spell_id].CastRestriction)){ Message_StringID(13,SPELL_NEED_TAR); return false; @@ -1386,16 +1380,16 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce //Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets) if (!spells[spell_id].InCombat && spells[spell_id].OutofCombat){ - if (IsDetrimentalSpell(spell_id)) { - if ( (spell_target->IsNPC() && spell_target->IsEngaged()) || + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && spell_target->IsEngaged()) || (spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount())){ Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string return false; } } - else if (IsBeneficialSpell(spell_id)) { - if ( (IsNPC() && IsEngaged()) || + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && IsEngaged()) || (IsClient() && CastToClient()->GetAggroCount())){ if (IsDiscipline(spell_id)) Message_StringID(13,NO_ABILITY_IN_COMBAT); @@ -1409,16 +1403,16 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce //Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets) else if (spells[spell_id].InCombat && !spells[spell_id].OutofCombat){ - if (IsDetrimentalSpell(spell_id)) { - if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) || + if (IsDetrimentalSpell(spell_id)) { + if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) || (spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount())){ Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string return false; } } - else if (IsBeneficialSpell(spell_id)) { - if ( (IsNPC() && !IsEngaged()) || + else if (IsBeneficialSpell(spell_id)) { + if ( (IsNPC() && !IsEngaged()) || (IsClient() && !CastToClient()->GetAggroCount())){ if (IsDiscipline(spell_id)) Message_StringID(13,NO_ABILITY_OUT_OF_COMBAT); @@ -1605,9 +1599,48 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce break; } + case ST_AETargetHateList: + { + if (spells[spell_id].range > 0) + { + if(!spell_target) + return false; + + ae_center = spell_target; + CastAction = AETarget; + } + else { + spell_target = nullptr; + ae_center = this; + CastAction = CAHateList; + } + break; + } + + case ST_AreaClientOnly: + case ST_AreaNPCOnly: + { + if (spells[spell_id].range > 0) + { + if(!spell_target) + return false; + + ae_center = spell_target; + CastAction = AETarget; + } + else { + spell_target = nullptr; + ae_center = this; + CastAction = AECaster; + } + break; + } + case ST_UndeadAE: //should only affect undead... + case ST_SummonedAE: case ST_TargetAETap: case ST_AETarget: + case ST_TargetAENoPlayersPets: { if(!spell_target) { @@ -1623,6 +1656,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // Group spells case ST_GroupTeleport: case ST_Group: + case ST_GroupNoPets: { if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id)) { if( (!target) || @@ -1634,6 +1668,12 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce } else { spell_target = this; } + + if (spell_target && spell_target->IsPet() && spells[spell_id].targettype == ST_GroupNoPets){ + Message_StringID(13,NO_CAST_ON_PET); + return false; + } + CastAction = GroupSpell; break; } @@ -1782,10 +1822,10 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_PetMaster: { - + Mob *owner = nullptr; - - if (IsPet()) + + if (IsPet()) owner = GetOwner(); else if ((IsNPC() && CastToNPC()->GetSwarmOwner())) owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner()); @@ -1798,6 +1838,22 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce break; } + case ST_Beam: + { + CastAction = Beam; + spell_target = nullptr; + ae_center = nullptr; + break; + } + + case ST_Ring: + { + CastAction = TargetRing; + spell_target = nullptr; + ae_center = nullptr; + break; + } + default: { mlog(SPELLS__CASTING_ERR, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name); @@ -1975,7 +2031,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 if (!TrySpellProjectile(spell_target, spell_id)) return false; } - + else if(!SpellOnTarget(spell_id, spell_target, false, true, resist_adjust, false)) { if(IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id)) { // Prevent mana usage/timers being set for beneficial buffs @@ -2020,15 +2076,28 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } else { // regular PB AE or targeted AE spell - spell_target is null if PB if(spell_target) // this must be an AETarget spell - { + { + bool cast_on_target = true; + if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && spell_target->IsPetOwnerClient()) + cast_on_target = false; + if (spells[spell_id].targettype == ST_AreaClientOnly && !spell_target->IsClient()) + cast_on_target = false; + if (spells[spell_id].targettype == ST_AreaNPCOnly && !spell_target->IsNPC()) + cast_on_target = false; + // affect the target too - SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); + if (cast_on_target) + SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); } if(ae_center && ae_center == this && IsBeneficialSpell(spell_id)) SpellOnTarget(spell_id, this); bool affect_caster = !IsNPC(); //NPC AE spells do not affect the NPC caster - entity_list.AESpell(this, ae_center, spell_id, affect_caster, resist_adjust); + + if (spells[spell_id].targettype == ST_AETargetHateList) + hate_list.SpellCast(this, spell_id, spells[spell_id].aoerange, ae_center); + else + entity_list.AESpell(this, ae_center, spell_id, affect_caster, resist_adjust); } break; } @@ -2086,7 +2155,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 SpellOnTarget(spell_id, this); #ifdef GROUP_BUFF_PETS //pet too - if (GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) + if (spells[spell_id].targettype != ST_GroupNoPets && GetPet() && HasPetAffinity() && !GetPet()->IsCharmed()) SpellOnTarget(spell_id, GetPet()); #endif } @@ -2094,7 +2163,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 SpellOnTarget(spell_id, spell_target); #ifdef GROUP_BUFF_PETS //pet too - if (spell_target->GetPet() && HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) + if (spells[spell_id].targettype != ST_GroupNoPets && spell_target->GetPet() && HasPetAffinity() && !spell_target->GetPet()->IsCharmed()) SpellOnTarget(spell_id, spell_target->GetPet()); #endif } @@ -2113,52 +2182,19 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 case DirectionalAE: { - float angle_start = spells[spell_id].directional_start + (GetHeading() * 360.0f / 256.0f); - float angle_end = spells[spell_id].directional_end + (GetHeading() * 360.0f / 256.0f); + ConeDirectional(spell_id, resist_adjust); + break; + } + + case Beam: + { + BeamDirectional(spell_id, resist_adjust); + break; + } - while(angle_start > 360.0f) - angle_start -= 360.0f; - - while(angle_end > 360.0f) - angle_end -= 360.0f; - - std::list targets_in_range; - std::list::iterator iter; - - entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); - iter = targets_in_range.begin(); - while(iter != targets_in_range.end()) - { - float heading_to_target = (CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 256.0f); - while(heading_to_target < 0.0f) - heading_to_target += 360.0f; - - while(heading_to_target > 360.0f) - heading_to_target -= 360.0f; - - if(angle_start > angle_end) - { - if((heading_to_target >= angle_start && heading_to_target <= 360.0f) || - (heading_to_target >= 0.0f && heading_to_target <= angle_end)) - { - if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ - (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, spell_target, false, true, resist_adjust); - } - } - } - else - { - if(heading_to_target >= angle_start && heading_to_target <= angle_end) - { - if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ - (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); - SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); - } - } - } - ++iter; - } + case TargetRing: + { + entity_list.AESpell(this, nullptr, spell_id, false, resist_adjust); break; } } @@ -2175,7 +2211,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 // if this was a spell slot or an ability use up the mana for it // CastSpell already reduced the cost for it if we're a client with focus - if(slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && mana_used > 0) + if(slot != USE_ITEM_SPELL_SLOT && slot != POTION_BELT_SPELL_SLOT && slot != TARGET_RING_SPELL_SLOT && mana_used > 0) { mlog(SPELLS__CASTING, "Spell %d: consuming %d mana", spell_id, mana_used); if (!DoHPToManaCovert(mana_used)) @@ -2191,7 +2227,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 CastToClient()->GetPTimers().Start(casting_spell_timer, casting_spell_timer_duration); mlog(SPELLS__CASTING, "Spell %d: Setting custom reuse timer %d to %d", spell_id, casting_spell_timer, casting_spell_timer_duration); } - else if(spells[spell_id].recast_time > 1000) { + else if(spells[spell_id].recast_time > 1000 && !spells[spell_id].IsDisciplineBuff) { int recast = spells[spell_id].recast_time/1000; if (spell_id == SPELL_LAY_ON_HANDS) //lay on hands { @@ -2210,11 +2246,17 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 } } - if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT))) + if(IsClient() && ((slot == USE_ITEM_SPELL_SLOT) || (slot == POTION_BELT_SPELL_SLOT) || (slot == TARGET_RING_SPELL_SLOT))) { 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); } } @@ -2386,7 +2428,7 @@ bool Mob::ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot) { void Mob::BardPulse(uint16 spell_id, Mob *caster) { int buffs_i; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (buffs_i = 0; buffs_i < buff_count; buffs_i++) { if(buffs[buffs_i].spellid != spell_id) continue; @@ -2723,7 +2765,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, if ((effect2 == SE_BStacker) && (!IsCastonFadeDurationSpell(spellid1) && buffs[buffslot].ticsremaining != 1 && IsEffectInSpell(spellid1, SE_CStacker))) return -1; } - + if (spellbonuses.DStacker[0]) { if ((effect2 == SE_DStacker) && (sp2.effectid[i] <= spellbonuses.DStacker[1])) return -1; @@ -2775,7 +2817,7 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, mlog(SPELLS__STACKING, "%s (%d) blocks effect %d on slot %d below %d, but we do not have that effect on that slot. Ignored.", sp1.name, spellid1, blocked_effect, blocked_slot, blocked_below_value); } - } + } } } else { mlog(SPELLS__STACKING, "%s (%d) and %s (%d) appear to be in the same line, skipping Stacking Overwrite/Blocking checks", @@ -2991,7 +3033,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid // it would overwrite, and then hitting a buff we can't stack with. // we also check if overwriting will occur. this is so after this loop // we can determine if there will be room for this buff - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); uint32 start_slot = 0; uint32 end_slot = 0; if (IsDisciplineBuff(spell_id)) { @@ -3116,12 +3158,18 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, BIT_SoDAndLater); - if(GetTarget() == this) + if(IsClient() && GetTarget() == this) CastToClient()->QueuePacket(outapp); 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(); @@ -3139,7 +3187,7 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) mlog(AI__BUFFS, "Checking if buff %d cast at level %d can stack on me.%s", spellid, caster_level, iFailIfOverwrite?" failing if we would overwrite something":""); - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (i=0; i < buff_count; i++) { const Buffs_Struct &curbuf = buffs[i]; @@ -3208,6 +3256,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r if(spelltar->IsClient() && spelltar->CastToClient()->IsHoveringForRespawn()) return false; + if (spells[spell_id].sneak && IsClient() && !CastToClient()->sneaking){ + Message_StringID(13, SNEAK_RESTRICT); + return false;//Fail Safe, this can cause a zone crash certain situations if you try to apply sneak effects when not sneaking. + } + if(IsDetrimentalSpell(spell_id) && !IsAttackAllowed(spelltar) && !IsResurrectionEffects(spell_id)) { if(!IsClient() || !CastToClient()->GetGM()) { Message_StringID(MT_SpellFailure, SPELL_NO_HOLD); @@ -3290,6 +3343,12 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r sprintf(temp1, "%d", spell_id); parse->EventNPC(EVENT_CAST_ON, spelltar->CastToNPC(), this, temp1, 0); } + else if (spelltar->IsClient()) + { + char temp1[100]; + sprintf(temp1, "%d", spell_id); + parse->EventPlayer(EVENT_CAST_ON, spelltar->CastToClient(),temp1, 0); + } mod_spell_cast(spell_id, spelltar, reflect, use_resist_adjust, resist_adjust, isproc); @@ -3487,7 +3546,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r } // Block next spell effect should be used up first(since its blocking the next spell) if(CanBlockSpell()) { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); int focus = 0; for (int b=0; b < buff_count; b++) { if(IsEffectInSpell(buffs[b].spellid, SE_BlockNextSpellFocus)) { @@ -3564,7 +3623,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r if(spell_effectiveness == 0 || !IsPartialCapableSpell(spell_id) ) { mlog(SPELLS__RESISTS, "Spell %d was completely resisted by %s", spell_id, spelltar->GetName()); - + if (spells[spell_id].resisttype == RESIST_PHYSICAL){ Message_StringID(MT_SpellFailure, PHYSICAL_RESIST_FAIL,spells[spell_id].name); spelltar->Message_StringID(MT_SpellFailure, YOU_RESIST, spells[spell_id].name); @@ -3606,108 +3665,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); @@ -3733,7 +3693,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r safe_delete(action_packet); return false; } - + // cause the effects to the target if(!spelltar->SpellEffect(this, spell_id, spell_effectiveness)) { @@ -3746,11 +3706,13 @@ 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); - + if (spelltar) spelltar->CheckNumHitsRemaining(NUMHIT_IncomingSpells); } @@ -3848,9 +3810,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r void Corpse::CastRezz(uint16 spellid, Mob* Caster) { - _log(SPELLS__REZ, "Corpse::CastRezz spellid %i, Rezzed() is %i, rezzexp is %i", spellid,Rezzed(),rezzexp); + _log(SPELLS__REZ, "Corpse::CastRezz spellid %i, Rezzed() is %i, rezzexp is %i", spellid,IsRezzed(),rez_experience); - if(Rezzed()){ + if(IsRezzed()){ if(Caster && Caster->IsClient()) Caster->Message(13,"This character has already been resurrected."); @@ -3867,7 +3829,7 @@ void Corpse::CastRezz(uint16 spellid, Mob* Caster) EQApplicationPacket* outapp = new EQApplicationPacket(OP_RezzRequest, sizeof(Resurrect_Struct)); Resurrect_Struct* rezz = (Resurrect_Struct*) outapp->pBuffer; // Why are we truncating these names to 30 characters ? - memcpy(rezz->your_name,this->orgname,30); + memcpy(rezz->your_name,this->corpse_name,30); memcpy(rezz->corpse_name,this->name,30); memcpy(rezz->rezzer_name,Caster->GetName(),30); rezz->zone_id = zone->GetZoneID(); @@ -3880,7 +3842,7 @@ void Corpse::CastRezz(uint16 spellid, Mob* Caster) rezz->unknown020 = 0x00000000; rezz->unknown088 = 0x00000000; // We send this to world, because it needs to go to the player who may not be in this zone. - worldserver.RezzPlayer(outapp, rezzexp, dbid, OP_RezzRequest); + worldserver.RezzPlayer(outapp, rez_experience, corpse_db_id, OP_RezzRequest); _pkt(SPELLS__REZ, outapp); safe_delete(outapp); } @@ -3900,7 +3862,7 @@ bool Mob::FindBuff(uint16 spellid) // removes all buffs void Mob::BuffFadeAll() { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j = 0; j < buff_count; j++) { if(buffs[j].spellid != SPELL_UNKNOWN) BuffFadeBySlot(j, false); @@ -3911,7 +3873,7 @@ void Mob::BuffFadeAll() void Mob::BuffFadeNonPersistDeath() { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j = 0; j < buff_count; j++) { if (buffs[j].spellid != SPELL_UNKNOWN && !IsPersistDeathSpell(buffs[j].spellid)) BuffFadeBySlot(j, false); @@ -3921,7 +3883,7 @@ void Mob::BuffFadeNonPersistDeath() } void Mob::BuffFadeDetrimental() { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j = 0; j < buff_count; j++) { if(buffs[j].spellid != SPELL_UNKNOWN) { if(IsDetrimentalSpell(buffs[j].spellid)) @@ -3935,7 +3897,7 @@ void Mob::BuffFadeDetrimentalByCaster(Mob *caster) if(!caster) return; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j = 0; j < buff_count; j++) { if(buffs[j].spellid != SPELL_UNKNOWN) { if(IsDetrimentalSpell(buffs[j].spellid)) @@ -3975,7 +3937,7 @@ void Mob::BuffFadeBySitModifier() // removes the buff matching spell_id void Mob::BuffFadeBySpellID(uint16 spell_id) { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int j = 0; j < buff_count; j++) { if (buffs[j].spellid == spell_id) @@ -3991,7 +3953,7 @@ void Mob::BuffFadeByEffect(int effectid, int skipslot) { int i; - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(i = 0; i < buff_count; i++) { if(buffs[i].spellid == SPELL_UNKNOWN) @@ -4408,7 +4370,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (CharismaCheck) { - /* + /* Charisma ONLY effects the initial resist check when charm is cast with 10 CHA = -1 Resist mod up to 255 CHA (min ~ 75 cha) Charisma less than ~ 75 gives a postive modifier to resist checks at approximate ratio of -10 CHA = +6 Resist. Mez spells do same initial resist check as a above. @@ -4470,7 +4432,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use if (CharmTick) { int min_charmbreakchance = ((100/RuleI(Spells, CharmBreakCheckChance))/66 * 100)*2; - + if (resist_chance < min_charmbreakchance) resist_chance = min_charmbreakchance; } @@ -4501,13 +4463,12 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } else { - resist_chance -= roll; if(resist_chance < 1) { resist_chance = 1; } - int partial_modifier = ((150 * (roll - resist_chance)) / resist_chance); + int partial_modifier = ((150 * (resist_chance - roll)) / resist_chance); if(IsNPC()) { @@ -4535,17 +4496,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use } } - if(partial_modifier < 0) + if(partial_modifier <= 0) + { + return 100; + } + else if(partial_modifier >= 100) { return 0; } - if(partial_modifier > 100) - { - return 100; - } - - return partial_modifier; + return (100.0f - partial_modifier); } } } @@ -4624,7 +4584,7 @@ float Mob::GetAOERange(uint16 spell_id) { if(IsBardSong(spell_id) && IsBeneficialSpell(spell_id)) { //Live AA - Extended Notes, SionachiesCrescendo - float song_bonus = aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange; + float song_bonus = static_cast(aabonuses.SongRange + spellbonuses.SongRange + itembonuses.SongRange); range += range*song_bonus /100.0f; } @@ -4756,7 +4716,7 @@ void NPC::Stun(int duration) { void NPC::UnStun() { Mob::UnStun(); - SetRunAnimSpeed(this->GetRunspeed()); + SetRunAnimSpeed(static_cast(GetRunspeed())); SendPosition(); } @@ -4819,8 +4779,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); @@ -4843,6 +4803,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); @@ -4857,6 +4819,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); @@ -4884,6 +4848,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) @@ -4899,7 +4864,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)); @@ -4927,8 +4893,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) { @@ -4965,68 +4932,56 @@ int Client::FindSpellBookSlotBySpellID(uint16 spellid) { return -1; //default } -bool Client::SpellGlobalCheck(uint16 Spell_ID, uint16 Char_ID) { +bool Client::SpellGlobalCheck(uint16 spell_ID, uint32 char_ID) { - std::string Spell_Global_Name; - int Spell_Global_Value; - int Global_Value; + std::string spell_Global_Name; + int spell_Global_Value; + int global_Value; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT qglobal, value FROM spell_globals WHERE spellid=%i", Spell_ID), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - Spell_Global_Name = row[0]; - Spell_Global_Value = atoi(row[1]); - - mysql_free_result(result); - - if (Spell_Global_Name.empty()) { // If the entry in the spell_globals table has nothing set for the qglobal name - return true; - } - else if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT value FROM quest_globals WHERE charid=%i AND name='%s'", Char_ID, Spell_Global_Name.c_str()), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - - Global_Value = atoi(row[0]); - mysql_free_result(result); - if (Global_Value == Spell_Global_Value) { // If the values match from both tables, allow the spell to be scribed - return true; - } - else if (Global_Value > Spell_Global_Value) { // Check if the qglobal value is greater than the require spellglobal value - return true; - } - else // If no matching result found in qglobals, don't scribe this spell - { - LogFile->write(EQEMuLog::Error, "Char ID: %i Spell_globals Name: '%s' Value: '%i' did not match QGlobal Value: '%i' for Spell ID %i", Char_ID, Spell_Global_Name.c_str(), Spell_Global_Value, Global_Value, Spell_ID); - return false; - } - } - else - LogFile->write(EQEMuLog::Error, "Char ID: %i does not have the Qglobal Name: '%s' for Spell ID %i", Char_ID, Spell_Global_Name.c_str(), Spell_ID); - safe_delete_array(query); - } - else - LogFile->write(EQEMuLog::Error, "Spell ID %i query of spell_globals with Name: '%s' Value: '%i' failed", Spell_ID, Spell_Global_Name.c_str(), Spell_Global_Value); - } - else { - return true; // Spell ID isn't listed in the spells_global table, so it is not restricted from scribing - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error while querying Spell ID %i spell_globals table query '%s': %s", Spell_ID, query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT qglobal, value FROM spell_globals " + "WHERE spellid = %i", spell_ID); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error while querying Spell ID %i spell_globals table query '%s': %s", spell_ID, query.c_str(), results.ErrorMessage().c_str()); return false; // Query failed, so prevent spell from scribing just in case - } - return false; // Default is false + } + + if (results.RowCount() != 1) + return true; // Spell ID isn't listed in the spells_global table, so it is not restricted from scribing + + auto row = results.begin(); + spell_Global_Name = row[0]; + spell_Global_Value = atoi(row[1]); + + if (spell_Global_Name.empty()) + return true; // If the entry in the spell_globals table has nothing set for the qglobal name + + query = StringFormat("SELECT value FROM quest_globals " + "WHERE charid = %i AND name = '%s'", + char_ID, spell_Global_Name.c_str()); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Spell ID %i query of spell_globals with Name: '%s' Value: '%i' failed", spell_ID, spell_Global_Name.c_str(), spell_Global_Value); + return false; + } + + if (results.RowCount() != 1) { + LogFile->write(EQEMuLog::Error, "Char ID: %i does not have the Qglobal Name: '%s' for Spell ID %i", char_ID, spell_Global_Name.c_str(), spell_ID); + return false; + } + + row = results.begin(); + + global_Value = atoi(row[0]); + + if (global_Value == spell_Global_Value) + return true; // If the values match from both tables, allow the spell to be scribed + else if (global_Value > spell_Global_Value) + return true; // Check if the qglobal value is greater than the require spellglobal value + + // If no matching result found in qglobals, don't scribe this spell + LogFile->write(EQEMuLog::Error, "Char ID: %i Spell_globals Name: '%s' Value: '%i' did not match QGlobal Value: '%i' for Spell ID %i", char_ID, spell_Global_Name.c_str(), spell_Global_Value, global_Value, spell_ID); + return false; } // TODO get rid of this @@ -5051,7 +5006,7 @@ uint16 Mob::GetSpellIDFromSlot(uint8 slot) } bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for (int i = 0; i < buff_count; i++) { if (buffs[i].spellid != SPELL_UNKNOWN) { @@ -5081,7 +5036,7 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) { } bool Mob::IsCombatProc(uint16 spell_id) { - + if (RuleB(Spells, FocusCombatProcs)) return false; @@ -5092,7 +5047,7 @@ bool Mob::IsCombatProc(uint16 spell_id) { { for (int i = 0; i < MAX_PROCS; i++){ - if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id + if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id || RangedProcs[i].spellID == spell_id){ return true; } @@ -5150,7 +5105,7 @@ bool Mob::AddDefensiveProc(uint16 spell_id, uint16 iChance, uint16 base_spell_id { if(spell_id == SPELL_UNKNOWN) return(false); - + int i; for (i = 0; i < MAX_PROCS; i++) { if (DefensiveProcs[i].spellID == SPELL_UNKNOWN) { @@ -5255,20 +5210,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); } @@ -5343,6 +5318,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) @@ -5352,6 +5328,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; } } @@ -5361,7 +5338,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target) void Mob::BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration) { - uint32 buff_count = GetMaxTotalSlots(); + int buff_count = GetMaxTotalSlots(); for(int i = 0; i < buff_count; ++i) { if (buffs[i].spellid == spell_id) @@ -5369,7 +5346,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]); } } } @@ -5420,3 +5397,155 @@ 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); +} + +void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, float StartX, float StartY, float &dX, float &dY, float &dZ) +{ + if (!distance) { return; } + if (!MaxZDiff) { MaxZDiff = 5; } + + float ReverseHeading = 256 - heading; + float ConvertAngle = ReverseHeading * 1.40625f; + if (ConvertAngle <= 270) + ConvertAngle = ConvertAngle + 90; + else + ConvertAngle = ConvertAngle - 270; + + float Radian = ConvertAngle * (3.1415927f / 180.0f); + + float CircleX = distance * cos(Radian); + float CircleY = distance * sin(Radian); + dX = CircleX + StartX; + dY = CircleY + StartY; + dZ = FindGroundZ(dX, dY, MaxZDiff); +} + +void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) +{ + int maxtarget_count = 0; + bool beneficial_targets = false; + + if (IsBeneficialSpell(spell_id) && IsClient()) + beneficial_targets = true; + + std::list targets_in_range; + std::list::iterator iter; + + entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].range, spells[spell_id].range / 2, targets_in_range); + iter = targets_in_range.begin(); + + float dX = 0; + float dY = 0; + float dZ = 0; + + CalcDestFromHeading(GetHeading(), spells[spell_id].range, 5, GetX(), GetY(), dX, dY, dZ); + dZ = GetZ(); + + //FIND SLOPE: Put it into the form y = mx + b + float m = (dY - GetY()) / (dX - GetX()); + float b = (GetY() * dX - dY * GetX()) / (dX - GetX()); + + while(iter != targets_in_range.end()) + { + if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerClient())) + || (*iter)->BehindMob(this, (*iter)->GetX(),(*iter)->GetY())){ + ++iter; + continue; + } + + //# shortest distance from line to target point + float d = abs( (*iter)->GetY() - m * (*iter)->GetX() - b) / sqrt(m * m + 1); + + if (d <= spells[spell_id].aoerange) + { + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); + SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + maxtarget_count++; + } + + if (maxtarget_count >= spells[spell_id].aemaxtargets) + return; + } + ++iter; + } +} + +void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) +{ + int maxtarget_count = 0; + bool beneficial_targets = false; + + if (IsBeneficialSpell(spell_id) && IsClient()) + beneficial_targets = true; + + float angle_start = spells[spell_id].directional_start + (GetHeading() * 360.0f / 256.0f); + float angle_end = spells[spell_id].directional_end + (GetHeading() * 360.0f / 256.0f); + + while(angle_start > 360.0f) + angle_start -= 360.0f; + + while(angle_end > 360.0f) + angle_end -= 360.0f; + + std::list targets_in_range; + std::list::iterator iter; + + entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range); + iter = targets_in_range.begin(); + + while(iter != targets_in_range.end()){ + + if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerClient()))){ + ++iter; + continue; + } + + float heading_to_target = (CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 256.0f); + + while(heading_to_target < 0.0f) + heading_to_target += 360.0f; + + while(heading_to_target > 360.0f) + heading_to_target -= 360.0f; + + if(angle_start > angle_end){ + if((heading_to_target >= angle_start && heading_to_target <= 360.0f) || (heading_to_target >= 0.0f && heading_to_target <= angle_end)){ + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){ + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); + SpellOnTarget(spell_id,(*iter), false, true, resist_adjust); + maxtarget_count++; + } + } + } + else{ + if(heading_to_target >= angle_start && heading_to_target <= angle_end){ + if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los) { + (*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this); + SpellOnTarget(spell_id, (*iter), false, true, resist_adjust); + maxtarget_count++; + } + } + } + + if (maxtarget_count >= spells[spell_id].aemaxtargets) + return; + + ++iter; + } +} \ No newline at end of file diff --git a/zone/StringIDs.h b/zone/string_ids.h similarity index 90% rename from zone/StringIDs.h rename to zone/string_ids.h index b1e13edca..e1e1bc7b3 100644 --- a/zone/StringIDs.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) @@ -205,6 +207,24 @@ #define MERCHANT_HANDY_ITEM2 1146 //Greetings, %3. You look like you could use a %4. #define MERCHANT_HANDY_ITEM3 1147 //Hi there %3, just browsing? Have you seen the %4 I just got in? #define MERCHANT_HANDY_ITEM4 1148 //Welcome to my shop, %3. You would probably find a %4 handy. +#define WONT_SELL_RACE1 1154 //I don't like to speak to %B3(12) much less sell to them! +#define WONT_SELL_CLASS1 1155 //It's %B3(13) like you that are ruining the continent...get OUT! +#define WONT_SELL_CLASS2 1156 //Isn't there some kind of ordinance against %B3(13) crawling out from under their rocks? +#define WONT_SELL_CLASS3 1157 //%B3(13) like you don't have any place in my shop..now make way for welcome customers. +#define WONT_SELL_CLASS4 1158 //I thought scumbag %B3(13) like you just stole whatever they need. Now GET OUT! +#define WONT_SELL_CLASS5 1159 //I don't have anything to do with %B3(13)..move along. +#define WONT_SELL_NONSTDRACE1 1160 //I don't have anything to do with your little gang..move along. +#define WONT_SELL_RACE2 1161 //It's not enough that you %B3(12) have ruined your own land. Now get lost! +#define WONT_SELL_RACE3 1162 //I have something here that %B3(12) use..let me see...it's the EXIT, now get LOST! +#define WONT_SELL_RACE4 1163 //Don't you %B3(12) have your own merchants? Whatever, I'm not selling anything to you! +#define WONT_SELL_NONSTDRACE2 1164 //Members of your little "club" have ruined things around here..get lost! +#define WONT_SELL_NONSTDRACE3 1165 //I don't have anything to do with your damned club..move along. +#define WONT_SELL_DEEDS1 1166 //Creatures like you make me sick..the things you do..get out of here Pagan! +#define WONT_SELL_DEEDS2 1167 //After all the things you've done..the things you believe in..leave my shop! +#define WONT_SELL_DEEDS3 1168 //Actions speak louder than beliefs, and I despise both your actions and all you believe in. +#define WONT_SELL_DEEDS4 1169 //Get out of here now! +#define WONT_SELL_DEEDS5 1170 //I am tolerant by nature..but infidels like you push me past my limit..get out! +#define WONT_SELL_DEEDS6 1171 //I cannot abide you or your actions against all that is right..BE GONE! #define AA_POINT 1197 //point #define AA_POINTS 1215 //points #define SPELL_FIZZLE_OTHER 1218 //%1's spell fizzles! @@ -231,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. @@ -242,6 +264,7 @@ #define TRADESKILL_MISSING_COMPONENTS 3456 //Sorry, but you don't have everything you need for this recipe in your general inventory. #define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1! #define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end. +#define NO_CAST_ON_PET 4045 //You cannot cast this spell on your pet. #define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again. #define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can! #define CORPSEDRAG_ALREADY 4062 //You are already dragging %1. @@ -251,8 +274,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. @@ -270,10 +296,14 @@ #define GUILD_BANK_FULL 6098 // There is no more room in the Guild Bank. #define GUILD_BANK_TRANSFERRED 6100 // '%1' transferred to Guild Bank from Deposits. #define GUILD_BANK_EMPTY_HANDS 6108 // You must empty your hands to withdraw from the Guild Bank. +#define TRANSFORM_FAILED 6326 //This mold cannot be applied to your %1. +#define TRANSFORM_COMPLETE 6327 //You have successfully transformed your %1. +#define DETRANSFORM_FAILED 6341 //%1 has no transformation that can be removed. #define GENERIC_STRING 6688 //%1 (used to any basic message) #define SENTINEL_TRIG_YOU 6724 //You have triggered your sentinel. #define SENTINEL_TRIG_OTHER 6725 //%1 has triggered your sentinel. #define IDENTIFY_SPELL 6765 //Item Lore: %1. +#define BUFF_NOT_BLOCKABLE 7608 //You cannot block this effect. #define LDON_DONT_KNOW_TRAPPED 7552 //You do not know if this object is trapped. #define LDON_HAVE_DISARMED 7553 //You have disarmed %1! #define LDON_ACCIDENT_SETOFF 7554 //You accidentally set off the trap! @@ -313,6 +343,7 @@ #define STRIKETHROUGH_STRING 9078 //You strike through your opponent's defenses! #define SPELL_REFLECT 9082 //%1's spell has been reflected by %2. #define NEW_SPELLS_AVAIL 9149 //You have new spells available to you. Check the merchants near your guild master. +#define SNEAK_RESTRICT 9240 //You can not use this ability because you have not been hidden for long enough. #define PET_NOW_FOCUSING 9254 //Focusing on one target, Master. #define PET_NOT_FOCUSING 9263 //No longer focusing on one target, Master. #define PET_NOT_CASTING 9264 //Not casting spells, Master. diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 221085d4e..924d3770b 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -27,24 +27,23 @@ Copyright (C) 2001-2008 EQEMu Development Team (http://eqemulator.net) #define strcasecmp _stricmp #endif -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" #include "../common/rulesys.h" #include "masterentity.h" #include "../common/features.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" +#include "mob.h" +#include "queryserv.h" +extern QueryServ* QServ; TaskManager::TaskManager() { - for(int i=0; iActivityCount; j++) { @@ -62,43 +61,31 @@ 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) { @@ -130,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; } @@ -337,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; } @@ -509,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]); } } @@ -839,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]); } } @@ -899,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) { @@ -1156,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. @@ -1234,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; @@ -1251,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; } @@ -1431,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) { @@ -1983,15 +1818,20 @@ 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) - { + + if(Task->Activity[ActivityID].GoalMethod != METHODQUEST) { char buf[24]; snprintf(buf, 23, "%d %d", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID); buf[23] = '\0'; parse->EventPlayer(EVENT_TASK_STAGE_COMPLETE, c, buf, 0); + + /* QS: PlayerLogTaskUpdates :: Update */ + if (RuleB(QueryServ, PlayerLogTaskUpdates)){ + std::string event_desc = StringFormat("Task Stage Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, c->GetZoneID(), c->GetInstanceID()); + 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. @@ -2001,6 +1841,12 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T buf[23] = '\0'; parse->EventPlayer(EVENT_TASK_COMPLETE, c, buf, 0); + /* QS: PlayerLogTaskUpdates :: Complete */ + if (RuleB(QueryServ, PlayerLogTaskUpdates)){ + std::string event_desc = StringFormat("Task Complete :: taskid:%i activityid:%i donecount:%i in zoneid:%i instid:%i", ActiveTasks[TaskIndex].TaskID, ActiveTasks[TaskIndex].Activity[ActivityID].ActivityID, ActiveTasks[TaskIndex].Activity[ActivityID].DoneCount, c->GetZoneID(), c->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Task_Updates, c->CharacterID(), event_desc); + } + taskmanager->SendCompletedTasksToClient(c, this); c->SendTaskActivityComplete(ActiveTasks[TaskIndex].TaskID, 0, TaskIndex, false); taskmanager->SaveClientState(c, this); @@ -2010,6 +1856,7 @@ void ClientTaskState::IncrementDoneCount(Client *c, TaskInformation* Task, int T // If Experience and/or cash rewards are set, reward them from the task even if RewardMethod is METHODQUEST RewardTask(c, Task); //RemoveTask(c, TaskIndex); + } } @@ -3134,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--; } @@ -3289,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; } @@ -3478,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/titles.cpp b/zone/titles.cpp index f51937dba..eeae38b8a 100644 --- a/zone/titles.cpp +++ b/zone/titles.cpp @@ -19,7 +19,7 @@ #include "../common/eq_packet_structs.h" #include "masterentity.h" #include "titles.h" -#include "../common/StringUtil.h" +#include "../common/string_util.h" #include "worldserver.h" extern WorldServer worldserver; @@ -31,25 +31,17 @@ bool TitleManager::LoadTitles() { Titles.clear(); - TitleEntry Title; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - - if (!database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, `min_aa_points`, `max_aa_points`, `class`, `gender`, " - "`char_id`, `status`, `item_id`, `prefix`, `suffix`, `title_set` from titles"), errbuf, &result)) - { - LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query, errbuf); - safe_delete_array(query); - return(false); + std::string query = "SELECT `id`, `skill_id`, `min_skill_value`, `max_skill_value`, " + "`min_aa_points`, `max_aa_points`, `class`, `gender`, `char_id`, " + "`status`, `item_id`, `prefix`, `suffix`, `title_set` FROM titles"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Unable to load titles: %s : %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); - - while ((row = mysql_fetch_row(result))) { + for (auto row = results.begin(); row != results.end(); ++row) { + TitleEntry Title; Title.TitleID = atoi(row[0]); Title.SkillID = (SkillUseTypes) atoi(row[1]); Title.MinSkillValue = atoi(row[2]); @@ -66,9 +58,8 @@ bool TitleManager::LoadTitles() Title.TitleSet = atoi(row[13]); Titles.push_back(Title); } - mysql_free_result(result); - return(true); + return true; } EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *c) @@ -244,92 +235,70 @@ bool TitleManager::IsNewTradeSkillTitleAvailable(int SkillID, int SkillValue) return false; } -void TitleManager::CreateNewPlayerTitle(Client *c, const char *Title) +void TitleManager::CreateNewPlayerTitle(Client *client, const char *title) { - if(!c || !Title) + if(!client || !title) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; + char *escTitle = new char[strlen(title) * 2 + 1]; - char *EscTitle = new char[strlen(Title) * 2 + 1]; + client->SetAATitle(title); - c->SetAATitle(Title); - - database.DoEscapeString(EscTitle, Title, strlen(Title)); - - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `id` from titles where `prefix` = '%s' and char_id = %i", EscTitle, c->CharacterID()), errbuf, &result)) - { - if(mysql_num_rows(result) > 0) - { - mysql_free_result(result); - safe_delete_array(query); - safe_delete_array(EscTitle); - return; - } - mysql_free_result(result); + database.DoEscapeString(escTitle, title, strlen(title)); + auto query = StringFormat("SELECT `id` FROM titles " + "WHERE `prefix` = '%s' AND char_id = %i", + escTitle, client->CharacterID()); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() > 0){ + safe_delete_array(escTitle); + return; } - safe_delete_array(query); - - if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `prefix`) VALUES(%i, '%s')", - c->CharacterID(), EscTitle), errbuf)) - LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query, errbuf); - else - { - ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - } - safe_delete_array(query); - safe_delete_array(EscTitle); + query = StringFormat("INSERT INTO titles (`char_id`, `prefix`) VALUES(%i, '%s')", + client->CharacterID(), escTitle); + safe_delete_array(escTitle); + results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error adding title: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); } -void TitleManager::CreateNewPlayerSuffix(Client *c, const char *Suffix) +void TitleManager::CreateNewPlayerSuffix(Client *client, const char *suffix) { - if(!c || !Suffix) + if(!client || !suffix) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; + client->SetTitleSuffix(suffix); - char *EscSuffix = new char[strlen(Suffix) * 2 + 1]; + char *escSuffix = new char[strlen(suffix) * 2 + 1]; + database.DoEscapeString(escSuffix, suffix, strlen(suffix)); - c->SetTitleSuffix(Suffix); - - database.DoEscapeString(EscSuffix, Suffix, strlen(Suffix)); - - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `id` from titles where `suffix` = '%s' and char_id = %i", EscSuffix, c->CharacterID()), errbuf, &result)) - { - if(mysql_num_rows(result) > 0) - { - mysql_free_result(result); - safe_delete_array(query); - safe_delete_array(EscSuffix); + std::string query = StringFormat("SELECT `id` FROM titles " + "WHERE `suffix` = '%s' AND char_id = %i", + escSuffix, client->CharacterID()); + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount() > 0) { + safe_delete_array(escSuffix); return; - } - mysql_free_result(result); - } + } - safe_delete_array(query); - - if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT into titles (`char_id`, `suffix`) VALUES(%i, '%s')", - c->CharacterID(), EscSuffix), errbuf)) - LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query, errbuf); - else - { - ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); - worldserver.SendPacket(pack); - safe_delete(pack); - } - safe_delete_array(query); - safe_delete_array(EscSuffix); + query = StringFormat("INSERT INTO titles (`char_id`, `suffix`) VALUES(%i, '%s')", + client->CharacterID(), escSuffix); + safe_delete_array(escSuffix); + results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error adding title suffix: %s %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + ServerPacket* pack = new ServerPacket(ServerOP_ReloadTitles, 0); + worldserver.SendPacket(pack); + safe_delete(pack); } void Client::SetAATitle(const char *Title) @@ -368,67 +337,48 @@ void Client::SetTitleSuffix(const char *Suffix) safe_delete(outapp); } -void Client::EnableTitle(int titleset) { +void Client::EnableTitle(int titleSet) { - if (CheckTitle(titleset)) { + if (CheckTitle(titleSet)) return; - } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("INSERT INTO player_titlesets " + "(char_id, title_set) VALUES (%i, %i)", + CharacterID(), titleSet); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleSet, CharacterID()); - if(!database.RunQuery(query,MakeAnyLenString(&query, "INSERT INTO player_titlesets (char_id, title_set) VALUES (%i, %i)", CharacterID(), titleset), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in EnableTitle query for titleset %i and charid %i", titleset, CharacterID()); - safe_delete_array(query); - return; - } - else { - safe_delete_array(query); - return; - } } -bool Client::CheckTitle(int titleset) { +bool Client::CheckTitle(int titleSet) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT `id` FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", titleset, CharacterID()), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) >= 1) { - mysql_free_result(result); - return(true); - } - mysql_free_result(result); + std::string query = StringFormat("SELECT `id` FROM player_titlesets " + "WHERE `title_set`=%i AND `char_id`=%i LIMIT 1", + titleSet, CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - else { - LogFile->write(EQEMuLog::Error, "Error in CheckTitle query '%s': %s", query, errbuf); - safe_delete_array(query); - } + if (results.RowCount() == 0) + return false; - return(false); + return true; } -void Client::RemoveTitle(int titleset) { +void Client::RemoveTitle(int titleSet) { - if (!CheckTitle(titleset)) { + if (!CheckTitle(titleSet)) return; - } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("DELETE FROM player_titlesets " + "WHERE `title_set` = %i AND `char_id` = %i", + titleSet, CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - if (database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM player_titlesets WHERE `title_set`=%i AND `char_id`=%i", titleset, CharacterID()), errbuf)) { - safe_delete_array(query); - } - - else { - LogFile->write(EQEMuLog::Error, "Error in RemoveTitle query '%s': %s", query, errbuf); - safe_delete_array(query); - } - - return; } diff --git a/zone/tradeskills.cpp b/zone/tradeskills.cpp index d89692dbb..fd9f5a632 100644 --- a/zone/tradeskills.cpp +++ b/zone/tradeskills.cpp @@ -24,16 +24,16 @@ #include //for htonl #endif -#include "masterentity.h" -#include "zonedb.h" -#include "../common/packet_functions.h" -#include "../common/packet_dump.h" -#include "titles.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" #include "../common/rulesys.h" -#include "QuestParserCollection.h" +#include "../common/string_util.h" +#include "queryserv.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "titles.h" +#include "zonedb.h" + +extern QueryServ* QServ; static const SkillUseTypes TradeskillUnknown = Skill1HBlunt; /* an arbitrary non-tradeskill */ @@ -139,14 +139,14 @@ 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); ItemInst *aug = tobe_auged->GetAugment(slot); if(aug) { - std::vector args; + std::vector args; args.push_back(aug); parse->EventItem(EVENT_AUGMENT_ITEM, user, tobe_auged, nullptr, "", slot, &args); @@ -168,7 +168,7 @@ void Object::HandleAugmentation(Client* user, const AugmentItem_Struct* in_augme const uint32 id = auged_with->GetID(); ItemInst *aug = tobe_auged->GetAugment(in_augment->augment_slot); if(aug) { - std::vector args; + std::vector args; args.push_back(aug); parse->EventItem(EVENT_UNAUGMENT_ITEM, user, tobe_auged, nullptr, "", slot, &args); @@ -280,6 +280,44 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob } container = inst; + if (container->GetItem() && container->GetItem()->BagType == BagTypeTransformationmold) { + const ItemInst* inst = container->GetItem(0); + bool AllowAll = RuleB(Inventory, AllowAnyWeaponTransformation); + if (inst && ItemInst::CanTransform(inst->GetItem(), container->GetItem(), AllowAll)) { + const Item_Struct* new_weapon = inst->GetItem(); + user->DeleteItemInInventory(Inventory::CalcSlotId(in_combine->container_slot, 0), 0, true); + container->Clear(); + user->SummonItem(new_weapon->ID, inst->GetCharges(), inst->GetAugmentItemID(0), inst->GetAugmentItemID(1), inst->GetAugmentItemID(2), inst->GetAugmentItemID(3), inst->GetAugmentItemID(4), inst->IsInstNoDrop(), MainCursor, container->GetItem()->Icon, atoi(container->GetItem()->IDFile + 2)); + user->Message_StringID(4, TRANSFORM_COMPLETE, inst->GetItem()->Name); + if (RuleB(Inventory, DeleteTransformationMold)) + user->DeleteItemInInventory(in_combine->container_slot, 0, true); + } + else if (inst) { + user->Message_StringID(4, TRANSFORM_FAILED, inst->GetItem()->Name); + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } + + if (container->GetItem() && container->GetItem()->BagType == BagTypeDetransformationmold) { + const ItemInst* inst = container->GetItem(0); + if (inst && inst->GetOrnamentationIcon() && inst->GetOrnamentationIcon()) { + const Item_Struct* new_weapon = inst->GetItem(); + user->DeleteItemInInventory(Inventory::CalcSlotId(in_combine->container_slot, 0), 0, true); + container->Clear(); + user->SummonItem(new_weapon->ID, inst->GetCharges(), inst->GetAugmentItemID(0), inst->GetAugmentItemID(1), inst->GetAugmentItemID(2), inst->GetAugmentItemID(3), inst->GetAugmentItemID(4), inst->IsInstNoDrop(), MainCursor, 0, 0); + user->Message_StringID(4, TRANSFORM_COMPLETE, inst->GetItem()->Name); + } + else if (inst) { + user->Message_StringID(4, DETRANSFORM_FAILED, inst->GetItem()->Name); + } + EQApplicationPacket* outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0); + user->QueuePacket(outapp); + safe_delete(outapp); + return; + } DBTradeskillRecipe_Struct spec; if (!database.GetTradeRecipe(container, c_type, some_id, user->CharacterID(), &spec)) { @@ -421,38 +459,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; @@ -463,17 +491,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]); @@ -488,10 +514,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) @@ -517,12 +542,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... @@ -536,19 +561,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); @@ -651,34 +671,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]); @@ -688,14 +700,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; @@ -708,39 +716,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; } @@ -770,20 +769,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) @@ -813,7 +810,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); @@ -928,20 +924,20 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { _log(TRADESKILLS__TRACE, "...Bonusstat: %d , INT: %d , WIS: %d , DEX: %d , STR: %d", bonusstat , GetINT() , GetWIS() , GetDEX() , GetSTR()); float res = MakeRandomFloat(0, 99); - int AAChance = 0; + int aa_chance = 0; //AA modifiers //can we do this with nested switches? if(spec->tradeskill == SkillAlchemy){ switch(GetAA(aaAlchemyMastery)){ case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -949,13 +945,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if(spec->tradeskill == SkillJewelryMaking){ switch(GetAA(aaJewelCraftMastery)){ case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -964,13 +960,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (spec->tradeskill == SkillBlacksmithing) { switch(GetAA(aaBlacksmithingMastery)) { case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -978,13 +974,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (spec->tradeskill == SkillBaking) { switch(GetAA(aaBakingMastery)) { case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -992,13 +988,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (spec->tradeskill == SkillBrewing) { switch(GetAA(aaBrewingMastery)) { case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -1006,13 +1002,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (spec->tradeskill == SkillFletching) { switch(GetAA(aaFletchingMastery2)) { case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -1020,13 +1016,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (spec->tradeskill == SkillPottery) { switch(GetAA(aaPotteryMastery)) { case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -1034,13 +1030,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (spec->tradeskill == SkillTailoring) { switch(GetAA(aaTailoringMastery)) { case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } @@ -1048,26 +1044,26 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (spec->tradeskill == SkillResearch) { switch(GetAA(aaArcaneTongues)) { case 1: - AAChance = 10; + aa_chance = 10; break; case 2: - AAChance = 25; + aa_chance = 25; break; case 3: - AAChance = 50; + aa_chance = 50; break; } } chance = mod_tradeskill_chance(chance, spec); - if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < AAChance){ + if (((spec->tradeskill==75) || GetGM() || (chance > res)) || MakeRandomInt(0, 99) < aa_chance){ success_modifier = 1; if(over_trivial < 0) CheckIncreaseTradeskill(bonusstat, stat_modifier, skillup_modifier, success_modifier, spec->tradeskill); - Message_StringID(4,TRADESKILL_SUCCEED,spec->name.c_str()); + Message_StringID(4, TRADESKILL_SUCCEED, spec->name.c_str()); _log(TRADESKILLS__TRACE, "Tradeskill success"); @@ -1076,16 +1072,24 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { //should we check this crap? SummonItem(itr->first, itr->second); item = database.GetItem(itr->first); - if (this->GetGroup()) - { - entity_list.MessageGroup(this,true,MT_Skills,"%s has successfully fashioned %s!",GetName(),item->Name); + if (this->GetGroup()) { + entity_list.MessageGroup(this, true, MT_Skills, "%s has successfully fashioned %s!", GetName(), item->Name); } + + /* QS: Player_Log_Trade_Skill_Events */ + if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ + std::string event_desc = StringFormat("Success :: fashioned recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); + } + if(RuleB(TaskSystem, EnableTaskSystem)) UpdateTasksForItem(ActivityTradeSkill, itr->first, itr->second); ++itr; } return(true); - } else { + } + /* Tradeskill Fail */ + else { success_modifier = 2; // Halves the chance if(over_trivial < 0) @@ -1097,6 +1101,13 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { if (this->GetGroup()) { entity_list.MessageGroup(this,true,MT_Skills,"%s was unsuccessful in %s tradeskill attempt.",GetName(),this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its"); + + } + + /* QS: Player_Log_Trade_Skill_Events */ + if (RuleB(QueryServ, PlayerLogTradeSkillEvents)){ + std::string event_desc = StringFormat("Failed :: recipe_id:%i tskillid:%i trivial:%i chance:%4.2f in zoneid:%i instid:%i", spec->recipe_id, spec->tradeskill, spec->trivial, chance, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Trade_Skill_Events, this->CharacterID(), event_desc); } itr = spec->onfail.begin(); @@ -1106,6 +1117,8 @@ bool Client::TradeskillExecute(DBTradeskillRecipe_Struct *spec) { ++itr; } + /* Salvage Item rolls */ + // Rolls on each item, is possible to return everything int SalvageChance = aabonuses.SalvageChance + itembonuses.SalvageChance + spellbonuses.SalvageChance; // Skip check if not a normal TS or if a quest recipe these should be nofail, but check amyways @@ -1152,7 +1165,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)) { @@ -1171,249 +1184,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); + 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; @@ -1422,141 +1398,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()); } @@ -1602,33 +1546,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 8c564c130..738e763ed 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -17,12 +17,15 @@ */ #include "../common/debug.h" #include "masterentity.h" -#include "StringIDs.h" -#include "../common/StringUtil.h" +#include "string_ids.h" +#include "../common/string_util.h" #include "../common/rulesys.h" -#include "QuestParserCollection.h" +#include "quest_parser_collection.h" #include "worldserver.h" +#include "queryserv.h" + extern WorldServer worldserver; +extern QueryServ* QServ; // The maximum amount of a single bazaar/barter transaction expressed in copper. // Equivalent to 2 Million plat @@ -71,8 +74,9 @@ void Trade::Start(uint32 mob_id, bool initiate_with) } // Add item from a given slot to trade bucket (automatically does bag data too) -void Trade::AddEntity(uint16 from_slot_id, uint16 trade_slot_id) -{ +void Trade::AddEntity(uint16 trade_slot_id, uint32 stack_size) { + // TODO: review for inventory saves / consider changing return type to bool so failure can be passed to desync handler + if (!owner || !owner->IsClient()) { // This should never happen LogFile->write(EQEMuLog::Debug, "Programming error: NPC's should not call Trade::AddEntity()"); @@ -87,45 +91,56 @@ void Trade::AddEntity(uint16 from_slot_id, uint16 trade_slot_id) // Item always goes into trade bucket from cursor Client* client = owner->CastToClient(); - const ItemInst* inst = client->GetInv().GetItem(MainCursor); + ItemInst* inst = client->GetInv().GetItem(MainCursor); + if (!inst) { client->Message(13, "Error: Could not find item on your cursor!"); return; } - _log(TRADING__HOLDER, "%s added item '%s' to trade slot %i", owner->GetName(), inst->GetItem()->Name, trade_slot_id); - ItemInst* inst2 = client->GetInv().GetItem(trade_slot_id); - int new_charges = 0; - if (!inst2 || !inst2->GetItem()) { - // Send all item data to other client - SendItemData(inst, trade_slot_id); - // Move item on cursor to the trade slots - client->PutItemInInventory(trade_slot_id, *inst); - } - else - { - if (client->GetInv().GetItem(MainCursor)->GetID() != client->GetInv().GetItem(trade_slot_id)->GetID()) { + + // it looks like the original code attempted to allow stacking... + // (it just didn't handle partial stack move actions -U) + if (stack_size > 0) { + if (!inst->IsStackable() || !inst2 || !inst2->GetItem() || (inst->GetID() != inst2->GetID()) || (stack_size > inst->GetCharges())) { client->Kick(); return; } - new_charges = (inst2->GetCharges()+inst->GetCharges()); - if (new_charges < inst2->GetItem()->StackSize) - { - inst2->SetCharges(new_charges); - new_charges = 0; - } - else - { - new_charges = inst->GetCharges()-(inst2->GetItem()->StackSize-inst2->GetCharges()); //Leftover charges = charges - difference + + uint32 _stack_size = 0; + + if ((stack_size + inst2->GetCharges()) > inst2->GetItem()->StackSize) { + _stack_size = (stack_size + inst2->GetCharges()) - inst->GetItem()->StackSize; inst2->SetCharges(inst2->GetItem()->StackSize); } + else { + _stack_size = inst->GetCharges() - stack_size; + inst2->SetCharges(stack_size + inst2->GetCharges()); + } + + _log(TRADING__HOLDER, "%s added partial item '%s' stack (qty: %i) to trade slot %i", owner->GetName(), inst->GetItem()->Name, stack_size, trade_slot_id); + + if (_stack_size > 0) + inst->SetCharges(_stack_size); + else + client->DeleteItemInInventory(MainCursor); + SendItemData(inst2, trade_slot_id); } - if (new_charges > 0) - client->GetInv().GetItem(from_slot_id)->SetCharges(new_charges); - else - client->DeleteItemInInventory(from_slot_id);//, (ItemInst&)trade_inst); + else { + if (inst2 && inst2->GetID()) { + 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); + + client->PutItemInInventory(trade_slot_id, *inst); + client->DeleteItemInInventory(MainCursor); + } } // Retrieve mob the owner is trading with @@ -304,200 +319,464 @@ void Trade::DumpTrade() #endif void Client::ResetTrade() { - const Item_Struct* TempItem = 0; - ItemInst* ins; - int x; AddMoneyToPP(trade->cp, trade->sp, trade->gp, trade->pp, true); - for(x = EmuConstants::TRADE_BEGIN; x <= EmuConstants::TRADE_END; x++) - { - TempItem = 0; - ins = GetInv().GetItem(x); - if (ins) - TempItem = ins->GetItem(); - if (TempItem) - { - bool is_arrow = (TempItem->ItemType == ItemTypeArrow) ? true : false; - int freeslotid = GetInv().FindFreeSlot(ins->IsType(ItemClassContainer), true, TempItem->Size, is_arrow); - if (freeslotid == INVALID_INDEX) - { - DropInst(ins); + + // step 1: process bags + for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) { + const ItemInst* inst = m_inv[trade_slot]; + + if (inst && inst->IsType(ItemClassContainer)) { + int16 free_slot = m_inv.FindFreeSlotForTradeItem(inst); + + if (free_slot != INVALID_INDEX) { + PutItemInInventory(free_slot, *inst); + SendItemPacket(free_slot, inst, ItemPacketTrade); } - else - { - PutItemInInventory(freeslotid, *ins); - SendItemPacket(freeslotid, ins, ItemPacketTrade); + else { + DropInst(inst); } - DeleteItemInInventory(x); + + DeleteItemInInventory(trade_slot); + } + } + + // step 2a: process stackables + for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) { + ItemInst* inst = GetInv().GetItem(trade_slot); + + if (inst && inst->IsStackable()) { + while (true) { + // there's no built-in safety check against an infinite loop..but, it should break on one of the conditional checks + int16 free_slot = m_inv.FindFreeSlotForTradeItem(inst); + + if ((free_slot == MainCursor) || (free_slot == INVALID_INDEX)) + break; + + ItemInst* partial_inst = GetInv().GetItem(free_slot); + + if (!partial_inst) + break; + + if (partial_inst->GetID() != inst->GetID()) { + _log(TRADING__ERROR, "Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()"); + + break; + } + + if ((partial_inst->GetCharges() + inst->GetCharges()) > partial_inst->GetItem()->StackSize) { + int16 new_charges = (partial_inst->GetCharges() + inst->GetCharges()) - partial_inst->GetItem()->StackSize; + + partial_inst->SetCharges(partial_inst->GetItem()->StackSize); + inst->SetCharges(new_charges); + } + else { + partial_inst->SetCharges(partial_inst->GetCharges() + inst->GetCharges()); + inst->SetCharges(0); + } + + PutItemInInventory(free_slot, *partial_inst); + SendItemPacket(free_slot, partial_inst, ItemPacketTrade); + + if (inst->GetCharges() == 0) { + DeleteItemInInventory(trade_slot); + + break; + } + } + } + } + + // step 2b: adjust trade stack bias + // (if any partial stacks exist before the final stack, FindFreeSlotForTradeItem() will return that slot in step 3 and an overwrite will occur) + for (int16 trade_slot = EmuConstants::TRADE_END; trade_slot >= EmuConstants::TRADE_BEGIN; --trade_slot) { + ItemInst* inst = GetInv().GetItem(trade_slot); + + if (inst && inst->IsStackable()) { + for (int16 bias_slot = EmuConstants::TRADE_BEGIN; bias_slot <= EmuConstants::TRADE_END; ++bias_slot) { + if (bias_slot >= trade_slot) + break; + + ItemInst* bias_inst = GetInv().GetItem(bias_slot); + + if (!bias_inst || (bias_inst->GetID() != inst->GetID()) || (bias_inst->GetCharges() >= bias_inst->GetItem()->StackSize)) + continue; + + if ((bias_inst->GetCharges() + inst->GetCharges()) > bias_inst->GetItem()->StackSize) { + int16 new_charges = (bias_inst->GetCharges() + inst->GetCharges()) - bias_inst->GetItem()->StackSize; + + bias_inst->SetCharges(bias_inst->GetItem()->StackSize); + inst->SetCharges(new_charges); + } + else { + bias_inst->SetCharges(bias_inst->GetCharges() + inst->GetCharges()); + inst->SetCharges(0); + } + + if (inst->GetCharges() == 0) { + DeleteItemInInventory(trade_slot); + + break; + } + } + } + } + + // step 3: process everything else + for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) { + const ItemInst* inst = m_inv[trade_slot]; + + if (inst) { + int16 free_slot = m_inv.FindFreeSlotForTradeItem(inst); + + if (free_slot != INVALID_INDEX) { + PutItemInInventory(free_slot, *inst); + SendItemPacket(free_slot, inst, ItemPacketTrade); + } + else { + DropInst(inst); + } + + DeleteItemInInventory(trade_slot); } } } -void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) { - +void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, std::list* event_details) { if(tradingWith && tradingWith->IsClient()) { Client* other = tradingWith->CastToClient(); + QSPlayerLogTrade_Struct* qs_audit = nullptr; + bool qs_log = false; if(other) { mlog(TRADING__CLIENT, "Finishing trade with client %s", other->GetName()); - int16 slot_id; - const Item_Struct* item = nullptr; - QSPlayerLogTrade_Struct* qsaudit = nullptr; - bool QSPLT = false; + this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true); + // step 0: pre-processing // QS code - if(qspack && RuleB(QueryServ, PlayerLogTrades)) { - qsaudit = (QSPlayerLogTrade_Struct*) qspack->pBuffer; - QSPLT = true; + if (RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) { + qs_audit = (QSPlayerLogTrade_Struct*)event_entry; + qs_log = true; - if(finalizer) { qsaudit->char2_id = this->character_id; } - else { qsaudit->char1_id = this->character_id; } + if (finalizer) { + qs_audit->char2_id = this->character_id; + + qs_audit->char2_money.platinum = this->trade->pp; + qs_audit->char2_money.gold = this->trade->gp; + qs_audit->char2_money.silver = this->trade->sp; + qs_audit->char2_money.copper = this->trade->cp; + } + else { + qs_audit->char1_id = this->character_id; + + qs_audit->char1_money.platinum = this->trade->pp; + qs_audit->char1_money.gold = this->trade->gp; + qs_audit->char1_money.silver = this->trade->sp; + qs_audit->char1_money.copper = this->trade->cp; + } } - // Move each trade slot into free inventory slot - for(int16 i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_END; i++){ - const ItemInst* inst = m_inv[i]; - uint16 parent_offset = 0; + // step 1: process bags + for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) { + const ItemInst* inst = m_inv[trade_slot]; - if(inst == nullptr) { continue; } + if (inst && inst->IsType(ItemClassContainer)) { + mlog(TRADING__CLIENT, "Giving container %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); - mlog(TRADING__CLIENT, "Giving %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, i, other->GetName()); + // TODO: need to check bag items/augments for no drop..everything for attuned... + if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) { + int16 free_slot = other->GetInv().FindFreeSlotForTradeItem(inst); - /// Log Player Trades through QueryServ if Rule Enabled - if(QSPLT) { - uint16 item_count = qsaudit->char1_count + qsaudit->char2_count; - parent_offset = item_count; + if (free_slot != INVALID_INDEX) { + if (other->PutItemInInventory(free_slot, *inst, true)) { + mlog(TRADING__CLIENT, "Container %s (%d) successfully transferred, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID); + if (qs_log) { + QSTradeItems_Struct* detail = new QSTradeItems_Struct; - qsaudit->items[item_count].from_id = this->character_id; - qsaudit->items[item_count].from_slot = i; - qsaudit->items[item_count].to_id = other->CharacterID(); - qsaudit->items[item_count].to_slot = 0; - qsaudit->items[item_count].item_id = inst->GetID(); - qsaudit->items[item_count].charges = inst->GetCharges(); - qsaudit->items[item_count].aug_1 = inst->GetAugmentItemID(1); - qsaudit->items[item_count].aug_2 = inst->GetAugmentItemID(2); - qsaudit->items[item_count].aug_3 = inst->GetAugmentItemID(3); - qsaudit->items[item_count].aug_4 = inst->GetAugmentItemID(4); - qsaudit->items[item_count].aug_5 = inst->GetAugmentItemID(5); + detail->from_id = this->character_id; + detail->from_slot = trade_slot; + detail->to_id = other->CharacterID(); + detail->to_slot = free_slot; + detail->item_id = inst->GetID(); + detail->charges = 1; + detail->aug_1 = inst->GetAugmentItemID(1); + detail->aug_2 = inst->GetAugmentItemID(2); + detail->aug_3 = inst->GetAugmentItemID(3); + detail->aug_4 = inst->GetAugmentItemID(4); + detail->aug_5 = inst->GetAugmentItemID(5); - if(finalizer) { qsaudit->char2_count++; } - else { qsaudit->char1_count++; } + event_details->push_back(detail); - if(inst->IsType(ItemClassContainer)) { - // Pseudo-Slot ID's are generated based on how the db saves bag items... - for(uint8 j = SUB_BEGIN; j < inst->GetItem()->BagSlots; j++) { - const ItemInst* baginst = inst->GetItem(j); + if (finalizer) + qs_audit->char2_count += detail->charges; + else + qs_audit->char1_count += detail->charges; - if(baginst == nullptr) { continue; } + //for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) { + for (uint8 sub_slot = SUB_BEGIN; (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++sub_slot) { // this is to catch ALL items + const ItemInst* bag_inst = inst->GetItem(sub_slot); - int16 k=Inventory::CalcSlotId(i, j); - item_count = qsaudit->char1_count + qsaudit->char2_count; + if (bag_inst) { + detail = new QSTradeItems_Struct; - qsaudit->items[item_count].from_id = this->character_id; - qsaudit->items[item_count].from_slot = k; - qsaudit->items[item_count].to_id = other->CharacterID(); - qsaudit->items[item_count].to_slot = 0; - qsaudit->items[item_count].item_id = baginst->GetID(); - qsaudit->items[item_count].charges = baginst->GetCharges(); - qsaudit->items[item_count].aug_1 = baginst->GetAugmentItemID(1); - qsaudit->items[item_count].aug_2 = baginst->GetAugmentItemID(2); - qsaudit->items[item_count].aug_3 = baginst->GetAugmentItemID(3); - qsaudit->items[item_count].aug_4 = baginst->GetAugmentItemID(4); - qsaudit->items[item_count].aug_5 = baginst->GetAugmentItemID(5); + detail->from_id = this->character_id; + detail->from_slot = Inventory::CalcSlotId(trade_slot, sub_slot); + detail->to_id = other->CharacterID(); + detail->to_slot = Inventory::CalcSlotId(free_slot, sub_slot); + detail->item_id = bag_inst->GetID(); + detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges()); + detail->aug_1 = bag_inst->GetAugmentItemID(1); + detail->aug_2 = bag_inst->GetAugmentItemID(2); + detail->aug_3 = bag_inst->GetAugmentItemID(3); + detail->aug_4 = bag_inst->GetAugmentItemID(4); + detail->aug_5 = bag_inst->GetAugmentItemID(5); - if(finalizer) { qsaudit->char2_count++; } - else { qsaudit->char1_count++; } - } - } - } + event_details->push_back(detail); - if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) { - bool is_arrow = (inst->GetItem()->ItemType == ItemTypeArrow) ? true : false; - slot_id = other->GetInv().FindFreeSlot(inst->IsType(ItemClassContainer), true, inst->GetItem()->Size, is_arrow); - - mlog(TRADING__CLIENT, "Trying to put %s (%d) into slot %d", inst->GetItem()->Name, inst->GetItem()->ID, slot_id); - - if(other->PutItemInInventory(slot_id, *inst, true)) { - mlog(TRADING__CLIENT, "Item %s (%d) successfully transfered, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID); - - if(QSPLT) { - qsaudit->items[parent_offset].to_slot = slot_id; - - if(inst->IsType(ItemClassContainer)) { - for(uint8 bagslot_idx = SUB_BEGIN; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) { - const ItemInst* bag_inst = inst->GetItem(bagslot_idx); - - if(bag_inst == nullptr) { continue; } - int16 to_bagslot_id = Inventory::CalcSlotId(slot_id, bagslot_idx); - - qsaudit->items[++parent_offset].to_slot = to_bagslot_id; + if (finalizer) + qs_audit->char2_count += detail->charges; + else + qs_audit->char1_count += detail->charges; + } + } } } + else { + mlog(TRADING__ERROR, "Transfer of container %s (%d) to %s failed, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName()); + PushItemOnCursor(*inst, true); + } + } + else { + mlog(TRADING__ERROR, "%s's inventory is full, returning container %s (%d) to giver.", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID); + PushItemOnCursor(*inst, true); } } else { + mlog(TRADING__ERROR, "Container %s (%d) is NoDrop, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID); PushItemOnCursor(*inst, true); - mlog(TRADING__ERROR, "Unable to give item %d (%d) to %s, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName()); - - if(QSPLT) { - qsaudit->items[parent_offset].to_id = this->character_id; - qsaudit->items[parent_offset].to_slot = MainCursor; - - if(inst->IsType(ItemClassContainer)) { - for(uint8 bagslot_idx = SUB_BEGIN; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) { - const ItemInst* bag_inst = inst->GetItem(bagslot_idx); - - if(bag_inst == nullptr) { continue; } - int16 to_bagslot_id = Inventory::CalcSlotId(MainCursor, bagslot_idx); - - qsaudit->items[++parent_offset].to_id = this->character_id; - qsaudit->items[parent_offset].to_slot = to_bagslot_id; - } - } - } } - DeleteItemInInventory(i); + DeleteItemInInventory(trade_slot); } - else { - PushItemOnCursor(*inst, true); - DeleteItemInInventory(i); + } - if(QSPLT) { - qsaudit->items[parent_offset].to_id = this->character_id; - qsaudit->items[parent_offset].to_slot = MainCursor; + // step 2a: process stackables + for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) { + ItemInst* inst = GetInv().GetItem(trade_slot); - if(inst->IsType(ItemClassContainer)) { - for(uint8 bagslot_idx = SUB_BEGIN; bagslot_idx < inst->GetItem()->BagSlots; bagslot_idx++) { - const ItemInst* bag_inst = inst->GetItem(bagslot_idx); + if (inst && inst->IsStackable()) { + while (true) { + // there's no built-in safety check against an infinite loop..but, it should break on one of the conditional checks + int16 partial_slot = other->GetInv().FindFreeSlotForTradeItem(inst); - if(bag_inst == nullptr) { continue; } - int16 to_bagslot_id = Inventory::CalcSlotId(MainCursor, bagslot_idx); + if ((partial_slot == MainCursor) || (partial_slot == INVALID_INDEX)) + break; - qsaudit->items[++parent_offset].to_id = this->character_id; - qsaudit->items[parent_offset].to_slot = to_bagslot_id; + ItemInst* partial_inst = other->GetInv().GetItem(partial_slot); + + if (!partial_inst) + break; + + if (partial_inst->GetID() != inst->GetID()) { + _log(TRADING__ERROR, "Client::ResetTrade() - an incompatible location reference was returned by Inventory::FindFreeSlotForTradeItem()"); + break; + } + + int16 old_charges = inst->GetCharges(); + int16 partial_charges = partial_inst->GetCharges(); + + if ((partial_inst->GetCharges() + inst->GetCharges()) > partial_inst->GetItem()->StackSize) { + int16 new_charges = (partial_inst->GetCharges() + inst->GetCharges()) - partial_inst->GetItem()->StackSize; + + partial_inst->SetCharges(partial_inst->GetItem()->StackSize); + inst->SetCharges(new_charges); + } + else { + partial_inst->SetCharges(partial_inst->GetCharges() + inst->GetCharges()); + inst->SetCharges(0); + } + + mlog(TRADING__CLIENT, "Transferring partial stack %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); + + if (other->PutItemInInventory(partial_slot, *partial_inst, true)) { + mlog(TRADING__CLIENT, "Partial stack %s (%d) successfully transferred, deleting %i charges from trade slot.", + inst->GetItem()->Name, inst->GetItem()->ID, (old_charges - inst->GetCharges())); + if (qs_log) { + QSTradeItems_Struct* detail = new QSTradeItems_Struct; + + detail->from_id = this->character_id; + detail->from_slot = trade_slot; + detail->to_id = other->CharacterID(); + detail->to_slot = partial_slot; + detail->item_id = inst->GetID(); + detail->charges = (old_charges - inst->GetCharges()); + detail->aug_1 = 0; + detail->aug_2 = 0; + detail->aug_3 = 0; + detail->aug_4 = 0; + detail->aug_5 = 0; + + event_details->push_back(detail); + + if (finalizer) + qs_audit->char2_count += detail->charges; + else + qs_audit->char1_count += detail->charges; } } + else { + mlog(TRADING__ERROR, "Transfer of partial stack %s (%d) to %s failed, returning %i charges to trade slot.", + inst->GetItem()->Name, inst->GetItem()->ID, other->GetName(), (old_charges - inst->GetCharges())); + + inst->SetCharges(old_charges); + partial_inst->SetCharges(partial_charges); + break; + } + + if (inst->GetCharges() == 0) { + DeleteItemInInventory(trade_slot); + break; + } } } } - // Money - look into how NPC's receive cash - this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true); + // step 2b: adjust trade stack bias + // (if any partial stacks exist before the final stack, FindFreeSlotForTradeItem() will return that slot in step 3 and an overwrite will occur) + for (int16 trade_slot = EmuConstants::TRADE_END; trade_slot >= EmuConstants::TRADE_BEGIN; --trade_slot) { + ItemInst* inst = GetInv().GetItem(trade_slot); - // This is currently setup to show character offers, not receipts - if(QSPLT) { - if(finalizer) { - qsaudit->char2_money.platinum = this->trade->pp; - qsaudit->char2_money.gold = this->trade->gp; - qsaudit->char2_money.silver = this->trade->sp; - qsaudit->char2_money.copper = this->trade->cp; + if (inst && inst->IsStackable()) { + for (int16 bias_slot = EmuConstants::TRADE_BEGIN; bias_slot <= EmuConstants::TRADE_END; ++bias_slot) { + if (bias_slot >= trade_slot) + break; + + ItemInst* bias_inst = GetInv().GetItem(bias_slot); + + if (!bias_inst || (bias_inst->GetID() != inst->GetID()) || (bias_inst->GetCharges() >= bias_inst->GetItem()->StackSize)) + continue; + + int16 old_charges = inst->GetCharges(); + + if ((bias_inst->GetCharges() + inst->GetCharges()) > bias_inst->GetItem()->StackSize) { + int16 new_charges = (bias_inst->GetCharges() + inst->GetCharges()) - bias_inst->GetItem()->StackSize; + + bias_inst->SetCharges(bias_inst->GetItem()->StackSize); + inst->SetCharges(new_charges); + } + else { + bias_inst->SetCharges(bias_inst->GetCharges() + inst->GetCharges()); + inst->SetCharges(0); + } + + if (qs_log) { + QSTradeItems_Struct* detail = new QSTradeItems_Struct; + + detail->from_id = this->character_id; + detail->from_slot = trade_slot; + detail->to_id = this->character_id; + detail->to_slot = bias_slot; + detail->item_id = inst->GetID(); + detail->charges = (old_charges - inst->GetCharges()); + detail->aug_1 = 0; + detail->aug_2 = 0; + detail->aug_3 = 0; + detail->aug_4 = 0; + detail->aug_5 = 0; + + event_details->push_back(detail); + } + + if (inst->GetCharges() == 0) { + DeleteItemInInventory(trade_slot); + break; + } + } } - else { - qsaudit->char1_money.platinum = this->trade->pp; - qsaudit->char1_money.gold = this->trade->gp; - qsaudit->char1_money.silver = this->trade->sp; - qsaudit->char1_money.copper = this->trade->cp; + } + + // step 3: process everything else + for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_END; ++trade_slot) { + const ItemInst* inst = m_inv[trade_slot]; + + if (inst) { + mlog(TRADING__CLIENT, "Giving item %s (%d) in slot %d to %s", inst->GetItem()->Name, inst->GetItem()->ID, trade_slot, other->GetName()); + + // TODO: need to check bag items/augments for no drop..everything for attuned... + if (inst->GetItem()->NoDrop != 0 || Admin() >= RuleI(Character, MinStatusForNoDropExemptions) || RuleI(World, FVNoDropFlag) == 1 || other == this) { + int16 free_slot = other->GetInv().FindFreeSlotForTradeItem(inst); + + if (free_slot != INVALID_INDEX) { + if (other->PutItemInInventory(free_slot, *inst, true)) { + mlog(TRADING__CLIENT, "Item %s (%d) successfully transferred, deleting from trade slot.", inst->GetItem()->Name, inst->GetItem()->ID); + if (qs_log) { + QSTradeItems_Struct* detail = new QSTradeItems_Struct; + + detail->from_id = this->character_id; + detail->from_slot = trade_slot; + detail->to_id = other->CharacterID(); + detail->to_slot = free_slot; + detail->item_id = inst->GetID(); + detail->charges = (!inst->IsStackable() ? 1 : inst->GetCharges()); + detail->aug_1 = inst->GetAugmentItemID(1); + detail->aug_2 = inst->GetAugmentItemID(2); + detail->aug_3 = inst->GetAugmentItemID(3); + detail->aug_4 = inst->GetAugmentItemID(4); + detail->aug_5 = inst->GetAugmentItemID(5); + + event_details->push_back(detail); + + if (finalizer) + qs_audit->char2_count += detail->charges; + else + qs_audit->char1_count += detail->charges; + + // 'step 3' should never really see containers..but, just in case... + //for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) { + for (uint8 sub_slot = SUB_BEGIN; (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE); ++sub_slot) { // this is to catch ALL items + const ItemInst* bag_inst = inst->GetItem(sub_slot); + + if (bag_inst) { + detail = new QSTradeItems_Struct; + + detail->from_id = this->character_id; + detail->from_slot = trade_slot; + detail->to_id = other->CharacterID(); + detail->to_slot = free_slot; + detail->item_id = bag_inst->GetID(); + detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges()); + detail->aug_1 = bag_inst->GetAugmentItemID(1); + detail->aug_2 = bag_inst->GetAugmentItemID(2); + detail->aug_3 = bag_inst->GetAugmentItemID(3); + detail->aug_4 = bag_inst->GetAugmentItemID(4); + detail->aug_5 = bag_inst->GetAugmentItemID(5); + + event_details->push_back(detail); + + if (finalizer) + qs_audit->char2_count += detail->charges; + else + qs_audit->char1_count += detail->charges; + } + } + } + } + else { + mlog(TRADING__ERROR, "Transfer of Item %s (%d) to %s failed, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID, other->GetName()); + PushItemOnCursor(*inst, true); + } + } + else { + mlog(TRADING__ERROR, "%s's inventory is full, returning item %s (%d) to giver.", other->GetName(), inst->GetItem()->Name, inst->GetItem()->ID); + PushItemOnCursor(*inst, true); + } + } + else { + mlog(TRADING__ERROR, "Item %s (%d) is NoDrop, returning to giver.", inst->GetItem()->Name, inst->GetItem()->ID); + PushItemOnCursor(*inst, true); + } + + DeleteItemInInventory(trade_slot); } } @@ -505,62 +784,72 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) } } else if(tradingWith && tradingWith->IsNPC()) { - QSPlayerLogHandin_Struct* qsaudit = nullptr; - bool QSPLH = false; + QSPlayerLogHandin_Struct* qs_audit = nullptr; + bool qs_log = false; // QS code - if(qspack && RuleB(QueryServ, PlayerLogTrades)) { + if(RuleB(QueryServ, PlayerLogTrades) && event_entry && event_details) { // Currently provides only basic functionality. Calling method will also // need to be modified before item returns and rewards can be logged. -U - qsaudit = (QSPlayerLogHandin_Struct*) qspack->pBuffer; - QSPLH = true; + qs_audit = (QSPlayerLogHandin_Struct*)event_entry; + qs_log = true; - qsaudit->quest_id = 0; - qsaudit->char_id = character_id; - qsaudit->char_money.platinum = trade->pp; - qsaudit->char_money.gold = trade->gp; - qsaudit->char_money.silver = trade->sp; - qsaudit->char_money.copper = trade->cp; - qsaudit->char_count = 0; - qsaudit->npc_id = tradingWith->GetNPCTypeID(); - qsaudit->npc_money.platinum = 0; - qsaudit->npc_money.gold = 0; - qsaudit->npc_money.silver = 0; - qsaudit->npc_money.copper = 0; - qsaudit->npc_count = 0; + qs_audit->quest_id = 0; + qs_audit->char_id = character_id; + qs_audit->char_money.platinum = trade->pp; + qs_audit->char_money.gold = trade->gp; + qs_audit->char_money.silver = trade->sp; + qs_audit->char_money.copper = trade->cp; + qs_audit->char_count = 0; + qs_audit->npc_id = tradingWith->GetNPCTypeID(); + qs_audit->npc_money.platinum = 0; + qs_audit->npc_money.gold = 0; + qs_audit->npc_money.silver = 0; + qs_audit->npc_money.copper = 0; + qs_audit->npc_count = 0; } - if(QSPLH) { // This can be incoporated below when revisions are made -U - for(int16 slot_id = EmuConstants::TRADE_BEGIN; slot_id <= EmuConstants::TRADE_NPC_END; slot_id++) { - const ItemInst* trade_inst = m_inv[slot_id]; + if(qs_log) { // This can be incorporated below when revisions are made -U + for (int16 trade_slot = EmuConstants::TRADE_BEGIN; trade_slot <= EmuConstants::TRADE_NPC_END; ++trade_slot) { + const ItemInst* trade_inst = m_inv[trade_slot]; if(trade_inst) { - strcpy(qsaudit->items[qsaudit->char_count].action_type, "HANDIN"); + QSHandinItems_Struct* detail = new QSHandinItems_Struct; - qsaudit->items[qsaudit->char_count].char_slot = slot_id; - qsaudit->items[qsaudit->char_count].item_id = trade_inst->GetID(); - qsaudit->items[qsaudit->char_count].charges = trade_inst->GetCharges(); - qsaudit->items[qsaudit->char_count].aug_1 = trade_inst->GetAugmentItemID(1); - qsaudit->items[qsaudit->char_count].aug_2 = trade_inst->GetAugmentItemID(2); - qsaudit->items[qsaudit->char_count].aug_3 = trade_inst->GetAugmentItemID(3); - qsaudit->items[qsaudit->char_count].aug_4 = trade_inst->GetAugmentItemID(4); - qsaudit->items[qsaudit->char_count++].aug_5 = trade_inst->GetAugmentItemID(5); + strcpy(detail->action_type, "HANDIN"); + + detail->char_slot = trade_slot; + detail->item_id = trade_inst->GetID(); + detail->charges = (!trade_inst->IsStackable() ? 1 : trade_inst->GetCharges()); + detail->aug_1 = trade_inst->GetAugmentItemID(1); + detail->aug_2 = trade_inst->GetAugmentItemID(2); + detail->aug_3 = trade_inst->GetAugmentItemID(3); + detail->aug_4 = trade_inst->GetAugmentItemID(4); + detail->aug_5 = trade_inst->GetAugmentItemID(5); + + event_details->push_back(detail); + qs_audit->char_count += detail->charges; if(trade_inst->IsType(ItemClassContainer)) { - for(uint8 bag_idx = SUB_BEGIN; bag_idx < trade_inst->GetItem()->BagSlots; bag_idx++) { - const ItemInst* trade_baginst = trade_inst->GetItem(bag_idx); + for (uint8 sub_slot = SUB_BEGIN; sub_slot < trade_inst->GetItem()->BagSlots; ++sub_slot) { + const ItemInst* trade_baginst = trade_inst->GetItem(sub_slot); if(trade_baginst) { - strcpy(qsaudit->items[qsaudit->char_count].action_type, "HANDIN"); + detail = new QSHandinItems_Struct; - qsaudit->items[qsaudit->char_count].char_slot = Inventory::CalcSlotId(slot_id, bag_idx); - qsaudit->items[qsaudit->char_count].item_id = trade_baginst->GetID(); - qsaudit->items[qsaudit->char_count].charges = trade_baginst->GetCharges(); - qsaudit->items[qsaudit->char_count].aug_1 = trade_baginst->GetAugmentItemID(1); - qsaudit->items[qsaudit->char_count].aug_2 = trade_baginst->GetAugmentItemID(2); - qsaudit->items[qsaudit->char_count].aug_3 = trade_baginst->GetAugmentItemID(3); - qsaudit->items[qsaudit->char_count].aug_4 = trade_baginst->GetAugmentItemID(4); - qsaudit->items[qsaudit->char_count++].aug_5 = trade_baginst->GetAugmentItemID(5); + strcpy(detail->action_type, "HANDIN"); + + detail->char_slot = Inventory::CalcSlotId(trade_slot, sub_slot); + detail->item_id = trade_baginst->GetID(); + detail->charges = (!trade_inst->IsStackable() ? 1 : trade_inst->GetCharges()); + detail->aug_1 = trade_baginst->GetAugmentItemID(1); + detail->aug_2 = trade_baginst->GetAugmentItemID(2); + detail->aug_3 = trade_baginst->GetAugmentItemID(3); + detail->aug_4 = trade_baginst->GetAugmentItemID(4); + detail->aug_5 = trade_baginst->GetAugmentItemID(5); + + event_details->push_back(detail); + qs_audit->char_count += detail->charges; } } } @@ -574,7 +863,7 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) quest_npc = true; } - std::vector item_list; + std::vector item_list; uint32 items[4] = { 0 }; for(int i = EmuConstants::TRADE_BEGIN; i <= EmuConstants::TRADE_NPC_END; ++i) { ItemInst *inst = m_inv.GetItem(i); @@ -582,7 +871,7 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) items[i - EmuConstants::TRADE_BEGIN] = inst->GetItem()->ID; item_list.push_back(inst); } else { - item_list.push_back(nullptr); + item_list.push_back((ItemInst*)nullptr); continue; } @@ -597,7 +886,7 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) 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)) { @@ -606,8 +895,8 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) } } } - - 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 @@ -623,6 +912,9 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) if(UpdateTasksOnDeliver(items, Cash, tradingWith->GetNPCTypeID())) { if(!tradingWith->IsMoving()) tradingWith->FaceTarget(this); + + this->EVENT_ITEM_ScriptStopReturn(); + } } @@ -639,12 +931,12 @@ void Client::FinishTrade(Mob* tradingWith, ServerPacket* qspack, bool finalizer) 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); @@ -1176,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); } @@ -1325,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) { @@ -2005,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) { @@ -2144,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/trap.cpp b/zone/trap.cpp index 2ab431c24..44c177118 100644 --- a/zone/trap.cpp +++ b/zone/trap.cpp @@ -20,8 +20,8 @@ #include "entity.h" #include "masterentity.h" #include "../common/spdat.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" +#include "../common/misc_functions.h" +#include "../common/string_util.h" /* @@ -262,45 +262,36 @@ Mob* EntityList::GetTrapTrigger(Trap* trap) { //todo: rewrite this to not need direct access to trap members. bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - // int char_num = 0; - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id,x,y,z,effect,effectvalue,effectvalue2,skill,maxzdiff,radius,chance,message,respawn_time,respawn_var,level FROM traps WHERE zone='%s' AND version=%u", zonename, version), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - lengths = mysql_fetch_lengths(result); - Trap* trap = new Trap(); - trap->trap_id = atoi(row[0]); - trap->x = atof(row[1]); - trap->y = atof(row[2]); - trap->z = atof(row[3]); - trap->effect = atoi(row[4]); - trap->effectvalue = atoi(row[5]); - trap->effectvalue2 = atoi(row[6]); - trap->skill = atoi(row[7]); - trap->maxzdiff = atof(row[8]); - trap->radius = atof(row[9]); - trap->chance = atoi(row[10]); - trap->message = row[11]; - trap->respawn_time = atoi(row[12]); - trap->respawn_var = atoi(row[13]); - trap->level = atoi(row[14]); - entity_list.AddTrap(trap); - trap->CreateHiddenTrigger(); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, " + "maxzdiff, radius, chance, message, respawn_time, respawn_var, level " + "FROM traps WHERE zone='%s' AND version=%u", zonename, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTraps query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) { + Trap* trap = new Trap(); + trap->trap_id = atoi(row[0]); + trap->x = atof(row[1]); + trap->y = atof(row[2]); + trap->z = atof(row[3]); + trap->effect = atoi(row[4]); + trap->effectvalue = atoi(row[5]); + trap->effectvalue2 = atoi(row[6]); + trap->skill = atoi(row[7]); + trap->maxzdiff = atof(row[8]); + trap->radius = atof(row[9]); + trap->chance = atoi(row[10]); + trap->message = row[11]; + trap->respawn_time = atoi(row[12]); + trap->respawn_var = atoi(row[13]); + trap->level = atoi(row[14]); + entity_list.AddTrap(trap); + trap->CreateHiddenTrigger(); + } return true; } diff --git a/zone/tribute.cpp b/zone/tribute.cpp index 63166cc41..08bbf4662 100644 --- a/zone/tribute.cpp +++ b/zone/tribute.cpp @@ -20,7 +20,7 @@ #include "../common/features.h" #include "masterentity.h" #include "../common/packet_dump.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" #include #include @@ -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); } @@ -378,68 +380,60 @@ void Client::SendGuildTributes() { } bool ZoneDatabase::LoadTributes() { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - TributeData t; - memset(&t.tiers, 0, sizeof(t.tiers)); - t.tier_count = 0; + TributeData tributeData; + memset(&tributeData.tiers, 0, sizeof(tributeData.tiers)); + tributeData.tier_count = 0; tribute_list.clear(); - const char *query = "SELECT id,name,descr,unknown,isguild FROM tributes"; - if (RunQuery(query, strlen(query), errbuf, &result)) { - int r; - while ((row = mysql_fetch_row(result))) { - r = 0; - uint32 id = atoul(row[r++]); - t.name = row[r++]; - t.description = row[r++]; - t.unknown = strtoul(row[r++], nullptr, 10); - t.is_guild = atol(row[r++])==0?false:true; - - tribute_list[id] = t; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes first query '%s': %s", query, errbuf); + const std::string query = "SELECT id, name, descr, unknown, isguild FROM tributes"; + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes first query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = atoul(row[0]); + tributeData.name = row[1]; + tributeData.description = row[2]; + tributeData.unknown = strtoul(row[3], nullptr, 10); + tributeData.is_guild = atol(row[4]) == 0? false: true; - const char *query2 = "SELECT tribute_id,level,cost,item_id FROM tribute_levels ORDER BY tribute_id,level"; - if (RunQuery(query2, strlen(query2), errbuf, &result)) { - int r; - while ((row = mysql_fetch_row(result))) { - r = 0; - uint32 id = atoul(row[r++]); + tribute_list[id] = tributeData; + } - if(tribute_list.count(id) != 1) { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes: unknown tribute %lu in tribute_levels", (unsigned long)id); - continue; - } - - TributeData &cur = tribute_list[id]; - - if(cur.tier_count >= MAX_TRIBUTE_TIERS) { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes: on tribute %lu: more tiers defined than permitted", (unsigned long)id); - continue; - } - - TributeLevel_Struct &s = cur.tiers[cur.tier_count]; - - s.level = atoul(row[r++]); - s.cost = atoul(row[r++]); - s.tribute_item_id = atoul(row[r++]); - cur.tier_count++; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadTributes level query '%s': %s", query, errbuf); + const std::string query2 = "SELECT tribute_id, level, cost, item_id FROM tribute_levels ORDER BY tribute_id, level"; + results = QueryDatabase(query2); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes level query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return false; } + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = atoul(row[0]); + + if(tribute_list.count(id) != 1) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes: unknown tribute %lu in tribute_levels", (unsigned long)id); + continue; + } + + TributeData &cur = tribute_list[id]; + + if(cur.tier_count >= MAX_TRIBUTE_TIERS) { + LogFile->write(EQEMuLog::Error, "Error in LoadTributes: on tribute %lu: more tiers defined than permitted", (unsigned long)id); + continue; + } + + TributeLevel_Struct &s = cur.tiers[cur.tier_count]; + + s.level = atoul(row[1]); + s.cost = atoul(row[2]); + s.tribute_item_id = atoul(row[3]); + cur.tier_count++; + } + return true; } diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index eb22f13a8..908f0f946 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -19,21 +19,17 @@ #ifdef _EQDEBUG #include #endif -//#include -#include -#include -#include "npc.h" -#include "masterentity.h" -#include "NpcAI.h" -#include "map.h" -#include "water_map.h" -#include "../common/moremath.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" -#include "../common/rulesys.h" + #include "../common/features.h" -#include "QuestParserCollection.h" +#include "../common/misc_functions.h" +#include "../common/rulesys.h" +#include "../common/string_util.h" +#include "map.h" +#include "npc.h" +#include "quest_parser_collection.h" +#include "water_map.h" +#include +#include struct wp_distance { @@ -567,7 +563,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b { Map::Vertex dest(x_pos, y_pos, z_pos); - float newz = zone->zonemap->FindBestZ(dest, nullptr); + float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); @@ -696,7 +692,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b { Map::Vertex dest(x_pos, y_pos, z_pos); - float newz = zone->zonemap->FindBestZ(dest, nullptr); + float newz = zone->zonemap->FindBestZ(dest, nullptr); + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); @@ -821,7 +817,7 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec { Map::Vertex dest(x_pos, y_pos, z_pos); - float newz = zone->zonemap->FindBestZ(dest, nullptr); + float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f; mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos); @@ -869,97 +865,78 @@ void NPC::AssignWaypoints(int32 grid) { return; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - bool GridErr = false, WPErr = false; Waypoints.clear(); + roamer = false; // Retrieve the wander and pause types for this grid - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `type`,`type2` FROM `grid` WHERE `id`=%i AND `zoneid`=%i",grid,zone->GetZoneID()),errbuf, &result)) - { - if((row = mysql_fetch_row(result))) - { - if(row[0] != 0) - wandertype = atoi(row[0]); - else - wandertype = 0; - if(row[1] != 0) - pausetype = atoi(row[1]); - else - pausetype = 0; - } - else // No grid record found in this zone for the given ID - GridErr = true; - mysql_free_result(result); + std::string query = StringFormat("SELECT `type`, `type2` FROM `grid` WHERE `id` = %i AND `zoneid` = %i", grid, zone->GetZoneID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign grid %u to mob %s: %s", grid, name, results.ErrorMessage().c_str()); + return; } - else // DB query error! - { - GridErr = true; - LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign grid %u to mob %s: %s", grid, name, errbuf); - } - safe_delete_array(query); - if(!GridErr) - { - this->CastToNPC()->SetGrid(grid); // Assign grid number + if (results.RowCount() == 0) + return; - // Retrieve all waypoints for this grid - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT `x`,`y`,`z`,`pause`,`heading` FROM grid_entries WHERE `gridid`=%i AND `zoneid`=%i ORDER BY `number`",grid,zone->GetZoneID()),errbuf,&result)) - { - roamer = true; - max_wp = -1; // Initialize it; will increment it for each waypoint successfully added to the list + auto row = results.begin(); - while((row = mysql_fetch_row(result))) - { - if(row[0] != 0 && row[1] != 0 && row[2] != 0 && row[3] != 0) - { - wplist newwp; - newwp.index = ++max_wp; - newwp.x = atof(row[0]); - newwp.y = atof(row[1]); - newwp.z = atof(row[2]); + wandertype = atoi(row[0]); + pausetype = atoi(row[1]); - if(zone->HasMap() && RuleB(Map, FixPathingZWhenLoading) ) - { - if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() || - (zone->HasWaterMap() && !zone->watermap->InWater(newwp.x, newwp.y, newwp.z))) - { - Map::Vertex dest(newwp.x, newwp.y, newwp.z); - float newz = zone->zonemap->FindBestZ(dest, nullptr); + this->CastToNPC()->SetGrid(grid); // Assign grid number - if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaLoading)) - newwp.z = newz + 1; - } - } + // Retrieve all waypoints for this grid + query = StringFormat("SELECT `x`,`y`,`z`,`pause`,`heading` " + "FROM grid_entries WHERE `gridid` = %i AND `zoneid` = %i " + "ORDER BY `number`", grid, zone->GetZoneID()); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign waypoints from grid %u to mob %s: %s", grid, name, results.ErrorMessage().c_str()); + return; + } + + roamer = true; + max_wp = 0; // Initialize it; will increment it for each waypoint successfully added to the list + + for (auto row = results.begin(); row != results.end(); ++row, ++max_wp) + { + wplist newwp; + newwp.index = max_wp; + newwp.x = atof(row[0]); + newwp.y = atof(row[1]); + newwp.z = atof(row[2]); + + if(zone->HasMap() && RuleB(Map, FixPathingZWhenLoading) ) + { + if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() || + (zone->HasWaterMap() && !zone->watermap->InWater(newwp.x, newwp.y, newwp.z))) + { + Map::Vertex dest(newwp.x, newwp.y, newwp.z); + + float newz = zone->zonemap->FindBestZ(dest, nullptr); + + if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaLoading)) + newwp.z = newz + 1; + } + } + + newwp.pause = atoi(row[3]); + newwp.heading = atof(row[4]); + Waypoints.push_back(newwp); + } - newwp.pause = atoi(row[3]); - newwp.heading = atof(row[4]); - Waypoints.push_back(newwp); - } - } - mysql_free_result(result); - } - else // DB query error! - { - WPErr = true; - LogFile->write(EQEMuLog::Error, "MySQL Error while trying to assign waypoints from grid %u to mob %s: %s", grid, name, errbuf); - } - safe_delete_array(query); - } // end if (!GridErr) if(Waypoints.size() < 2) { roamer = false; - } else if(!GridErr && !WPErr) { - UpdateWaypoint(0); - SetWaypointPause(); - if (wandertype == 1 || wandertype == 2 || wandertype == 5) - CalculateNewWaypoint(); - } else { - roamer = false; } + + UpdateWaypoint(0); + SetWaypointPause(); + + if (wandertype == 1 || wandertype == 2 || wandertype == 5) + CalculateNewWaypoint(); + } void Mob::SendTo(float new_x, float new_y, float new_z) { @@ -1026,182 +1003,142 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) { } int ZoneDatabase::GetHighestGrid(uint32 zoneid) { - char *query = 0; - char errbuff[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - int res = 0; - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", - zoneid),errbuff,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - res = atoi( row[0] ); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query, errbuff); - safe_delete_array(query); - } - return(res); + std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetHighestGrid query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } uint8 ZoneDatabase::GetGridType2(uint32 grid, uint16 zoneid) { - char *query = 0; - char errbuff[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - int type2 = 0; - if (RunQuery(query, MakeAnyLenString(&query,"SELECT type2 from grid where id = %i and zoneid = %i",grid,zoneid),errbuff,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - type2 = atoi( row[0] ); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetGridType2 query '%s': %s", query, errbuff); - safe_delete_array(query); - } - return(type2); + int type2 = 0; + std::string query = StringFormat("SELECT type2 FROM grid WHERE id = %i AND zoneid = %i", grid, zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetGridType2 query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } bool ZoneDatabase::GetWaypoints(uint32 grid, uint16 zoneid, uint32 num, wplist* wp) { - char *query = 0; - char errbuff[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query,"SELECT x, y, z, pause, heading from grid_entries where gridid = %i and number = %i and zoneid = %i",grid,num,zoneid),errbuff,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - if ( wp ) { - wp->x = atof( row[0] ); - wp->y = atof( row[1] ); - wp->z = atof( row[2] ); - wp->pause = atoi( row[3] ); - wp->heading = atof( row[4] ); - } - mysql_free_result(result); - return true; - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetWaypoints query '%s': %s", query, errbuff); - safe_delete_array(query); - } - return false; + + if (wp == nullptr) + return false; + + std::string query = StringFormat("SELECT x, y, z, pause, heading FROM grid_entries " + "WHERE gridid = %i AND number = %i AND zoneid = %i", grid, num, zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetWaypoints query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return false; + } + + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + + wp->x = atof(row[0]); + wp->y = atof(row[1]); + wp->z = atof(row[2]); + wp->pause = atoi(row[3]); + wp->heading = atof(row[4]); + + return true; } void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; int matches = 0, fuzzy = 0, spawn2id = 0; - uint32 affected_rows; float dbx = 0, dby = 0; // looks like most of the stuff in spawn2 is straight integers // so let's try that first - if(!RunQuery( - query, - MakeAnyLenString( - &query, - "SELECT id,x,y FROM spawn2 WHERE zone='%s' AND x=%i AND y=%i", - zone->GetShortName(), (int)x, (int)y - ), - errbuf, - &result - )) { - LogFile->write(EQEMuLog::Error, "Error querying spawn2 '%s': '%s'", query, errbuf); - return; + std::string query = StringFormat("SELECT id, x, y FROM spawn2 WHERE zone = '%s' AND x = %i AND y = %i", + zone->GetShortName(), (int)x, (int)y); + auto results = QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error querying spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + return; } - safe_delete_array(query); // how much it's allowed to be off by #define _GASSIGN_TOLERANCE 1.0 - if(!(matches = mysql_num_rows(result))) // try a fuzzy match if that didn't find it + if (results.RowCount() == 0) // try a fuzzy match if that didn't find it { - mysql_free_result(result); - if(!RunQuery( - query, - MakeAnyLenString( - &query, - "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 - ), - errbuf, - &result - )) { - LogFile->write(EQEMuLog::Error, "Error querying fuzzy spawn2 '%s': '%s'", query, errbuf); + 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()) { + LogFile->write(EQEMuLog::Error, "Error querying fuzzy spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); return; } - safe_delete_array(query); + fuzzy = 1; - if(!(matches = mysql_num_rows(result))) - mysql_free_result(result); + matches = results.RowCount(); } - if(matches) + + if (matches == 0) { - if(matches > 1) - { - client->Message(0, "ERROR: Unable to assign grid - multiple spawn2 rows match"); - mysql_free_result(result); - } - else - { - row = mysql_fetch_row(result); - spawn2id = atoi(row[0]); - dbx = atof(row[1]); - dby = atof(row[2]); - if(!RunQuery( - query, - MakeAnyLenString( - &query, - "UPDATE spawn2 SET pathgrid = %d WHERE id = %d", grid, spawn2id - ), - errbuf, - &result, - &affected_rows - )) { - LogFile->write(EQEMuLog::Error, "Error updating spawn2 '%s': '%s'", query, errbuf); - return; - } - if(affected_rows == 1) - { - if(client) client->LogSQL(query); - if(fuzzy) - { - float difference; - difference = sqrtf(pow(fabs(x-dbx),2) + pow(fabs(y-dby),2)); - client->Message(0, - "Grid assign: spawn2 id = %d updated - fuzzy match: deviation %f", - spawn2id, difference - ); - } - else - { - client->Message(0, "Grid assign: spawn2 id = %d updated - exact match", spawn2id); - } - } - else - { - client->Message(0, "ERROR: found spawn2 id %d but the update query failed", spawn2id); - } - } - } - else + client->Message(0, "ERROR: Unable to assign grid - can't find it in spawn2"); + return; + } + + if(matches > 1) { - client->Message(0, "ERROR: Unable to assign grid - can't find it in spawn2"); + client->Message(0, "ERROR: Unable to assign grid - multiple spawn2 rows match"); + return; } + + auto row = results.begin(); + + spawn2id = atoi(row[0]); + dbx = atof(row[1]); + dby = atof(row[2]); + + query = StringFormat("UPDATE spawn2 SET pathgrid = %d WHERE id = %d", grid, spawn2id); + results = QueryDatabase(query); + if (!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error updating spawn2 '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + if (results.RowsAffected() != 1) + { + client->Message(0, "ERROR: found spawn2 id %d but the update query failed", spawn2id); + return; + } + + if(client) + client->LogSQL(query.c_str()); + + if (!fuzzy) + { + client->Message(0, "Grid assign: spawn2 id = %d updated - exact match", spawn2id); + return; + } + + float difference = sqrtf(pow(fabs(x - dbx) , 2) + pow(fabs(y - dby), 2)); + client->Message(0, "Grid assign: spawn2 id = %d updated - fuzzy match: deviation %f", spawn2id, difference); } /****************** @@ -1211,53 +1148,57 @@ void ZoneDatabase::AssignGrid(Client *client, float x, float y, uint32 grid) * type,type2: The type and type2 values for the grid being created (ignored if grid is being deleted) * zoneid: The ID number of the zone the grid is being created/deleted in */ +void ZoneDatabase::ModifyGrid(Client *client, bool remove, uint32 id, uint8 type, uint8 type2, uint16 zoneid) { -void ZoneDatabase::ModifyGrid(Client *c, bool remove, uint32 id, uint8 type, uint8 type2, uint16 zoneid) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; if (!remove) { - if(!RunQuery(query, MakeAnyLenString(&query,"INSERT INTO grid(id,zoneid,type,type2) VALUES(%i,%i,%i,%i)",id,zoneid,type,type2), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error creating grid entry '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); - } - safe_delete_array(query); + std::string query = StringFormat("INSERT INTO grid(id, zoneid, type, type2) " + "VALUES (%i, %i, %i, %i)", id, zoneid, type, type2); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error creating grid entry '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + if(client) + client->LogSQL(query.c_str()); + + return; } - else - { - if(!RunQuery(query, MakeAnyLenString(&query,"DELETE FROM grid where id=%i",id), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error deleting grid '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); - } - safe_delete_array(query); - query = 0; - if(!RunQuery(query, MakeAnyLenString(&query,"DELETE FROM grid_entries WHERE zoneid=%i AND gridid=%i",zoneid,id), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error deleting grid entries '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); - } - safe_delete_array(query); - } -} /*** END ZoneDatabase::ModifyGrid() ***/ + + std::string query = StringFormat("DELETE FROM grid where id=%i", id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error deleting grid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + else if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("DELETE FROM grid_entries WHERE zoneid = %i AND gridid = %i", zoneid, id); + results = QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error deleting grid entries '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + else if(client) + client->LogSQL(query.c_str()); + +} /************************************** * AddWP - Adds a new waypoint to a specific grid for a specific zone. */ - -void ZoneDatabase::AddWP(Client *c, uint32 gridid, uint32 wpnum, float xpos, float ypos, float zpos, uint32 pause, uint16 zoneid, float heading) +void ZoneDatabase::AddWP(Client *client, uint32 gridid, uint32 wpnum, float xpos, float ypos, float zpos, uint32 pause, uint16 zoneid, float heading) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - - if(!RunQuery(query,MakeAnyLenString(&query,"INSERT INTO grid_entries (gridid,zoneid,`number`,x,y,z,pause,heading) values (%i,%i,%i,%f,%f,%f,%i,%f)",gridid,zoneid,wpnum,xpos,ypos,zpos,pause,heading), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error adding waypoint '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); + std::string query = StringFormat("INSERT INTO grid_entries (gridid, zoneid, `number`, x, y, z, pause, heading) " + "VALUES (%i, %i, %i, %f, %f, %f, %i, %f)", + gridid, zoneid, wpnum, xpos, ypos, zpos, pause, heading); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error adding waypoint '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + return; } - safe_delete_array(query); -} /*** END ZoneDatabase::AddWP() ***/ + + if(client) + client->LogSQL(query.c_str()); +} /********** @@ -1270,19 +1211,20 @@ void ZoneDatabase::AddWP(Client *c, uint32 gridid, uint32 wpnum, float xpos, flo * wp_num: The number of the waypoint being deleted * zoneid: The ID number of the zone that contains the waypoint being deleted */ - -void ZoneDatabase::DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num, uint16 zoneid) +void ZoneDatabase::DeleteWaypoint(Client *client, uint32 grid_num, uint32 wp_num, uint16 zoneid) { - char *query=0; - char errbuf[MYSQL_ERRMSG_SIZE]; - - if(!RunQuery(query, MakeAnyLenString(&query,"DELETE FROM grid_entries where gridid=%i and zoneid=%i and `number`=%i",grid_num,zoneid,wp_num), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error deleting waypoint '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); + std::string query = StringFormat("DELETE FROM grid_entries WHERE " + "gridid = %i AND zoneid = %i AND `number` = %i", + grid_num, zoneid, wp_num); + auto results = QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error deleting waypoint '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + return; } - safe_delete_array(query); -} /*** END ZoneDatabase::DeleteWaypoint() ***/ + + if(client) + client->LogSQL(query.c_str()); +} /****************** @@ -1292,139 +1234,113 @@ void ZoneDatabase::DeleteWaypoint(Client *c, uint32 grid_num, uint32 wp_num, uin * Returns 0 if the function didn't have to create a new grid. If the function had to create a new grid for the spawn, then the ID of * the created grid is returned. */ +uint32 ZoneDatabase::AddWPForSpawn(Client *client, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading) { -uint32 ZoneDatabase::AddWPForSpawn(Client *c, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading) { - char *query = 0; - uint32 grid_num, // The grid number the spawn is assigned to (if spawn has no grid, will be the grid number we end up creating) - next_wp_num; // The waypoint number we should be assigning to the new waypoint - bool CreatedNewGrid; // Did we create a new grid in this function? - MYSQL_RES *result; - MYSQL_ROW row; - char errbuf[MYSQL_ERRMSG_SIZE]; + uint32 grid_num; // The grid number the spawn is assigned to (if spawn has no grid, will be the grid number we end up creating) + uint32 next_wp_num; // The waypoint number we should be assigning to the new waypoint + bool createdNewGrid; // Did we create a new grid in this function? // See what grid number our spawn is assigned - if(RunQuery(query, MakeAnyLenString(&query,"SELECT pathgrid FROM spawn2 WHERE id=%i",spawn2id),errbuf,&result)) - { - safe_delete_array(query); - if(mysql_num_rows(result) > 0) - { - row = mysql_fetch_row(result); - grid_num = atoi(row[0]); - } - else // This spawn ID was not found in the `spawn2` table - return 0; - - mysql_free_result(result); - } - else { // Query error - LogFile->write(EQEMuLog::Error, "Error setting pathgrid '%s': '%s'", query, errbuf); + std::string query = StringFormat("SELECT pathgrid FROM spawn2 WHERE id = %i", spawn2id); + auto results = QueryDatabase(query); + if (!results.Success()) { + // Query error + LogFile->write(EQEMuLog::Error, "Error setting pathgrid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); return 0; } - if (grid_num == 0) // Our spawn doesn't have a grid assigned to it -- we need to create a new grid and assign it to the spawn - { - CreatedNewGrid = true; - if((grid_num = GetFreeGrid(zoneid)) == 0) // There are no grids for the current zone -- create Grid #1 - grid_num = 1; + if (results.RowCount() == 0) + return 0; - if(!RunQuery(query, MakeAnyLenString(&query,"insert into grid set id='%i',zoneid= %i, type='%i', type2='%i'",grid_num,zoneid,type1,type2), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error adding grid '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); - } - safe_delete_array(query); + auto row = results.begin(); + grid_num = atoi(row[0]); - query = 0; - if(!RunQuery(query, MakeAnyLenString(&query,"update spawn2 set pathgrid='%i' where id='%i'",grid_num,spawn2id), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error updating spawn2 pathing '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); - } - safe_delete_array(query); + if (grid_num == 0) + { // Our spawn doesn't have a grid assigned to it -- we need to create a new grid and assign it to the spawn + createdNewGrid = true; + grid_num = GetFreeGrid(zoneid); + if(grid_num == 0) // There are no grids for the current zone -- create Grid #1 + grid_num = 1; + + query = StringFormat("INSERT INTO grid SET id = '%i', zoneid = %i, type ='%i', type2 = '%i'", + grid_num, zoneid, type1, type2); + results = QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error adding grid '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + else if(client) + client->LogSQL(query.c_str()); + + query = StringFormat("UPDATE spawn2 SET pathgrid = '%i' WHERE id = '%i'", grid_num, spawn2id); + results = QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error updating spawn2 pathing '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + else if(client) + client->LogSQL(query.c_str()); } else // NPC had a grid assigned to it - CreatedNewGrid = false; - + createdNewGrid = false; // Find out what the next waypoint is for this grid - query = 0; - if(RunQuery(query, MakeAnyLenString(&query,"SELECT max(`number`) FROM grid_entries WHERE zoneid='%i' AND gridid='%i'",zoneid,grid_num),errbuf,&result)) - { - safe_delete_array(query); - row = mysql_fetch_row(result); - if(row[0] != 0) - next_wp_num = atoi(row[0]) + 1; - else // No waypoints in this grid yet - next_wp_num = 1; + query = StringFormat("SELECT max(`number`) FROM grid_entries WHERE zoneid = '%i' AND gridid = '%i'", zoneid, grid_num); - mysql_free_result(result); - } - else { // Query error - LogFile->write(EQEMuLog::Error, "Error getting next waypoint id '%s': '%s'", query, errbuf); + results = QueryDatabase(query); + if(!results.Success()) { // Query error + LogFile->write(EQEMuLog::Error, "Error getting next waypoint id '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); return 0; } - query = 0; - if(!RunQuery(query, MakeAnyLenString(&query,"INSERT INTO grid_entries(gridid,zoneid,`number`,x,y,z,pause,heading) VALUES (%i,%i,%i,%f,%f,%f,%i,%f)",grid_num,zoneid,next_wp_num,xpos,ypos,zpos,pause,heading), errbuf)) { - LogFile->write(EQEMuLog::Error, "Error adding grid entry '%s': '%s'", query, errbuf); - } else { - if(c) c->LogSQL(query); - } - safe_delete_array(query); + row = results.begin(); + if(row[0] != 0) + next_wp_num = atoi(row[0]) + 1; + else // No waypoints in this grid yet + next_wp_num = 1; - if(CreatedNewGrid) - return grid_num; - - return 0; -} /*** END ZoneDatabase::AddWPForSpawn() ***/ + query = StringFormat("INSERT INTO grid_entries(gridid, zoneid, `number`, x, y, z, pause, heading) " + "VALUES (%i, %i, %i, %f, %f, %f, %i, %f)", + grid_num, zoneid, next_wp_num, xpos, ypos, zpos, pause, heading); + results = QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error adding grid entry '%s': '%s'", query.c_str(), results.ErrorMessage().c_str()); + else if(client) + client->LogSQL(query.c_str()); + return createdNewGrid? grid_num: 0; +} uint32 ZoneDatabase::GetFreeGrid(uint16 zoneid) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query,"SELECT max(id) from grid where zoneid = %i",zoneid),errbuf,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 tmp=0; - if (row[0]) - tmp = atoi(row[0]); - mysql_free_result(result); - tmp++; - return tmp; - } - mysql_free_result(result); + + std::string query = StringFormat("SELECT max(id) FROM grid WHERE zoneid = %i", zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetFreeGrid query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; } - else { - LogFile->write(EQEMuLog::Error, "Error in GetFreeGrid query '%s': %s", query, errbuf); - safe_delete_array(query); - } - return 0; + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + uint32 freeGridID = 1; + freeGridID = atoi(row[0]) + 1; + + return freeGridID; } int ZoneDatabase::GetHighestWaypoint(uint32 zoneid, uint32 gridid) { - char *query = 0; - char errbuff[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - int res = 0; - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT COALESCE(MAX(number), 0) FROM grid_entries WHERE zoneid = %i AND gridid = %i", - zoneid, gridid),errbuff,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - res = atoi( row[0] ); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetHighestWaypoint query '%s': %s", query, errbuff); - safe_delete_array(query); + + std::string query = StringFormat("SELECT COALESCE(MAX(number), 0) FROM grid_entries " + "WHERE zoneid = %i AND gridid = %i", zoneid, gridid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetHighestWaypoint query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; } - return(res); + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } void NPC::SaveGuardSpotCharm() diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 5bc6cb706..d0c6db522 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -33,26 +33,26 @@ #endif #include "../common/servertalk.h" -#include "worldserver.h" #include "../common/eq_packet_structs.h" #include "../common/packet_dump.h" -#include "../common/MiscFunctions.h" +#include "../common/misc_functions.h" +#include "../common/packet_functions.h" +#include "../common/md5.h" +#include "../common/rulesys.h" +#include "worldserver.h" #include "zonedb.h" #include "zone.h" #include "entity.h" #include "masterentity.h" #include "net.h" #include "petitions.h" -#include "../common/packet_functions.h" -#include "../common/md5.h" -#include "ZoneConfig.h" -#include "StringIDs.h" +#include "zone_config.h" +#include "string_ids.h" #include "guild_mgr.h" -#include "../common/rulesys.h" #include "titles.h" -#include "QGlobals.h" -#include "remote_call.h" +#include "qglobals.h" #include "remote_call_subscribe.h" +#include "remote_call.h" extern EntityList entity_list; extern Zone* zone; @@ -170,18 +170,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; @@ -324,7 +330,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); } @@ -700,8 +706,8 @@ void WorldServer::Process() { _log(SPELLS__REZ, "Found corpse. Marking corpse as rezzed."); // I don't know why Rezzed is not set to true in CompleteRezz(). - corpse->Rezzed(true); - corpse->CompleteRezz(); + corpse->IsRezzed(true); + corpse->CompleteResurrection(); } } @@ -827,12 +833,11 @@ void WorldServer::Process() { } case ServerOP_GroupInvite: { // A player in another zone invited a player in this zone to join their group. - // GroupInvite_Struct* gis = (GroupInvite_Struct*)pack->pBuffer; Mob *Invitee = entity_list.GetMob(gis->invitee_name); - if(Invitee && Invitee->IsClient() && !Invitee->IsGrouped() && !Invitee->IsRaidGrouped()) + if(Invitee && Invitee->IsClient() && Invitee->CastToClient()->MercOnlyOrNoGroup() && !Invitee->IsRaidGrouped()) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupInvite, sizeof(GroupInvite_Struct)); memcpy(outapp->pBuffer, gis, sizeof(GroupInvite_Struct)); @@ -844,7 +849,6 @@ void WorldServer::Process() { } case ServerOP_GroupFollow: { // Player in another zone accepted a group invitation from a player in this zone. - // ServerGroupFollow_Struct* sgfs = (ServerGroupFollow_Struct*) pack->pBuffer; Mob* Inviter = entity_list.GetClientByName(sgfs->gf.name1); @@ -853,12 +857,15 @@ void WorldServer::Process() { { Group* group = entity_list.GetGroupByClient(Inviter->CastToClient()); - if(!group){ - + if(!group) + { + //Make new group group = new Group(Inviter); - if(!group) + if (!group) + { break; + } entity_list.AddGroup(group); @@ -867,10 +874,8 @@ void WorldServer::Process() { break; } - database.SetGroupID(Inviter->GetName(), group->GetID(), Inviter->CastToClient()->CharacterID()); - + database.SetGroupID(Inviter->GetName(), group->GetID(), Inviter->CastToClient()->CharacterID(), false); database.SetGroupLeaderName(group->GetID(), Inviter->GetName()); - group->UpdateGroupAAs(); if(Inviter->CastToClient()->GetClientVersion() < EQClientSoD) @@ -887,25 +892,22 @@ void WorldServer::Process() { else { // SoD and later - // Inviter->CastToClient()->SendGroupCreatePacket(); Inviter->CastToClient()->SendGroupLeaderChangePacket(Inviter->GetName()); Inviter->CastToClient()->SendGroupJoinAcknowledge(); } } + if(!group) + { break; + } EQApplicationPacket* outapp=new EQApplicationPacket(OP_GroupFollow, sizeof(GroupGeneric_Struct)); - GroupGeneric_Struct *gg = (GroupGeneric_Struct *)outapp->pBuffer; - strn0cpy(gg->name1, sgfs->gf.name1, sizeof(gg->name1)); - strn0cpy(gg->name2, sgfs->gf.name2, sizeof(gg->name2)); - Inviter->CastToClient()->QueuePacket(outapp); - safe_delete(outapp); if(!group->AddMember(nullptr, sgfs->gf.name2, sgfs->CharacterID)) @@ -915,46 +917,35 @@ void WorldServer::Process() { Inviter->CastToClient()->UpdateLFP(); ServerPacket* pack2 = new ServerPacket(ServerOP_GroupJoin, sizeof(ServerGroupJoin_Struct)); - ServerGroupJoin_Struct* gj = (ServerGroupJoin_Struct*)pack2->pBuffer; - gj->gid = group->GetID(); - gj->zoneid = zone->GetZoneID(); - gj->instance_id = zone->GetInstanceID(); - strn0cpy(gj->member_name, sgfs->gf.name2, sizeof(gj->member_name)); - worldserver.SendPacket(pack2); - safe_delete(pack2); + + // Send acknowledgement back to the Invitee to let them know we have added them to the group. - // ServerPacket* pack3 = new ServerPacket(ServerOP_GroupFollowAck, sizeof(ServerGroupFollowAck_Struct)); - ServerGroupFollowAck_Struct* sgfas = (ServerGroupFollowAck_Struct*)pack3->pBuffer; - strn0cpy(sgfas->Name, sgfs->gf.name2, sizeof(sgfas->Name)); - worldserver.SendPacket(pack3); - safe_delete(pack3); } break; } case ServerOP_GroupFollowAck: { // The Inviter (in another zone) has successfully added the Invitee (in this zone) to the group. - // ServerGroupFollowAck_Struct* sgfas = (ServerGroupFollowAck_Struct*)pack->pBuffer; - Client *c = entity_list.GetClientByName(sgfas->Name); + Client *client = entity_list.GetClientByName(sgfas->Name); - if(!c) + if(!client) break; - uint32 groupid = database.GetGroupID(c->GetName()); + uint32 groupid = database.GetGroupID(client->GetName()); Group* group = nullptr; @@ -970,26 +961,30 @@ void WorldServer::Process() { entity_list.AddGroup(group, groupid); else group = nullptr; - } //else, somebody from our group is already here... + } if(group) - group->UpdatePlayer(c); + group->UpdatePlayer(client); else { - if(c->GetMerc()) - database.SetGroupID(c->GetMerc()->GetCleanName(), 0, c->CharacterID(), true); - database.SetGroupID(c->GetName(), 0, c->CharacterID()); //cannot re-establish group, kill it + if(client->GetMerc()) + database.SetGroupID(client->GetMerc()->GetCleanName(), 0, client->CharacterID(), true); + database.SetGroupID(client->GetName(), 0, client->CharacterID(), false); //cannot re-establish group, kill it } } if(group) { - database.RefreshGroupFromDB(c); + if (client->GetMerc()) + { + client->GetMerc()->MercJoinClientGroup(); + } + database.RefreshGroupFromDB(client); - group->SendHPPacketsTo(c); + group->SendHPPacketsTo(client); - // If the group leader is not set, pull the group leader infomrmation from the database. + // If the group leader is not set, pull the group leader information from the database. if(!group->GetLeader()) { char ln[64]; @@ -997,9 +992,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); @@ -1009,9 +1006,14 @@ void WorldServer::Process() { group->SetPuller(PullerName); group->SetNPCMarker(NPCMarkerName); group->SetGroupAAs(&GLAA); + group->SetGroupMentor(mentor_percent, mentoree_name); } } + else if (client->GetMerc()) + { + client->GetMerc()->MercJoinClientGroup(); + } break; } @@ -1368,9 +1370,21 @@ 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); + Corpse* NewCorpse = database.LoadCharacterCorpse(s->player_corpse_id); if(NewCorpse) NewCorpse->Spawn(); else @@ -1779,6 +1793,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; @@ -1902,6 +1934,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(); @@ -2226,3 +2259,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..c1e86421f 100644 --- a/zone/worldserver.h +++ b/zone/worldserver.h @@ -20,12 +20,10 @@ #include "../common/worldconn.h" #include "../common/eq_packet_structs.h" -#include -struct GuildJoin_Struct; +class ServerPacket; class EQApplicationPacket; class Client; -class Database; class WorldServer : public WorldConnection { public: @@ -57,6 +55,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 e1e3f68b2..2d5f6e8e6 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -15,17 +15,14 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "../common/debug.h" + +#include #include -#include +#include #include #include -#include -#include -#include #ifdef _WINDOWS -#include #define snprintf _snprintf #define vsnprintf _vsnprintf #else @@ -33,32 +30,26 @@ #include "../common/unix.h" #endif -#include "masterentity.h" +#include "../common/debug.h" #include "../common/features.h" -#include "spawngroup.h" -#include "spawn2.h" -#include "zone.h" -#include "worldserver.h" -#include "npc.h" -#include "net.h" -#include "../common/seperator.h" -#include "../common/packet_dump_file.h" -#include "../common/EQStreamFactory.h" -#include "../common/EQStream.h" -#include "../common/StringUtil.h" -#include "ZoneConfig.h" -#include "../common/breakdowns.h" -#include "map.h" -#include "water_map.h" -#include "object.h" -#include "petitions.h" -#include "pathing.h" -#include "event_codes.h" -#include "client_logs.h" #include "../common/rulesys.h" +#include "../common/seperator.h" +#include "../common/string_util.h" +#include "client_logs.h" #include "guild_mgr.h" -#include "QuestParserCollection.h" -#include "remote_call.h" +#include "map.h" +#include "net.h" +#include "npc.h" +#include "object.h" +#include "pathing.h" +#include "petitions.h" +#include "quest_parser_collection.h" +#include "spawn2.h" +#include "spawngroup.h" +#include "water_map.h" +#include "worldserver.h" +#include "zone_config.h" +#include "zone.h" #include "remote_call_subscribe.h" #ifdef _WINDOWS @@ -67,20 +58,19 @@ #define strcasecmp _stricmp #endif - +extern bool staticzone; +extern NetConnection net; +extern PetitionList petition_list; +extern QuestParserCollection* parse; +extern uint16 adverrornum; +extern uint32 numclients; extern WorldServer worldserver; extern Zone* zone; -extern uint32 numclients; -extern NetConnection net; -extern uint16 adverrornum; -extern PetitionList petition_list; + Mutex MZoneShutdown; -extern bool staticzone; -Zone* zone = 0; + volatile bool ZoneLoaded = false; -extern QuestParserCollection* parse; -extern DBAsyncFinishedQueue MTdbafq; -extern DBAsync *dbasync; +Zone* zone = 0; bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { const char* zonename = database.GetZoneName(iZoneID); @@ -107,7 +97,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)) { @@ -158,140 +148,127 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) { //this really loads the objects into entity_list bool Zone::LoadZoneObjects() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 len_query = MakeAnyLenString(&query, "SELECT " - "id,zoneid,xpos,ypos,zpos,heading,itemid,charges,objectname,type,icon," - "unknown08,unknown10,unknown20,unknown24,unknown76" - " from object where zoneid=%i and (version=%u or version=-1)", zoneid, instanceversion); + std::string query = StringFormat("SELECT id, zoneid, xpos, ypos, zpos, heading, " + "itemid, charges, objectname, type, icon, unknown08, " + "unknown10, unknown20, unknown24, unknown76 fROM object " + "WHERE zoneid = %i AND (version = %u OR version = -1)", + zoneid, instanceversion); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Loading Objects from DB: %s",results.ErrorMessage().c_str()); + return false; + } - if (database.RunQuery(query, len_query, errbuf, &result)) { - safe_delete_array(query); - LogFile->write(EQEMuLog::Status, "Loading Objects from DB..."); - while ((row = mysql_fetch_row(result))) { - if (atoi(row[9]) == 0) - { - // Type == 0 - Static Object - const char* shortname = database.GetZoneName(atoi(row[1]), false); // zoneid -> zone_shortname + LogFile->write(EQEMuLog::Status, "Loading Objects from DB..."); + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[9]) == 0) + { + // Type == 0 - Static Object + const char* shortname = database.GetZoneName(atoi(row[1]), false); // zoneid -> zone_shortname - if (shortname) - { - Door d; - memset(&d, 0, sizeof(d)); + if (!shortname) + continue; - strn0cpy(d.zone_name, shortname, sizeof(d.zone_name)); - d.db_id = 1000000000 + atoi(row[0]); // Out of range of normal use for doors.id - d.door_id = -1; // Client doesn't care if these are all the same door_id - d.pos_x = atof(row[2]); // xpos - d.pos_y = atof(row[3]); // ypos - d.pos_z = atof(row[4]); // zpos - d.heading = atof(row[5]); // heading + Door d; + memset(&d, 0, sizeof(d)); - strn0cpy(d.door_name, row[8], sizeof(d.door_name)); // objectname - // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. - int len = strlen(d.door_name); - if ((len > 9) && (memcmp(&d.door_name[len - 9], "_ACTORDEF", 10) == 0)) - { - d.door_name[len - 9] = '\0'; - } + strn0cpy(d.zone_name, shortname, sizeof(d.zone_name)); + d.db_id = 1000000000 + atoi(row[0]); // Out of range of normal use for doors.id + d.door_id = -1; // Client doesn't care if these are all the same door_id + d.pos_x = atof(row[2]); // xpos + d.pos_y = atof(row[3]); // ypos + d.pos_z = atof(row[4]); // zpos + d.heading = atof(row[5]); // heading - memcpy(d.dest_zone, "NONE", 5); + strn0cpy(d.door_name, row[8], sizeof(d.door_name)); // objectname + // Strip trailing "_ACTORDEF" if present. Client won't accept it for doors. + int len = strlen(d.door_name); + if ((len > 9) && (memcmp(&d.door_name[len - 9], "_ACTORDEF", 10) == 0)) + d.door_name[len - 9] = '\0'; - if ((d.size = atoi(row[11])) == 0) // unknown08 = optional size percentage - { - d.size = 100; - } + memcpy(d.dest_zone, "NONE", 5); - switch (d.opentype = atoi(row[12])) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) - { - case 0: - d.opentype = 31; - break; - case 1: - d.opentype = 9; - break; - } + if ((d.size = atoi(row[11])) == 0) // unknown08 = optional size percentage + d.size = 100; - d.incline = atoi(row[13]); // unknown20 = optional model incline value - d.client_version_mask = 0xFFFFFFFF; //We should load the mask from the zone. + switch (d.opentype = atoi(row[12])) // unknown10 = optional request_nonsolid (0 or 1 or experimental number) + { + case 0: + d.opentype = 31; + break; + case 1: + d.opentype = 9; + break; + } - Doors* door = new Doors(&d); - entity_list.AddDoor(door); - } + d.incline = atoi(row[13]); // unknown20 = optional model incline value + d.client_version_mask = 0xFFFFFFFF; //We should load the mask from the zone. - continue; - } - Object_Struct data = {0}; - uint32 id = 0; - uint32 icon = 0; - uint32 type = 0; - uint32 itemid = 0; - uint32 idx = 0; - int16 charges = 0; + Doors* door = new Doors(&d); + entity_list.AddDoor(door); + } - id = (uint32)atoi(row[0]); - data.zone_id = atoi(row[1]); - data.x = atof(row[2]); - data.y = atof(row[3]); - data.z = atof(row[4]); - data.heading = atof(row[5]); - itemid = (uint32)atoi(row[6]); - charges = (int16)atoi(row[7]); - strcpy(data.object_name, row[8]); - type = (uint8)atoi(row[9]); - icon = (uint32)atoi(row[10]); - data.object_type = type; - data.linked_list_addr[0] = 0; - data.linked_list_addr[1] = 0; - data.unknown008 = (uint32)atoi(row[11]); - data.unknown010 = (uint32)atoi(row[12]); - data.unknown020 = (uint32)atoi(row[13]); - data.unknown024 = (uint32)atoi(row[14]); - data.unknown076 = (uint32)atoi(row[15]); - data.unknown084 = 0; + Object_Struct data = {0}; + uint32 id = 0; + uint32 icon = 0; + uint32 type = 0; + uint32 itemid = 0; + uint32 idx = 0; + int16 charges = 0; - ItemInst* inst = nullptr; - //FatherNitwit: this dosent seem to work... - //tradeskill containers do not have an itemid of 0... at least what I am seeing - if (itemid == 0) { - // Generic tradeskill container - inst = new ItemInst(ItemInstWorldContainer); - } - else { - // Groundspawn object - inst = database.CreateItem(itemid); - } + id = (uint32)atoi(row[0]); + data.zone_id = atoi(row[1]); + data.x = atof(row[2]); + data.y = atof(row[3]); + data.z = atof(row[4]); + data.heading = atof(row[5]); + itemid = (uint32)atoi(row[6]); + charges = (int16)atoi(row[7]); + strcpy(data.object_name, row[8]); + type = (uint8)atoi(row[9]); + icon = (uint32)atoi(row[10]); + data.object_type = type; + data.linked_list_addr[0] = 0; + data.linked_list_addr[1] = 0; + data.unknown008 = (uint32)atoi(row[11]); + data.unknown010 = (uint32)atoi(row[12]); + data.unknown020 = (uint32)atoi(row[13]); + data.unknown024 = (uint32)atoi(row[14]); + data.unknown076 = (uint32)atoi(row[15]); + data.unknown084 = 0; - //Father Nitwit's fix... not perfect... - if(inst == nullptr && type != OT_DROPPEDITEM) { - inst = new ItemInst(ItemInstWorldContainer); - } + ItemInst* inst = nullptr; + //FatherNitwit: this dosent seem to work... + //tradeskill containers do not have an itemid of 0... at least what I am seeing + if (itemid == 0) { + // Generic tradeskill container + inst = new ItemInst(ItemInstWorldContainer); + } + else { + // Groundspawn object + inst = database.CreateItem(itemid); + } - // Load child objects if container - if (inst && inst->IsType(ItemClassContainer)) { - database.LoadWorldContainer(id, inst); - } + //Father Nitwit's fix... not perfect... + if(inst == nullptr && type != OT_DROPPEDITEM) { + inst = new ItemInst(ItemInstWorldContainer); + } - Object* object = new Object(id, type, icon, data, inst); - entity_list.AddObject(object, false); - if(type == OT_DROPPEDITEM && itemid != 0) - { - entity_list.RemoveObject(object->GetID()); - } + // Load child objects if container + if (inst && inst->IsType(ItemClassContainer)) { + database.LoadWorldContainer(id, inst); + } - safe_delete(inst); - } - mysql_free_result(result); - } - else { - safe_delete_array(query); - LogFile->write(EQEMuLog::Error, "Error Loading Objects from DB: %s",errbuf); - return(false); - } - return(true); + Object* object = new Object(id, type, icon, data, inst); + entity_list.AddObject(object, false); + if(type == OT_DROPPEDITEM && itemid != 0) + entity_list.RemoveObject(object->GetID()); + + safe_delete(inst); + } + + return true; } //this also just loads into entity_list, not really into zone @@ -324,41 +301,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) { @@ -366,27 +342,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]; @@ -408,48 +384,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 " + "DISTINCT 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); @@ -457,50 +427,74 @@ 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){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; +void Zone::LoadNewMerchantData(uint32 merchantid) { + std::list merlist; - if (database.RunQuery(query, MakeAnyLenString(&query, "SELECT item, slot, faction_required, level_required, alt_currency_cost, classes_required FROM merchantlist WHERE merchantid=%d", merchantid), errbuf, &result)) { - while((row = mysql_fetch_row(result))) { - MerchantList ml; - ml.id = merchantid; - ml.item = atoul(row[0]); - ml.slot = atoul(row[1]); - ml.faction_required = atoul(row[2]); - ml.level_required = atoul(row[3]); - ml.alt_currency_cost = atoul(row[3]); - ml.classes_required = atoul(row[4]); - merlist.push_back(ml); - } - merchanttable[merchantid] = merlist; - mysql_free_result(result); - } - else - LogFile->write(EQEMuLog::Error, "Error in LoadNewMerchantData query '%s' %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT item, slot, faction_required, level_required, alt_currency_cost, " + "classes_required, probability 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()); + return; + } + + for(auto row = results.begin(); row != results.end(); ++row) { + MerchantList ml; + ml.id = merchantid; + ml.item = atoul(row[0]); + ml.slot = atoul(row[1]); + ml.faction_required = atoul(row[2]); + ml.level_required = atoul(row[3]); + ml.alt_currency_cost = atoul(row[4]); + ml.classes_required = atoul(row[5]); + ml.probability = atoul(row[6]); + merlist.push_back(ml); + } + + 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 " + "DISTINCT 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::Debug, "No Merchant Data found for %s.", GetShortName()); + 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); @@ -510,15 +504,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; } @@ -528,232 +522,139 @@ void Zone::LoadMerchantData_result(MYSQL_RES* result) { ml.level_required = atoul(row[4]); ml.alt_currency_cost = atoul(row[5]); ml.classes_required = atoul(row[6]); + 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 " - "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(){ - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; std::list merc_stances; merc_templates.clear(); - - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT `class_id`, `proficiency_id`, `stance_id`, `isdefault` FROM `merc_stance_entries` order by `class_id`, `proficiency_id`, `stance_id`"), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } + std::string query = "SELECT `class_id`, `proficiency_id`, `stance_id`, `isdefault` FROM " + "`merc_stance_entries` ORDER BY `class_id`, `proficiency_id`, `stance_id`"; + auto results = database.QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadMercTemplates()"); else { - while(DataRow = mysql_fetch_row(DatasetResult)) { + for (auto row = results.begin(); row != results.end(); ++row) { MercStanceInfo tempMercStanceInfo; - tempMercStanceInfo.ClassID = atoi(DataRow[0]); - tempMercStanceInfo.ProficiencyID = atoi(DataRow[1]); - tempMercStanceInfo.StanceID = atoi(DataRow[2]); - tempMercStanceInfo.IsDefault = atoi(DataRow[3]); + tempMercStanceInfo.ClassID = atoi(row[0]); + tempMercStanceInfo.ProficiencyID = atoi(row[1]); + tempMercStanceInfo.StanceID = atoi(row[2]); + tempMercStanceInfo.IsDefault = atoi(row[3]); merc_stances.push_back(tempMercStanceInfo); } - - mysql_free_result(DatasetResult); } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring AS merc_type_id, MTem.dbstring AS merc_subtype_id, MTyp.race_id, MS.class_id, MTyp.proficiency_id, MS.tier_id, 0 AS CostFormula, MTem.clientversion, MTem.merc_npc_type_id FROM merc_types MTyp, merc_templates MTem, merc_subtypes MS WHERE MTem.merc_type_id = MTyp.merc_type_id AND MTem.merc_subtype_id = MS.merc_subtype_id ORDER BY MTyp.race_id, MS.class_id, MTyp.proficiency_id;"), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - int stanceIndex = 0; - MercTemplate tempMercTemplate; - - tempMercTemplate.MercTemplateID = atoi(DataRow[0]); - tempMercTemplate.MercType = atoi(DataRow[1]); - tempMercTemplate.MercSubType = atoi(DataRow[2]); - tempMercTemplate.RaceID = atoi(DataRow[3]); - tempMercTemplate.ClassID = atoi(DataRow[4]); - tempMercTemplate.ProficiencyID = atoi(DataRow[5]); - tempMercTemplate.TierID = atoi(DataRow[6]); - tempMercTemplate.CostFormula = atoi(DataRow[7]); - tempMercTemplate.ClientVersion = atoi(DataRow[8]); - tempMercTemplate.MercNPCID = atoi(DataRow[9]); - - for(int i = 0; i < MaxMercStanceID; i++) { - tempMercTemplate.Stances[i] = 0; - } - - for (std::list::iterator mercStanceListItr = merc_stances.begin(); mercStanceListItr != merc_stances.end(); ++mercStanceListItr) { - if(mercStanceListItr->ClassID == tempMercTemplate.ClassID && mercStanceListItr->ProficiencyID == tempMercTemplate.ProficiencyID) { - zone->merc_stance_list[tempMercTemplate.MercTemplateID].push_back((*mercStanceListItr)); - tempMercTemplate.Stances[stanceIndex] = mercStanceListItr->StanceID; - stanceIndex++; - } - } - - merc_templates[tempMercTemplate.MercTemplateID] = tempMercTemplate; - } - - mysql_free_result(DatasetResult); + query = "SELECT DISTINCT MTem.merc_template_id, MTyp.dbstring " + "AS merc_type_id, MTem.dbstring " + "AS merc_subtype_id, MTyp.race_id, MS.class_id, MTyp.proficiency_id, MS.tier_id, 0 " + "AS CostFormula, MTem.clientversion, MTem.merc_npc_type_id " + "FROM merc_types MTyp, merc_templates MTem, merc_subtypes MS " + "WHERE MTem.merc_type_id = MTyp.merc_type_id AND MTem.merc_subtype_id = MS.merc_subtype_id " + "ORDER BY MTyp.race_id, MS.class_id, MTyp.proficiency_id;"; + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadMercTemplates()"); + return; } - safe_delete_array(Query); - Query = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + + MercTemplate tempMercTemplate; + + tempMercTemplate.MercTemplateID = atoi(row[0]); + tempMercTemplate.MercType = atoi(row[1]); + tempMercTemplate.MercSubType = atoi(row[2]); + tempMercTemplate.RaceID = atoi(row[3]); + tempMercTemplate.ClassID = atoi(row[4]); + tempMercTemplate.ProficiencyID = atoi(row[5]); + tempMercTemplate.TierID = atoi(row[6]); + tempMercTemplate.CostFormula = atoi(row[7]); + tempMercTemplate.ClientVersion = atoi(row[8]); + tempMercTemplate.MercNPCID = atoi(row[9]); + + for(int i = 0; i < MaxMercStanceID; i++) + tempMercTemplate.Stances[i] = 0; + + int stanceIndex = 0; + for (auto mercStanceListItr = merc_stances.begin(); mercStanceListItr != merc_stances.end(); ++mercStanceListItr) { + if(mercStanceListItr->ClassID != tempMercTemplate.ClassID || mercStanceListItr->ProficiencyID != tempMercTemplate.ProficiencyID) + continue; + + zone->merc_stance_list[tempMercTemplate.MercTemplateID].push_back((*mercStanceListItr)); + tempMercTemplate.Stances[stanceIndex] = mercStanceListItr->StanceID; + ++stanceIndex; + } + + merc_templates[tempMercTemplate.MercTemplateID] = tempMercTemplate; + + } - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadMercTemplates()"); - } } - void Zone::LoadLevelEXPMods(){ - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + level_exp_mod.clear(); + const std::string query = "SELECT level, exp_mod, aa_exp_mod FROM level_exp_mods"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadEXPLevelMods()"); + return; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT level, exp_mod, aa_exp_mod FROM level_exp_mods"), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - uint32 index = atoi(DataRow[0]); - float exp_mod = atof(DataRow[1]); - float aa_exp_mod = atof(DataRow[2]); - level_exp_mod[index].ExpMod = exp_mod; - level_exp_mod[index].AAExpMod = aa_exp_mod; - } - mysql_free_result(DatasetResult); - } + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 index = atoi(row[0]); + float exp_mod = atof(row[1]); + float aa_exp_mod = atof(row[2]); + level_exp_mod[index].ExpMod = exp_mod; + level_exp_mod[index].AAExpMod = aa_exp_mod; + } - safe_delete_array(Query); - Query = 0; - - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadEXPLevelMods()"); - } } + void Zone::LoadMercSpells(){ - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; merc_spells_list.clear(); + const std::string query = "SELECT msl.class_id, msl.proficiency_id, msle.spell_id, msle.spell_type, " + "msle.stance_id, msle.minlevel, msle.maxlevel, msle.slot, msle.procChance " + "FROM merc_spell_lists msl, merc_spell_list_entries msle " + "WHERE msle.merc_spell_list_id = msl.merc_spell_list_id " + "ORDER BY msl.class_id, msl.proficiency_id, msle.spell_type, msle.minlevel, msle.slot;"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadMercSpells()"); + return; + } - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT msl.class_id, msl.proficiency_id, msle.spell_id, msle.spell_type, msle.stance_id, msle.minlevel, msle.maxlevel, msle.slot, msle.procChance FROM merc_spell_lists msl, merc_spell_list_entries msle WHERE msle.merc_spell_list_id = msl.merc_spell_list_id ORDER BY msl.class_id, msl.proficiency_id, msle.spell_type, msle.minlevel, msle.slot;"), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - uint32 classid; - MercSpellEntry tempMercSpellEntry; + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 classid; + MercSpellEntry tempMercSpellEntry; - classid = atoi(DataRow[0]); - tempMercSpellEntry.proficiencyid = atoi(DataRow[1]); - tempMercSpellEntry.spellid = atoi(DataRow[2]); - tempMercSpellEntry.type = atoi(DataRow[3]); - tempMercSpellEntry.stance = atoi(DataRow[4]); - tempMercSpellEntry.minlevel = atoi(DataRow[5]); - tempMercSpellEntry.maxlevel = atoi(DataRow[6]); - tempMercSpellEntry.slot = atoi(DataRow[7]); - tempMercSpellEntry.proc_chance = atoi(DataRow[8]); + classid = atoi(row[0]); + tempMercSpellEntry.proficiencyid = atoi(row[1]); + tempMercSpellEntry.spellid = atoi(row[2]); + tempMercSpellEntry.type = atoi(row[3]); + tempMercSpellEntry.stance = atoi(row[4]); + tempMercSpellEntry.minlevel = atoi(row[5]); + tempMercSpellEntry.maxlevel = atoi(row[6]); + tempMercSpellEntry.slot = atoi(row[7]); + tempMercSpellEntry.proc_chance = atoi(row[8]); - merc_spells_list[classid].push_back(tempMercSpellEntry); - } + merc_spells_list[classid].push_back(tempMercSpellEntry); + } - mysql_free_result(DatasetResult); + if(MERC_DEBUG > 0) + LogFile->write(EQEMuLog::Debug, "Mercenary Debug: Loaded %i merc spells.", merc_spells_list[1].size() + merc_spells_list[2].size() + merc_spells_list[9].size() + merc_spells_list[12].size()); - if(MERC_DEBUG > 0) - LogFile->write(EQEMuLog::Debug, "Mercenary Debug: Loaded %i merc spells.", merc_spells_list[1].size() + merc_spells_list[2].size() + merc_spells_list[9].size() + merc_spells_list[12].size()); - } - - safe_delete_array(Query); - Query = 0; - - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error in 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; } void Zone::Shutdown(bool quite) @@ -761,6 +662,16 @@ void Zone::Shutdown(bool quite) if (!ZoneLoaded) return; + std::list mob_list; + entity_list.GetMobList(mob_list); + std::list::iterator mob_itr = mob_list.begin(); + while (mob_itr != mob_list.end()) { + Mob* mob_inst = *mob_itr; + mob_inst->AI_Stop(); + mob_inst->AI_ShutDown(); + ++mob_itr; + } + std::map::iterator itr; while(zone->npctable.size()) { itr=zone->npctable.begin(); @@ -795,7 +706,6 @@ void Zone::Shutdown(bool quite) RemoteCallSubscriptionHandler::Instance()->ClearAllConnections(); zone->ResetAuth(); safe_delete(zone); - dbasync->CommitWrites(); entity_list.ClearAreas(); parse->ReloadQuests(true); UpdateWindowTitle(); @@ -840,6 +750,7 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) zoneid = in_zoneid; instanceid = in_instanceid; instanceversion = database.GetInstanceVersion(instanceid); + pers_instance = false; zonemap = nullptr; watermap = nullptr; pathing = nullptr; @@ -909,11 +820,12 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name) if(!is_perma) { if(rem < 150) //give some leeway to people who are zoning in 2.5 minutes to finish zoning in and get ported out - rem = 150; + rem = 150; Instance_Timer = new Timer(rem * 1000); } else { + pers_instance = true; Instance_Timer = nullptr; } } @@ -934,8 +846,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); @@ -1002,7 +912,7 @@ bool Zone::Init(bool iStaticZone) { } LogFile->write(EQEMuLog::Status, "Loading player corpses..."); - if (!database.LoadPlayerCorpses(zoneid, instanceid)) { + if (!database.LoadCharacterCorpses(zoneid, instanceid)) { LogFile->write(EQEMuLog::Error, "Loading player corpses failed."); return false; } @@ -1384,7 +1294,7 @@ bool Zone::Process() { return true; } -void Zone::ChangeWeather() +void Zone::ChangeWeather() { if(!HasWeather()) { @@ -1717,45 +1627,41 @@ ZonePoint* Zone::GetClosestZonePointWithoutZone(float x, float y, float z, Clien bool ZoneDatabase::LoadStaticZonePoints(LinkedList* zone_point_list, const char* zonename, uint32 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + zone_point_list->Clear(); zone->numzonepoints = 0; - MakeAnyLenString(&query, "SELECT x, y, z, target_x, target_y, " - "target_z, target_zone_id, heading, target_heading, number, " - "target_instance, client_version_mask FROM zone_points " - "WHERE zone='%s' AND (version=%i OR version=-1) order by number", zonename, version); - if (RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - ZonePoint* zp = new ZonePoint; - zp->x = atof(row[0]); - zp->y = atof(row[1]); - zp->z = atof(row[2]); - zp->target_x = atof(row[3]); - zp->target_y = atof(row[4]); - zp->target_z = atof(row[5]); - zp->target_zone_id = atoi(row[6]); - zp->heading = atof(row[7]); - zp->target_heading = atof(row[8]); - zp->number = atoi(row[9]); - zp->target_zone_instance = atoi(row[10]); - zp->client_version_mask = (uint32)strtoul(row[11], nullptr, 0); - zone_point_list->Insert(zp); - zone->numzonepoints++; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error1 in LoadStaticZonePoints query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT x, y, z, target_x, target_y, " + "target_z, target_zone_id, heading, target_heading, " + "number, target_instance, client_version_mask " + "FROM zone_points WHERE zone='%s' AND (version=%i OR version=-1) " + "ORDER BY number", zonename, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error1 in LoadStaticZonePoints query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } + + for (auto row = results.begin(); row != results.end(); ++row) { + ZonePoint* zp = new ZonePoint; + + zp->x = atof(row[0]); + zp->y = atof(row[1]); + zp->z = atof(row[2]); + zp->target_x = atof(row[3]); + zp->target_y = atof(row[4]); + zp->target_z = atof(row[5]); + zp->target_zone_id = atoi(row[6]); + zp->heading = atof(row[7]); + zp->target_heading = atof(row[8]); + zp->number = atoi(row[9]); + zp->target_zone_instance = atoi(row[10]); + zp->client_version_mask = (uint32)strtoul(row[11], nullptr, 0); + + zone_point_list->Insert(zp); + + zone->numzonepoints++; + } + return true; } @@ -1885,32 +1791,26 @@ bool Zone::RemoveSpawnGroup(uint32 in_id) { // Added By Hogie bool ZoneDatabase::GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int i = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT varname, value FROM variables WHERE varname like 'decaytime%%' ORDER BY varname"), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - Seperator sep(row[0]); - npcCorpseDecayTimes[i].minlvl = atoi(sep.arg[1]); - npcCorpseDecayTimes[i].maxlvl = atoi(sep.arg[2]); - if (atoi(row[1]) > 7200) - npcCorpseDecayTimes[i].seconds = 720; - else - npcCorpseDecayTimes[i].seconds = atoi(row[1]); - i++; - } - mysql_free_result(result); - } - else { - safe_delete_array(query); - return false; - } + const std::string query = "SELECT varname, value FROM variables WHERE varname LIKE 'decaytime%%' ORDER BY varname"; + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + int index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + Seperator sep(row[0]); + npcCorpseDecayTimes[index].minlvl = atoi(sep.arg[1]); + npcCorpseDecayTimes[index].maxlvl = atoi(sep.arg[2]); + + if (atoi(row[1]) > 7200) + npcCorpseDecayTimes[index].seconds = 720; + else + npcCorpseDecayTimes[index].seconds = atoi(row[1]); + } + return true; -}// Added By Hogie -- End +} void Zone::weatherSend() { @@ -2081,170 +1981,129 @@ void Zone::SetInstanceTimer(uint32 new_duration) void Zone::LoadLDoNTraps() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT id, type, spell_id, " - "skill, locked FROM ldon_trap_templates"), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - uint8 x = 0; - LDoNTrapTemplate *lt = new LDoNTrapTemplate; - lt->id = atoi(row[x++]); - lt->type = (LDoNChestTypes)atoi(row[x++]); - lt->spell_id = atoi(row[x++]); - lt->skill = atoi(row[x++]); - lt->locked = atoi(row[x++]); - ldon_trap_list[lt->id] = lt; - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Zone::LoadLDoNTraps: %s (%s)", query, errbuf); - safe_delete_array(query); + const std::string query = "SELECT id, type, spell_id, skill, locked FROM ldon_trap_templates"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadLDoNTraps: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return; - } + } + + for (auto row = results.begin();row != results.end(); ++row) { + LDoNTrapTemplate *lt = new LDoNTrapTemplate; + lt->id = atoi(row[0]); + lt->type = (LDoNChestTypes)atoi(row[1]); + lt->spell_id = atoi(row[2]); + lt->skill = atoi(row[3]); + lt->locked = atoi(row[4]); + ldon_trap_list[lt->id] = lt; + } + } void Zone::LoadLDoNTrapEntries() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT id, trap_id FROM ldon_trap_entries"),errbuf,&result)) { - while((row = mysql_fetch_row(result))) - { - uint32 id = atoi(row[0]); - uint32 trap_id = atoi(row[1]); - - LDoNTrapTemplate *tt = nullptr; - std::map::iterator it; - it = ldon_trap_list.find(trap_id); - if(it == ldon_trap_list.end()) - { - continue; - } - else - { - tt = ldon_trap_list[trap_id]; - } - - std::list temp; - std::map >::iterator iter; - - iter = ldon_trap_entry_list.find(id); - if(iter == ldon_trap_entry_list.end()) - { - temp.push_back(tt); - ldon_trap_entry_list[id] = temp; - } - else - { - temp = ldon_trap_entry_list[id]; - temp.push_back(tt); - ldon_trap_entry_list[id] = temp; - } - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Zone::LoadLDoNTrapEntries: %s (%s)", query, errbuf); - safe_delete_array(query); + const std::string query = "SELECT id, trap_id FROM ldon_trap_entries"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadLDoNTrapEntries: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return; - } + } + + for (auto row = results.begin(); row != results.end(); ++row) + { + uint32 id = atoi(row[0]); + uint32 trap_id = atoi(row[1]); + + LDoNTrapTemplate *trapTemplate = nullptr; + auto it = ldon_trap_list.find(trap_id); + + if(it == ldon_trap_list.end()) + continue; + + trapTemplate = ldon_trap_list[trap_id]; + + std::list temp; + auto iter = ldon_trap_entry_list.find(id); + + if(iter != ldon_trap_entry_list.end()) + temp = ldon_trap_entry_list[id]; + + temp.push_back(trapTemplate); + ldon_trap_entry_list[id] = temp; + } + } void Zone::LoadVeteranRewards() { VeteranRewards.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - InternalVeteranReward current_reward; - uint8 idx = 0; + InternalVeteranReward current_reward; current_reward.claim_id = 0; + const std::string query = "SELECT claim_id, name, item_id, charges " + "FROM veteran_reward_templates " + "WHERE reward_slot < 8 and claim_id > 0 " + "ORDER by claim_id, reward_slot"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadVeteranRewards: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); + return; + } - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT claim_id, name, item_id, charges FROM" - " veteran_reward_templates WHERE reward_slot < 8 and claim_id > 0 ORDER by claim_id, reward_slot"), - errbuf,&result)) - { - while((row = mysql_fetch_row(result))) - { - uint32 claim = atoi(row[0]); - if(claim != current_reward.claim_id) - { - if(current_reward.claim_id != 0) - { - current_reward.claim_count = idx; - current_reward.number_available = 1; - VeteranRewards.push_back(current_reward); - } - idx = 0; - memset(¤t_reward, 0, sizeof(InternalVeteranReward)); - current_reward.claim_id = claim; - } + int index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) + { + uint32 claim = atoi(row[0]); - strcpy(current_reward.items[idx].item_name, row[1]); - current_reward.items[idx].item_id = atoi(row[2]); - current_reward.items[idx].charges = atoi(row[3]); - idx++; - } + if(claim != current_reward.claim_id) + { + if(current_reward.claim_id != 0) + { + current_reward.claim_count = index; + current_reward.number_available = 1; + VeteranRewards.push_back(current_reward); + } + + index = 0; + memset(¤t_reward, 0, sizeof(InternalVeteranReward)); + current_reward.claim_id = claim; + } + + strcpy(current_reward.items[index].item_name, row[1]); + current_reward.items[index].item_id = atoi(row[2]); + current_reward.items[index].charges = atoi(row[3]); + } + + if(current_reward.claim_id != 0) + { + current_reward.claim_count = index; + current_reward.number_available = 1; + VeteranRewards.push_back(current_reward); + } - if(current_reward.claim_id != 0) - { - current_reward.claim_count = idx; - current_reward.number_available = 1; - VeteranRewards.push_back(current_reward); - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Zone::LoadVeteranRewards: %s (%s)", query, errbuf); - safe_delete_array(query); - } } void Zone::LoadAlternateCurrencies() { AlternateCurrencies.clear(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + AltCurrencyDefinition_Struct current_currency; - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT id, item_id from alternate_currency"), - errbuf,&result)) - { - while((row = mysql_fetch_row(result))) - { - current_currency.id = atoi(row[0]); - current_currency.item_id = atoi(row[1]); - AlternateCurrencies.push_back(current_currency); - } + const std::string query = "SELECT id, item_id FROM alternate_currency"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadAlternateCurrencies: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + { + current_currency.id = atoi(row[0]); + current_currency.item_id = atoi(row[1]); + AlternateCurrencies.push_back(current_currency); + } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Zone::LoadAlternateCurrencies: %s (%s)", query, errbuf); - safe_delete_array(query); - } } void Zone::UpdateQGlobal(uint32 qid, QGlobal newGlobal) @@ -2279,28 +2138,18 @@ void Zone::DeleteQGlobal(std::string name, uint32 npcID, uint32 charID, uint32 z void Zone::LoadAdventureFlavor() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT id, text FROM adventure_template_entry_flavor"), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - uint32 id = atoi(row[0]); - std::string in_str = row[1]; - adventure_entry_list_flavor[id] = in_str; - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Zone::LoadAdventureFlavor: %s (%s)", query, errbuf); - safe_delete_array(query); + const std::string query = "SELECT id, text FROM adventure_template_entry_flavor"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadAdventureFlavor: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); return; } + + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 id = atoi(row[0]); + adventure_entry_list_flavor[id] = row[1]; + } + } void Zone::DoAdventureCountIncrease() @@ -2362,31 +2211,25 @@ void Zone::DoAdventureActions() void Zone::LoadNPCEmotes(LinkedList* NPCEmoteList) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - NPCEmoteList->Clear(); - if(database.RunQuery(query,MakeAnyLenString(&query,"SELECT emoteid, event_, type, text FROM npc_emotes"), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - NPC_Emote_Struct* nes = new NPC_Emote_Struct; - nes->emoteid = atoi(row[0]); - nes->event_ = atoi(row[1]); - nes->type = atoi(row[2]); - strn0cpy(nes->text, row[3], sizeof(nes->text)); - NPCEmoteList->Insert(nes); - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Zone::LoadNPCEmotes: %s (%s)", query, errbuf); - safe_delete_array(query); - } + NPCEmoteList->Clear(); + const std::string query = "SELECT emoteid, event_, type, text FROM npc_emotes"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadNPCEmotes: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + { + NPC_Emote_Struct* nes = new NPC_Emote_Struct; + nes->emoteid = atoi(row[0]); + nes->event_ = atoi(row[1]); + nes->type = atoi(row[2]); + strn0cpy(nes->text, row[3], sizeof(nes->text)); + NPCEmoteList->Insert(nes); + } + } void Zone::ReloadWorld(uint32 Option){ @@ -2399,35 +2242,30 @@ void Zone::ReloadWorld(uint32 Option){ void Zone::LoadTickItems() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; tick_items.clear(); - if(database.RunQuery(query, MakeAnyLenString(&query, "SELECT it_itemid, it_chance, it_level, it_qglobal, it_bagslot FROM item_tick"), errbuf, &result)) - { - while((row = mysql_fetch_row(result))) - { - if(atoi(row[0]) >= 1) - { - item_tick_struct ti_tmp; - ti_tmp.itemid = atoi(row[0]); - ti_tmp.chance = atoi(row[1]); - ti_tmp.level = atoi(row[2]); - ti_tmp.bagslot = (int16)atoi(row[4]); - ti_tmp.qglobal = std::string(row[3]); - tick_items[atoi(row[0])] = ti_tmp; - } - } - mysql_free_result(result); - safe_delete_array(query); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in Zone::LoadTickItems: %s (%s)", query, errbuf); - safe_delete_array(query); - } + const std::string query = "SELECT it_itemid, it_chance, it_level, it_qglobal, it_bagslot FROM item_tick"; + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in Zone::LoadTickItems: %s (%s)", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + + for (auto row = results.begin(); row != results.end(); ++row) { + if(atoi(row[0]) == 0) + continue; + + item_tick_struct ti_tmp; + ti_tmp.itemid = atoi(row[0]); + ti_tmp.chance = atoi(row[1]); + ti_tmp.level = atoi(row[2]); + ti_tmp.bagslot = (int16)atoi(row[4]); + ti_tmp.qglobal = std::string(row[3]); + tick_items[atoi(row[0])] = ti_tmp; + + } + } uint32 Zone::GetSpawnKillCount(uint32 in_spawnid) { @@ -2447,24 +2285,15 @@ uint32 Zone::GetSpawnKillCount(uint32 in_spawnid) { void Zone::UpdateHotzone() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool updh; + std::string query = StringFormat("SELECT hotzone FROM zone WHERE short_name = '%s'", GetShortName()); + auto results = database.QueryDatabase(query); + if (!results.Success()) + return; - if(database.RunQuery(query, MakeAnyLenString(&query,"SELECT hotzone FROM zone WHERE short_name = '%s'", GetShortName()), errbuf, &result) ) - { - if( (row = mysql_fetch_row(result)) ) - { - updh = atoi(row[0]) == 0 ? false:true; - //Hotzone status has changed - if(is_hotzone != updh) - { - is_hotzone = updh; - } - } - mysql_free_result(result); - } - safe_delete_array(query); + if (results.RowCount() == 0) + return; + + auto row = results.begin(); + + is_hotzone = atoi(row[0]) == 0 ? false: true; } diff --git a/zone/zone.h b/zone/zone.h index 1553b72e8..2d74c9f17 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -18,25 +18,13 @@ #ifndef ZONE_H #define ZONE_H -#include "../common/Mutex.h" -#include "../common/linked_list.h" -#include "../common/types.h" #include "../common/eqtime.h" -#include "../common/servertalk.h" +#include "../common/linked_list.h" #include "../common/rulesys.h" -#include "../common/eq_packet_structs.h" -#include "../common/features.h" -#include "spawngroup.h" -//#include "mob.h" -#include "zonedump.h" +#include "../common/types.h" +#include "qglobals.h" #include "spawn2.h" -#include "tasks.h" -#include "pathing.h" -#include "QGlobals.h" -#include - -class Map; -class WaterMap; +#include "spawngroup.h" struct ZonePoint { @@ -78,12 +66,10 @@ struct item_tick_struct { std::string qglobal; }; -extern EntityList entity_list; -class database; +class Map; +class WaterMap; class PathManager; -struct SendAA_Struct; - -class database; +extern EntityList entity_list; class Zone { @@ -104,6 +90,7 @@ public: inline const uint32 GetZoneID() const { return zoneid; } inline const uint32 GetInstanceID() const { return instanceid; } inline const uint16 GetInstanceVersion() const { return instanceversion; } + inline const bool IsInstancePersistent() const { return pers_instance; } inline const uint8 GetZoneType() const { return zone_type; } inline Timer* GetInstanceTimer() { return Instance_Timer; } @@ -166,7 +153,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 +160,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(); @@ -277,6 +261,7 @@ private: uint32 zoneid; uint32 instanceid; uint16 instanceversion; + bool pers_instance; char* short_name; char file_name[16]; char* long_name; diff --git a/zone/ZoneConfig.cpp b/zone/zone_config.cpp similarity index 97% rename from zone/ZoneConfig.cpp rename to zone/zone_config.cpp index 65ecefa13..3775f1e4b 100644 --- a/zone/ZoneConfig.cpp +++ b/zone/zone_config.cpp @@ -16,7 +16,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "../common/debug.h" -#include "ZoneConfig.h" +#include "zone_config.h" ZoneConfig *ZoneConfig::_zone_config = nullptr; diff --git a/zone/ZoneConfig.h b/zone/zone_config.h similarity index 97% rename from zone/ZoneConfig.h rename to zone/zone_config.h index dac5a371d..d67cb5201 100644 --- a/zone/ZoneConfig.h +++ b/zone/zone_config.h @@ -18,7 +18,7 @@ #ifndef __ZoneConfig_H #define __ZoneConfig_H -#include "../common/EQEmuConfig.h" +#include "../common/eqemu_config.h" class ZoneConfig : public EQEmuConfig { public: diff --git a/zone/zone_logsys.cpp b/zone/zone_logsys.cpp index 3ecf9ec6b..59b4755dc 100644 --- a/zone/zone_logsys.cpp +++ b/zone/zone_logsys.cpp @@ -18,7 +18,7 @@ #include "../common/debug.h" #include "../common/logsys.h" -#include "../common/BasePacket.h" +#include "../common/base_packet.h" #include "mob.h" #include #include diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index 0d1653453..17b5ff508 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1,18 +1,16 @@ -#include "zonedb.h" -#include "../common/Item.h" -#include "../common/StringUtil.h" #include "../common/extprofile.h" -#include "../common/guilds.h" +#include "../common/item.h" #include "../common/rulesys.h" -#include "zone.h" +#include "../common/string_util.h" #include "client.h" -#include "merc.h" +#include "corpse.h" #include "groups.h" -#include "raids.h" +#include "merc.h" +#include "zone.h" +#include "zonedb.h" +#include #include -#include -#include extern Zone* zone; @@ -69,124 +67,121 @@ ZoneDatabase::~ZoneDatabase() { } } -bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "update zone set underworld=%f,minclip=%f," - "maxclip=%f,fog_minclip=%f,fog_maxclip=%f,fog_blue=%i,fog_red=%i,fog_green=%i,sky=%i," - "ztype=%i,zone_exp_multiplier=%f,safe_x=%f,safe_y=%f,safe_z=%f " - "where zoneidnumber=%i and version=%i", - zd->underworld,zd->minclip, - zd->maxclip,zd->fog_minclip[0],zd->fog_maxclip[0],zd->fog_blue[0],zd->fog_red[0],zd->fog_green[0],zd->sky, - zd->ztype,zd->zone_exp_multiplier, - zd->safe_x,zd->safe_y,zd->safe_z, - zoneid, instance_id),errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in SaveZoneCFG query %s: %s", query, errbuf); - safe_delete_array(query); - return false; +bool ZoneDatabase::SaveZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct* zd) { + + std::string query = StringFormat("UPDATE zone SET underworld = %f, minclip = %f, " + "maxclip = %f, fog_minclip = %f, fog_maxclip = %f, " + "fog_blue = %i, fog_red = %i, fog_green = %i, " + "sky = %i, ztype = %i, zone_exp_multiplier = %f, " + "safe_x = %f, safe_y = %f, safe_z = %f " + "WHERE zoneidnumber = %i AND version = %i", + zd->underworld, zd->minclip, + zd->maxclip, zd->fog_minclip[0], zd->fog_maxclip[0], + zd->fog_blue[0], zd->fog_red[0], zd->fog_green[0], + zd->sky, zd->ztype, zd->zone_exp_multiplier, + zd->safe_x, zd->safe_y, zd->safe_z, + zoneid, instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in SaveZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return false; } - safe_delete_array(query); + return true; } bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct *zone_data, bool &can_bind, bool &can_combat, bool &can_levitate, bool &can_castoutdoor, bool &is_city, bool &is_hotzone, bool &allow_mercs, uint8 &zone_type, int &ruleset, char **map_filename) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int i=0; - int b=0; - bool good = false; + *map_filename = new char[100]; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT ztype," - "fog_red,fog_green,fog_blue,fog_minclip,fog_maxclip," - "fog_red2,fog_green2,fog_blue2,fog_minclip2,fog_maxclip2," - "fog_red3,fog_green3,fog_blue3,fog_minclip3,fog_maxclip3," - "fog_red4,fog_green4,fog_blue4,fog_minclip4,fog_maxclip4,fog_density," - "sky,zone_exp_multiplier,safe_x,safe_y,safe_z,underworld," - "minclip,maxclip,time_type,canbind,cancombat,canlevitate," - "castoutdoor,hotzone,ruleset,suspendbuffs,map_file_name,short_name," - "rain_chance1,rain_chance2,rain_chance3,rain_chance4," - "rain_duration1,rain_duration2,rain_duration3,rain_duration4," - "snow_chance1,snow_chance2,snow_chance3,snow_chance4," - "snow_duration1,snow_duration2,snow_duration3,snow_duration4" - " from zone where zoneidnumber=%i and version=%i",zoneid, instance_id), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if(row) - { - int r = 0; - memset(zone_data,0,sizeof(NewZone_Struct)); - zone_data->ztype=atoi(row[r++]); - - for(i=0;i<4;i++){ - zone_data->fog_red[i]=atoi(row[r++]); - zone_data->fog_green[i]=atoi(row[r++]); - zone_data->fog_blue[i]=atoi(row[r++]); - zone_data->fog_minclip[i]=atof(row[r++]); - zone_data->fog_maxclip[i]=atof(row[r++]); - } - - zone_data->fog_density = atof(row[r++]);; - zone_data->sky=atoi(row[r++]); - zone_data->zone_exp_multiplier=atof(row[r++]); - zone_data->safe_x=atof(row[r++]); - zone_data->safe_y=atof(row[r++]); - zone_data->safe_z=atof(row[r++]); - zone_data->underworld=atof(row[r++]); - zone_data->minclip=atof(row[r++]); - zone_data->maxclip=atof(row[r++]); - - zone_data->time_type=atoi(row[r++]); -//not in the DB yet: - zone_data->gravity = 0.4; - - b = atoi(row[r++]); - can_bind = b==0?false:true; - is_city = b==2?true:false; - can_combat = atoi(row[r++])==0?false:true; - can_levitate = atoi(row[r++])==0?false:true; - can_castoutdoor = atoi(row[r++])==0?false:true; - is_hotzone = atoi(row[r++])==0?false:true; - allow_mercs = true; - zone_type = zone_data->ztype; - ruleset = atoi(row[r++]); - zone_data->SuspendBuffs = atoi(row[r++]); - char *file = row[r++]; - if(file) - { - strcpy(*map_filename, file); - } - else - { - strcpy(*map_filename, row[r++]); - } - for(i=0;i<4;i++){ - zone_data->rain_chance[i]=atoi(row[r++]); - } - for(i=0;i<4;i++){ - zone_data->rain_duration[i]=atoi(row[r++]); - } - for(i=0;i<4;i++){ - zone_data->snow_chance[i]=atoi(row[r++]); - } - for(i=0;i<4;i++){ - zone_data->snow_duration[i]=atof(row[r++]); - } - good = true; - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in GetZoneCFG query %s: %s", query, errbuf); - strcpy(*map_filename, "default"); - } - safe_delete_array(query); - zone_data->zone_id = zoneid; - return(good); + std::string query = StringFormat("SELECT ztype, fog_red, fog_green, fog_blue, fog_minclip, fog_maxclip, " // 5 + "fog_red2, fog_green2, fog_blue2, fog_minclip2, fog_maxclip2, " // 5 + "fog_red3, fog_green3, fog_blue3, fog_minclip3, fog_maxclip3, " // 5 + "fog_red4, fog_green4, fog_blue4, fog_minclip4, fog_maxclip4, " // 5 + "fog_density, sky, zone_exp_multiplier, safe_x, safe_y, safe_z, underworld, " // 7 + "minclip, maxclip, time_type, canbind, cancombat, canlevitate, " // 6 + "castoutdoor, hotzone, ruleset, suspendbuffs, map_file_name, short_name, " // 6 + "rain_chance1, rain_chance2, rain_chance3, rain_chance4, " // 4 + "rain_duration1, rain_duration2, rain_duration3, rain_duration4, " // 4 + "snow_chance1, snow_chance2, snow_chance3, snow_chance4, " // 4 + "snow_duration1, snow_duration2, snow_duration3, snow_duration4 " // 4 + "FROM zone WHERE zoneidnumber = %i AND version = %i", zoneid, instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetZoneCFG query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + strcpy(*map_filename, "default"); + return false; + } + + if (results.RowCount() == 0) { + strcpy(*map_filename, "default"); + return false; + } + + auto row = results.begin(); + + memset(zone_data, 0, sizeof(NewZone_Struct)); + zone_data->ztype = atoi(row[0]); + zone_type = zone_data->ztype; + + int index; + for(index = 0; index < 4; index++) { + zone_data->fog_red[index]=atoi(row[1 + index * 5]); + zone_data->fog_green[index]=atoi(row[2 + index * 5]); + zone_data->fog_blue[index]=atoi(row[3 + index * 5]); + zone_data->fog_minclip[index]=atof(row[4 + index * 5]); + zone_data->fog_maxclip[index]=atof(row[5 + index * 5]); + } + + zone_data->fog_density = atof(row[21]); + zone_data->sky=atoi(row[22]); + zone_data->zone_exp_multiplier=atof(row[23]); + zone_data->safe_x=atof(row[24]); + zone_data->safe_y=atof(row[25]); + zone_data->safe_z=atof(row[26]); + zone_data->underworld=atof(row[27]); + zone_data->minclip=atof(row[28]); + zone_data->maxclip=atof(row[29]); + zone_data->time_type=atoi(row[30]); + + //not in the DB yet: + zone_data->gravity = 0.4; + allow_mercs = true; + + int bindable = 0; + bindable = atoi(row[31]); + + can_bind = bindable == 0? false: true; + is_city = bindable == 2? true: false; + can_combat = atoi(row[32]) == 0? false: true; + can_levitate = atoi(row[33]) == 0? false: true; + can_castoutdoor = atoi(row[34]) == 0? false: true; + is_hotzone = atoi(row[35]) == 0? false: true; + + + ruleset = atoi(row[36]); + zone_data->SuspendBuffs = atoi(row[37]); + + char *file = row[38]; + if(file) + strcpy(*map_filename, file); + else + strcpy(*map_filename, row[39]); + + for(index = 0; index < 4; index++) + zone_data->rain_chance[index]=atoi(row[40 + index]); + + for(index = 0; index < 4; index++) + zone_data->rain_duration[index]=atoi(row[44 + index]); + + for(index = 0; index < 4; index++) + zone_data->snow_chance[index]=atoi(row[48 + index]); + + for(index = 0; index < 4; index++) + zone_data->snow_duration[index]=atof(row[52 + index]); + + return true; } //updates or clears the respawn time in the database for the current spawn id @@ -196,99 +191,73 @@ void ZoneDatabase::UpdateSpawn2Timeleft(uint32 id, uint16 instance_id, uint32 ti gettimeofday(&tv, nullptr); uint32 cur = tv.tv_sec; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - //if we pass timeleft as 0 that means we clear from respawn time //otherwise we update with a REPLACE INTO - if(timeleft == 0) - { - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM respawn_times WHERE id=%lu " - "AND instance_id=%lu",(unsigned long)id, (unsigned long)instance_id),errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query, errbuf); - } - safe_delete_array(query); - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO respawn_times (id,start,duration,instance_id) " - "VALUES(%lu,%lu,%lu,%lu)",(unsigned long)id, (unsigned long)cur, (unsigned long)timeleft, (unsigned long)instance_id),errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query, errbuf); - } - safe_delete_array(query); + if(timeleft == 0) { + std::string query = StringFormat("DELETE FROM respawn_times WHERE id=%lu " + "AND instance_id = %lu",(unsigned long)id, (unsigned long)instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + + return; } + + std::string query = StringFormat("REPLACE INTO respawn_times (id, start, duration, instance_id) " + "VALUES (%lu, %lu, %lu, %lu)", + (unsigned long)id, (unsigned long)cur, + (unsigned long)timeleft, (unsigned long)instance_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateTimeLeft query %s: %s", query.c_str(), results.ErrorMessage().c_str()); + return; } //Gets the respawn time left in the database for the current spawn id uint32 ZoneDatabase::GetSpawnTimeLeft(uint32 id, uint16 instance_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - MakeAnyLenString(&query, "SELECT start, duration FROM respawn_times WHERE id=%lu AND instance_id=%lu", - (unsigned long)id, (unsigned long)zone->GetInstanceID()); - - if (RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - row = mysql_fetch_row(result); - if(row) - { - timeval tv; - gettimeofday(&tv, nullptr); - uint32 resStart = atoi(row[0]); - uint32 resDuration = atoi(row[1]); - - //compare our values to current time - if((resStart + resDuration) <= tv.tv_sec) - { - //our current time was expired - mysql_free_result(result); - return 0; - } - else - { - //we still have time left on this timer - mysql_free_result(result); - return ((resStart + resDuration) - tv.tv_sec); - } - } - else - { - mysql_free_result(result); - return 0; - } - } - else - { - LogFile->write(EQEMuLog::Error, "Error in GetSpawnTimeLeft query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT start, duration FROM respawn_times " + "WHERE id = %lu AND instance_id = %lu", + (unsigned long)id, (unsigned long)zone->GetInstanceID()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in GetSpawnTimeLeft query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return 0; - } - return 0; + } + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + timeval tv; + gettimeofday(&tv, nullptr); + uint32 resStart = atoi(row[0]); + uint32 resDuration = atoi(row[1]); + + //compare our values to current time + if((resStart + resDuration) <= tv.tv_sec) { + //our current time was expired + return 0; + } + + //we still have time left on this timer + return ((resStart + resDuration) - tv.tv_sec); + } void ZoneDatabase::UpdateSpawn2Status(uint32 id, uint8 new_status) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + std::string query = StringFormat("UPDATE spawn2 SET enabled = %i WHERE id = %lu", new_status, (unsigned long)id); + auto results = QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in UpdateSpawn2Status query %s: %s", query.c_str(), results.ErrorMessage().c_str()); - if(!RunQuery(query, MakeAnyLenString(&query, "UPDATE spawn2 SET enabled=%i WHERE id=%lu", new_status, (unsigned long)id),errbuf)) - { - LogFile->write(EQEMuLog::Error, "Error in UpdateSpawn2Status query %s: %s", query, errbuf); - } - safe_delete_array(query); - return; } bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 status,const char* charname, const char* target,const char* descriptiontype, const char* description,int event_nid){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 len = strlen(description); uint32 len2 = strlen(target); char* descriptiontext = new char[2*len+1]; @@ -297,22 +266,25 @@ bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 stat memset(targetarr, 0, 2*len2+1); DoEscapeString(descriptiontext, description, len); DoEscapeString(targetarr, target, len2); - if (!RunQuery(query, MakeAnyLenString(&query, "Insert into eventlog (accountname,accountid,status,charname,target,descriptiontype,description,event_nid) values('%s',%i,%i,'%s','%s','%s','%s','%i')", accountname,accountid,status,charname,targetarr,descriptiontype,descriptiontext,event_nid), errbuf)) { - std::cerr << "Error in logevents" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + std::string query = StringFormat("INSERT INTO eventlog (accountname, accountid, status, " + "charname, target, descriptiontype, description, event_nid) " + "VALUES('%s', %i, %i, '%s', '%s', '%s', '%s', '%i')", + accountname, accountid, status, charname, targetarr, + descriptiontype, descriptiontext, event_nid); + safe_delete_array(descriptiontext); + safe_delete_array(targetarr); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in logevents" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); - safe_delete_array(descriptiontext); - safe_delete_array(targetarr); + return true; } -void ZoneDatabase::UpdateBug(BugStruct* bug){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - +void ZoneDatabase::UpdateBug(BugStruct* bug) { uint32 len = strlen(bug->bug); char* bugtext = nullptr; @@ -342,83 +314,45 @@ void ZoneDatabase::UpdateBug(BugStruct* bug){ } //x and y are intentionally swapped because eq is inversexy coords - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) " - "values('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())", zone->GetShortName(), bug->name, - uitext==nullptr?"":uitext, bug->y, bug->x, bug->z, bug->chartype, bug->type, targettext==nullptr?"Unknown Target":targettext, - bugtext==nullptr?"":bugtext), errbuf)) { - std::cerr << "Error in UpdateBug" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); - safe_delete_array(bugtext); + std::string query = StringFormat("INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) " + "VALUES('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())", + zone->GetShortName(), bug->name, uitext == nullptr ? "": uitext, + bug->x, bug->y, bug->z, bug->chartype, bug->type, targettext == nullptr? "Unknown Target": targettext, + bugtext==nullptr?"":bugtext); + safe_delete_array(bugtext); safe_delete_array(uitext); safe_delete_array(targettext); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl; + } void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; + uint32 len = strlen(bug->text); char* bugtext = new char[2*len+1]; memset(bugtext, 0, 2*len+1); DoEscapeString(bugtext, bug->text, len); - if (!RunQuery(query, MakeAnyLenString(&query, "Insert into bugs (type,name,bugtext,flag) values('%s','%s','%s',%i)","Petition",bug->name,bugtext,25), errbuf)) { - std::cerr << "Error in UpdateBug" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); - safe_delete_array(bugtext); + + std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) " + "VALUES('%s', '%s', '%s', %i)", + "Petition", bug->name, bugtext, 25); + safe_delete_array(bugtext); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in UpdateBug '" << query << "' " << results.ErrorMessage() << std::endl; + } - -bool ZoneDatabase::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) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET npcspecialattks='%s' WHERE id=%i;",flag,id), errbuf, 0, &affected_rows)) { - safe_delete_array(query); + std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id); + auto results = QueryDatabase(query); + if (!results.Success()) return false; - } - safe_delete_array(query); - if (affected_rows == 0) { - return false; - } - - return true; + return results.RowsAffected() != 0; } bool ZoneDatabase::DoorIsOpen(uint8 door_id,const char* zone_name) @@ -440,12 +374,6 @@ void ZoneDatabase::SetDoorPlace(uint8 value,uint8 door_id,const char* zone_name) void ZoneDatabase::GetEventLogs(const char* name,char* target,uint32 account_id,uint8 eventid,char* detail,char* timestamp, CharacterEventLog_Struct* cel) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - uint32 count = 0; char modifications[200]; if(strlen(name) != 0) sprintf(modifications,"charname=\'%s\'",name); @@ -453,46 +381,42 @@ void ZoneDatabase::GetEventLogs(const char* name,char* target,uint32 account_id, sprintf(modifications,"accountid=%i",account_id); if(strlen(target) != 0) - sprintf(modifications,"%s AND target like \'%%%s%%\'",modifications,target); + sprintf(modifications,"%s AND target LIKE \'%%%s%%\'",modifications,target); if(strlen(detail) != 0) - sprintf(modifications,"%s AND description like \'%%%s%%\'",modifications,detail); + sprintf(modifications,"%s AND description LIKE \'%%%s%%\'",modifications,detail); if(strlen(timestamp) != 0) - sprintf(modifications,"%s AND time like \'%%%s%%\'",modifications,timestamp); + sprintf(modifications,"%s AND time LIKE \'%%%s%%\'",modifications,timestamp); if(eventid == 0) eventid =1; sprintf(modifications,"%s AND event_nid=%i",modifications,eventid); - MakeAnyLenString(&query, "SELECT id,accountname,accountid,status,charname,target,time,descriptiontype,description FROM eventlog where %s",modifications); - if (RunQuery(query, strlen(query), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) - { - if(count > 255) - break; - cel->eld[count].id = atoi(row[0]); - strn0cpy(cel->eld[count].accountname,row[1],64); - cel->eld[count].account_id = atoi(row[2]); - cel->eld[count].status = atoi(row[3]); - strn0cpy(cel->eld[count].charactername,row[4],64); - strn0cpy(cel->eld[count].targetname,row[5],64); - sprintf(cel->eld[count].timestamp,"%s",row[6]); - strn0cpy(cel->eld[count].descriptiontype,row[7],64); - strn0cpy(cel->eld[count].details,row[8],128); - cel->eventid = eventid; - count++; - cel->count = count; - } - mysql_free_result(result); - } - else - { - // TODO: Invalid item length in database - safe_delete_array(query); - } + std::string query = StringFormat("SELECT id, accountname, accountid, status, charname, target, " + "time, descriptiontype, description FROM eventlog WHERE %s", modifications); + auto results = QueryDatabase(query); + if (!results.Success()) + return; + + int index = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++index) { + if(index == 255) + break; + + cel->eld[index].id = atoi(row[0]); + strn0cpy(cel->eld[index].accountname,row[1],64); + cel->eld[index].account_id = atoi(row[2]); + cel->eld[index].status = atoi(row[3]); + strn0cpy(cel->eld[index].charactername,row[4],64); + strn0cpy(cel->eld[index].targetname,row[5],64); + sprintf(cel->eld[index].timestamp,"%s",row[6]); + strn0cpy(cel->eld[index].descriptiontype,row[7],64); + strn0cpy(cel->eld[index].details,row[8],128); + cel->eventid = eventid; + cel->count = index + 1; + } + } // Load child objects for a world container (i.e., forge, bag dropped to ground, etc) @@ -503,234 +427,207 @@ void ZoneDatabase::LoadWorldContainer(uint32 parentid, ItemInst* container) return; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - //const Item_Struct* item = nullptr; - //ItemInst* inst = nullptr; + std::string query = StringFormat("SELECT bagidx, itemid, charges, augslot1, augslot2, augslot3, augslot4, augslot5 " + "FROM object_contents WHERE parentid = %i", parentid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in DB::LoadWorldContainer: %s", results.ErrorMessage().c_str()); + return; + } - uint32 len_query = MakeAnyLenString(&query, "select " - "bagidx,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5 from object_contents where parentid=%i", parentid); + for (auto row = results.begin(); row != results.end(); ++row) { + uint8 index = (uint8)atoi(row[0]); + uint32 item_id = (uint32)atoi(row[1]); + int8 charges = (int8)atoi(row[2]); + uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; + aug[0] = (uint32)atoi(row[3]); + aug[1] = (uint32)atoi(row[4]); + aug[2] = (uint32)atoi(row[5]); + aug[3] = (uint32)atoi(row[6]); + aug[4] = (uint32)atoi(row[7]); - if (RunQuery(query, len_query, errbuf, &result)) { - while ((row = mysql_fetch_row(result))) { - uint8 index = (uint8)atoi(row[0]); - uint32 item_id = (uint32)atoi(row[1]); - int8 charges = (int8)atoi(row[2]); - uint32 aug[EmuConstants::ITEM_COMMON_SIZE]; - aug[0] = (uint32)atoi(row[3]); - aug[1] = (uint32)atoi(row[4]); - aug[2] = (uint32)atoi(row[5]); - aug[3] = (uint32)atoi(row[6]); - aug[4] = (uint32)atoi(row[7]); + ItemInst* inst = database.CreateItem(item_id, charges); + if (inst && inst->GetItem()->ItemClass == ItemClassCommon) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) + if (aug[i]) + inst->PutAugment(&database, i, aug[i]); + // Put item inside world container + container->PutItem(index, *inst); + safe_delete(inst); + } + } - ItemInst* inst = database.CreateItem(item_id, charges); - if (inst) { - if (inst->GetItem()->ItemClass == ItemClassCommon) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - if (aug[i]) { - inst->PutAugment(&database, i, aug[i]); - } - } - } - // Put item inside world container - container->PutItem(index, *inst); - safe_delete(inst); - } - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in DB::LoadWorldContainer: %s", errbuf); - } - - safe_delete_array(query); } // Save child objects for a world container (i.e., forge, bag dropped to ground, etc) void ZoneDatabase::SaveWorldContainer(uint32 zone_id, uint32 parent_id, const ItemInst* container) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - // Since state is not saved for each world container action, we'll just delete // all and save from scratch .. we may come back later to optimize - //DeleteWorldContainer(parent_id); - - if (!container) { + if (!container) return; - } + //Delete all items from container DeleteWorldContainer(parent_id,zone_id); + // Save all 10 items, if they exist for (uint8 index = SUB_BEGIN; index < EmuConstants::ITEM_CONTAINER_SIZE; index++) { + ItemInst* inst = container->GetItem(index); - if (inst) { - uint32 item_id = inst->GetItem()->ID; - uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (inst->IsType(ItemClassCommon)) { - for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { - ItemInst *auginst=inst->GetAugment(i); - augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } - } - uint32 len_query = MakeAnyLenString(&query, + if (!inst) + continue; - "replace into object_contents (zoneid,parentid,bagidx,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5,droptime) values (%i,%i,%i,%i,%i,%i,%i,%i,%i,%i,now())", - zone_id, parent_id, index, item_id, inst->GetCharges(),augslot[0],augslot[1],augslot[2],augslot[3],augslot[4]); + uint32 item_id = inst->GetItem()->ID; + uint32 augslot[EmuConstants::ITEM_COMMON_SIZE] = { NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM, NO_ITEM }; - if (!RunQuery(query, len_query, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", errbuf); - } - safe_delete_array(query); - } + if (inst->IsType(ItemClassCommon)) { + for(int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { + ItemInst *auginst=inst->GetAugment(i); + augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; + } + } + + std::string query = StringFormat("REPLACE INTO object_contents " + "(zoneid, parentid, bagidx, itemid, charges, " + "augslot1, augslot2, augslot3, augslot4, augslot5, droptime) " + "VALUES (%i, %i, %i, %i, %i, %i, %i, %i, %i, %i, now())", + zone_id, parent_id, index, item_id, inst->GetCharges(), + augslot[0], augslot[1], augslot[2], augslot[3], augslot[4]); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::SaveWorldContainer: %s", results.ErrorMessage().c_str()); + + } - } } // Remove all child objects inside a world container (i.e., forge, bag dropped to ground, etc) -void ZoneDatabase::DeleteWorldContainer(uint32 parent_id,uint32 zone_id) +void ZoneDatabase::DeleteWorldContainer(uint32 parent_id, uint32 zone_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + std::string query = StringFormat("DELETE FROM object_contents WHERE parentid = %i AND zoneid = %i", parent_id, zone_id); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::DeleteWorldContainer: %s", results.ErrorMessage().c_str()); - uint32 len_query = MakeAnyLenString(&query, - "delete from object_contents where parentid=%i and zoneid=%i", parent_id,zone_id); - if (!RunQuery(query, len_query, errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::DeleteWorldContainer: %s", errbuf); - } - - safe_delete_array(query); } -Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; +Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id) +{ Trader_Struct* loadti = new Trader_Struct; memset(loadti,0,sizeof(Trader_Struct)); - if (RunQuery(query,MakeAnyLenString(&query, "select * from trader where char_id=%i order by slot_id limit 80",char_id),errbuf,&result)){ - safe_delete_array(query); - loadti->Code = BazaarTrader_ShowItems; - while ((row = mysql_fetch_row(result))) { - if(atoi(row[5])>=80 || atoi(row[4])<0) - _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); - else{ - loadti->Items[atoi(row[5])] = atoi(row[1]); - loadti->ItemCost[atoi(row[5])] = atoi(row[4]); - } - } - mysql_free_result(result); - } - else{ - safe_delete_array(query); + + std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i ORDER BY slot_id LIMIT 80", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { _log(TRADING__CLIENT, "Failed to load trader information!\n"); + return loadti; + } + + loadti->Code = BazaarTrader_ShowItems; + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[5]) >= 80 || atoi(row[4]) < 0) { + _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); + continue; + } + + loadti->Items[atoi(row[5])] = atoi(row[1]); + loadti->ItemCost[atoi(row[5])] = atoi(row[4]); } return loadti; } -TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; +TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id) +{ TraderCharges_Struct* loadti = new TraderCharges_Struct; memset(loadti,0,sizeof(TraderCharges_Struct)); - if (RunQuery(query,MakeAnyLenString(&query, "select * from trader where char_id=%i order by slot_id limit 80",char_id),errbuf,&result)){ - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - if(atoi(row[5])>=80 || atoi(row[5])<0) - _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); - else{ - loadti->ItemID[atoi(row[5])] = atoi(row[1]); - loadti->SerialNumber[atoi(row[5])] = atoi(row[2]); - loadti->Charges[atoi(row[5])] = atoi(row[3]); - loadti->ItemCost[atoi(row[5])] = atoi(row[4]); - } - } - mysql_free_result(result); - } - else{ - safe_delete_array(query); + + std::string query = StringFormat("SELECT * FROM trader WHERE char_id=%i ORDER BY slot_id LIMIT 80", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { _log(TRADING__CLIENT, "Failed to load trader information!\n"); + return loadti; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + if (atoi(row[5]) >= 80 || atoi(row[5]) < 0) { + _log(TRADING__CLIENT, "Bad Slot number when trying to load trader information!\n"); + continue; + } + + loadti->ItemID[atoi(row[5])] = atoi(row[1]); + loadti->SerialNumber[atoi(row[5])] = atoi(row[2]); + loadti->Charges[atoi(row[5])] = atoi(row[3]); + loadti->ItemCost[atoi(row[5])] = atoi(row[4]); } return loadti; } -ItemInst* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) { +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); + if (!results.Success()) + return nullptr; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + if (results.RowCount() == 0) { + _log(TRADING__CLIENT, "Bad result from query\n"); fflush(stdout); + return nullptr; + } - if (RunQuery(query,MakeAnyLenString(&query, "select * from trader where char_id=%i and serialnumber=%i order by slot_id limit 80", - CharID, SerialNumber),errbuf,&result)){ - safe_delete_array(query); + auto row = results.begin(); - if (mysql_num_rows(result) != 1) { - _log(TRADING__CLIENT, "Bad result from query\n"); fflush(stdout); - return nullptr; - } - row = mysql_fetch_row(result); - int ItemID = atoi(row[1]); - int Charges = atoi(row[3]); - int Cost = atoi(row[4]); + int ItemID = atoi(row[1]); + int Charges = atoi(row[3]); + int Cost = atoi(row[4]); - const Item_Struct *item=database.GetItem(ItemID); + const Item_Struct *item = database.GetItem(ItemID); - if(!item) { - _log(TRADING__CLIENT, "Unable to create item\n"); fflush(stdout); - return nullptr; - } - - if (item && (item->NoDrop!=0)) { - ItemInst* inst = database.CreateItem(item); - if(!inst) { - _log(TRADING__CLIENT, "Unable to create item instance\n"); fflush(stdout); - return nullptr; - } - - inst->SetCharges(Charges); - inst->SetSerialNumber(SerialNumber); - inst->SetMerchantSlot(SerialNumber); - inst->SetPrice(Cost); - if(inst->IsStackable()) - inst->SetMerchantCount(Charges); - - return inst; - } + if(!item) { + _log(TRADING__CLIENT, "Unable to create item\n"); + fflush(stdout); + return nullptr; } - return nullptr; + if (item->NoDrop == 0) + return nullptr; + ItemInst* inst = database.CreateItem(item); + if(!inst) { + _log(TRADING__CLIENT, "Unable to create item instance\n"); + fflush(stdout); + return nullptr; + } + inst->SetCharges(Charges); + inst->SetSerialNumber(SerialNumber); + inst->SetMerchantSlot(SerialNumber); + inst->SetPrice(Cost); + + if(inst->IsStackable()) + inst->SetMerchantCount(Charges); + + return inst; } void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNumber, int32 Charges, uint32 ItemCost, uint8 Slot){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "replace INTO trader VALUES(%i,%i,%i,%i,%i,%i)", - CharID, ItemID, SerialNumber, Charges, ItemCost, Slot),errbuf))) - _log(TRADING__CLIENT, "Failed to save trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, errbuf); + std::string query = StringFormat("REPLACE INTO trader VALUES(%i, %i, %i, %i, %i, %i)", + CharID, ItemID, SerialNumber, Charges, ItemCost, Slot); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to save trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - safe_delete_array(query); } -void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { - +void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) { _log(TRADING__CLIENT, "ZoneDatabase::UpdateTraderItemCharges(%i, %i, %i)", CharID, SerialNumber, Charges); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "update trader set charges=%i where char_id=%i and serialnumber=%i", - Charges, CharID, SerialNumber),errbuf))) - _log(TRADING__CLIENT, "Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n", - SerialNumber, CharID, errbuf); - safe_delete_array(query); + std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i", + Charges, CharID, SerialNumber); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update charges for trader item: %i for char_id: %i, the error was: %s\n", + SerialNumber, CharID, results.ErrorMessage().c_str()); } @@ -743,267 +640,1081 @@ void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charg if(!item) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - - char* Query = 0; - if(NewPrice == 0) { _log(TRADING__CLIENT, "Removing Trader items from the DB for CharID %i, ItemID %i", CharID, ItemID); - if (!(RunQuery(Query,MakeAnyLenString(&Query, "delete from trader where char_id=%i and item_id=%i", - CharID, ItemID),errbuf))) - - _log(TRADING__CLIENT, "Failed to remove trader item(s): %i for char_id: %i, the error was: %s\n", - ItemID, CharID, errbuf); - - safe_delete_array(Query); + std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i AND item_id = %i",CharID, ItemID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to remove trader item(s): %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); return; } - else { - if(!item->Stackable) { - if (!(RunQuery(Query,MakeAnyLenString(&Query, "update trader set item_cost=%i where char_id=%i and item_id=%i" - " and charges=%i", NewPrice, CharID, ItemID, Charges),errbuf))) - _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", - ItemID, CharID, errbuf); - } - else { - if (!(RunQuery(Query,MakeAnyLenString(&Query, "update trader set item_cost=%i where char_id=%i and item_id=%i", - NewPrice, CharID, ItemID),errbuf))) + if(!item->Stackable) { + std::string query = StringFormat("UPDATE trader SET item_cost = %i " + "WHERE char_id = %i AND item_id = %i AND charges=%i", + NewPrice, CharID, ItemID, Charges); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", - ItemID, CharID, errbuf); - } - - safe_delete_array(Query); - } + return; + } + std::string query = StringFormat("UPDATE trader SET item_cost = %i " + "WHERE char_id = %i AND item_id = %i", + NewPrice, CharID, ItemID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update price for trader item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); } void ZoneDatabase::DeleteTraderItem(uint32 char_id){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if(char_id==0){ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader"),errbuf))) - _log(TRADING__CLIENT, "Failed to delete all trader items data, the error was: %s\n",errbuf); + + if(char_id==0) { + const std::string query = "DELETE FROM trader"; + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete all trader items data, the error was: %s\n", results.ErrorMessage().c_str()); + + return; } - else{ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader where char_id=%i",char_id),errbuf))) - _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",char_id,errbuf); + + std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n", char_id, results.ErrorMessage().c_str()); + +} +void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID) { + + std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i And slot_id = %i", CharID, SlotID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",CharID, results.ErrorMessage().c_str()); +} + +void ZoneDatabase::DeleteBuyLines(uint32 CharID) { + + if(CharID==0) { + const std::string query = "DELETE FROM buyer"; + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete all buyer items data, the error was: %s\n",results.ErrorMessage().c_str()); + + return; } - safe_delete_array(query); -} -void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from trader where char_id=%i and slot_id=%i",CharID, SlotID),errbuf))) - _log(TRADING__CLIENT, "Failed to delete trader item data for char_id: %i, the error was: %s\n",CharID, errbuf); - safe_delete_array(query); + + std::string query = StringFormat("DELETE FROM buyer WHERE charid = %i", CharID); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to delete buyer item data for charid: %i, the error was: %s\n",CharID,results.ErrorMessage().c_str()); + } -void ZoneDatabase::DeleteBuyLines(uint32 CharID){ +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); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to save buline item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, results.ErrorMessage().c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if(CharID==0){ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from buyer"),errbuf))) - _log(TRADING__CLIENT, "Failed to delete all buyer items data, the error was: %s\n",errbuf); - } - else{ - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from buyer where charid=%i",CharID),errbuf))) - _log(TRADING__CLIENT, "Failed to delete buyer item data for charid: %i, the error was: %s\n",CharID,errbuf); - } - safe_delete_array(query); } -void ZoneDatabase::AddBuyLine(uint32 CharID, uint32 BuySlot, uint32 ItemID, const char* ItemName, uint32 Quantity, uint32 Price) { +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()) + _log(TRADING__CLIENT, "Failed to delete buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - if (!(RunQuery(query,MakeAnyLenString(&query, "replace INTO buyer VALUES(%i,%i, %i,\"%s\",%i,%i)", - CharID, BuySlot, ItemID, ItemName, Quantity, Price),errbuf))) - _log(TRADING__CLIENT, "Failed to save buline item: %i for char_id: %i, the error was: %s\n", ItemID, CharID, errbuf); - - safe_delete_array(query); } -void ZoneDatabase::RemoveBuyLine(uint32 CharID, uint32 BuySlot) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - - if (!(RunQuery(query,MakeAnyLenString(&query, "delete from buyer where charid=%i and buyslot=%i", CharID, BuySlot), errbuf))) - _log(TRADING__CLIENT, "Failed to delete buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, errbuf); - - safe_delete_array(query); -} - -void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { - +void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity) { if(Quantity <= 0) { RemoveBuyLine(CharID, BuySlot); return; } - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; + std::string query = StringFormat("UPDATE buyer SET quantity = %i WHERE charid = %i AND buyslot = %i", Quantity, CharID, BuySlot); + auto results = QueryDatabase(query); + if (!results.Success()) + _log(TRADING__CLIENT, "Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, results.ErrorMessage().c_str()); - if (!(RunQuery(query,MakeAnyLenString(&query, "update buyer set quantity=%i where charid=%i and buyslot=%i", - Quantity, CharID, BuySlot), errbuf))) - _log(TRADING__CLIENT, "Failed to update quantity in buyslot %i for charid: %i, the error was: %s\n", BuySlot, CharID, errbuf); - - safe_delete_array(query); - -} - -bool ZoneDatabase::GetCharacterInfoForLogin(const char* name, 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) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 querylen; - MYSQL_RES *result; - - bool ret = false; - - //DO NOT FORGET TO EDIT Client::Handle_Connect_OP_ZoneEntry if you change this. - - if (character_id && *character_id) { - // searching by ID should be a lil bit faster - querylen = 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); - } - else { - querylen = 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 name='%s'", name); - } - - if (RunQuery(query, querylen, errbuf, &result)) { - ret = GetCharacterInfoForLogin_result(result, character_id, current_zone, pp, inv, ext, pplen, guilddbid, guildrank, class_, level, LFP, LFG, NumXTargets, firstlogon); - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "GetCharacterInfoForLogin query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; } #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 && atoi(row[1]) <= SPDAT_RECORDS){ + 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 && atoi(row[1]) <= SPDAT_RECORDS){ + 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++]); + pp->binds[4].instance_id = atoi(row[i++]); + pp->binds[4].x = atoi(row[i++]); + pp->binds[4].y = atoi(row[i++]); + pp->binds[4].z = atoi(row[i++]); + pp->binds[4].heading = atoi(row[i++]); + } + /* Is regular bind point */ + else{ + pp->binds[0].zoneId = atoi(row[i++]); + pp->binds[0].instance_id = atoi(row[i++]); + pp->binds[0].x = atoi(row[i++]); + pp->binds[0].y = atoi(row[i++]); + pp->binds[0].z = atoi(row[i++]); + pp->binds[0].heading = atoi(row[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()) { + LogFile->write(EQEMuLog::Debug, "ERROR Bind Home Save: %s. %s", results.ErrorMessage().c_str(), query.c_str()); + } + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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); + 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){ + if (spell_id > SPDAT_RECORDS){ return false; } + 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){ + if (spell_id > SPDAT_RECORDS){ return false; } + 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){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "Select (UNIX_TIMESTAMP(NOW())-timelaston) from character_ where name='%s'", name), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 seconds = atoi(row[0]); - mysql_free_result(result); - return (seconds>1800); - } - } - return false; + std::string query = StringFormat("SELECT (UNIX_TIMESTAMP(NOW()) - last_login) FROM `character_data` WHERE name = '%s'", name); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + if (results.RowCount() != 1) + return false; + + auto row = results.begin(); + uint32 seconds = atoi(row[0]); + + return (seconds>1800); } /* Searches npctable for matching id, and returns the item if found, @@ -1012,1939 +1723,1376 @@ bool ZoneDatabase::NoRentExpired(const char* name){ */ const NPCType* ZoneDatabase::GetNPCType (uint32 id) { const NPCType *npc=nullptr; - std::map::iterator itr; // If NPC is already in tree, return it. - if((itr = zone->npctable.find(id)) != zone->npctable.end()) + auto itr = zone->npctable.find(id); + if(itr != zone->npctable.end()) return itr->second; - // Otherwise, get NPCs from database. - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + // Otherwise, get NPCs from database. - // If id is 0, load all npc_types for the current zone, - // according to spawn2. - const char *basic_query = "SELECT " - "npc_types.id," - "npc_types.name," - "npc_types.level," - "npc_types.race," - "npc_types.class," - "npc_types.hp," - "npc_types.mana," - "npc_types.gender," - "npc_types.texture," - "npc_types.helmtexture," - "npc_types.size," - "npc_types.loottable_id," - "npc_types.merchant_id," - "npc_types.alt_currency_id," - "npc_types.adventure_template_id," - "npc_types.trap_template," - "npc_types.attack_speed," - "npc_types.STR," - "npc_types.STA," - "npc_types.DEX," - "npc_types.AGI," - "npc_types._INT," - "npc_types.WIS," - "npc_types.CHA," - "npc_types.MR," - "npc_types.CR," - "npc_types.DR," - "npc_types.FR," - "npc_types.PR," - "npc_types.Corrup," - "npc_types.PhR," - "npc_types.mindmg," - "npc_types.maxdmg," - "npc_types.attack_count," - "npc_types.special_abilities," - "npc_types.npc_spells_id," - "npc_types.npc_spells_effects_id," - "npc_types.d_meele_texture1," - "npc_types.d_meele_texture2," - "npc_types.ammo_idfile," - "npc_types.prim_melee_type," - "npc_types.sec_melee_type," - "npc_types.ranged_type," - "npc_types.runspeed," - "npc_types.findable," - "npc_types.trackable," - "npc_types.hp_regen_rate," - "npc_types.mana_regen_rate," - "npc_types.aggroradius," - "npc_types.assistradius," - "npc_types.bodytype," - "npc_types.npc_faction_id," - "npc_types.face," - "npc_types.luclin_hairstyle," - "npc_types.luclin_haircolor," - "npc_types.luclin_eyecolor," - "npc_types.luclin_eyecolor2," - "npc_types.luclin_beardcolor," - "npc_types.luclin_beard," - "npc_types.drakkin_heritage," - "npc_types.drakkin_tattoo," - "npc_types.drakkin_details," - "npc_types.armortint_id," - "npc_types.armortint_red," - "npc_types.armortint_green," - "npc_types.armortint_blue," - "npc_types.see_invis," - "npc_types.see_invis_undead," - "npc_types.lastname," - "npc_types.qglobal," - "npc_types.AC," - "npc_types.npc_aggro," - "npc_types.spawn_limit," - "npc_types.see_hide," - "npc_types.see_improved_hide," - "npc_types.ATK," - "npc_types.Accuracy," - "npc_types.Avoidance," - "npc_types.slow_mitigation," - "npc_types.maxlevel," - "npc_types.scalerate," - "npc_types.private_corpse," - "npc_types.unique_spawn_by_name," - "npc_types.underwater," - "npc_types.emoteid," - "npc_types.spellscale," - "npc_types.healscale," - "npc_types.no_target_hotkey," - "npc_types.raid_target"; + // If id is 0, load all npc_types for the current zone, + // according to spawn2. + std::string query = StringFormat("SELECT npc_types.id, npc_types.name, npc_types.level, npc_types.race, " + "npc_types.class, npc_types.hp, npc_types.mana, npc_types.gender, " + "npc_types.texture, npc_types.helmtexture, npc_types.size, " + "npc_types.loottable_id, npc_types.merchant_id, npc_types.alt_currency_id, " + "npc_types.adventure_template_id, npc_types.trap_template, npc_types.attack_speed, " + "npc_types.STR, npc_types.STA, npc_types.DEX, npc_types.AGI, npc_types._INT, " + "npc_types.WIS, npc_types.CHA, npc_types.MR, npc_types.CR, npc_types.DR, " + "npc_types.FR, npc_types.PR, npc_types.Corrup, npc_types.PhR," + "npc_types.mindmg, npc_types.maxdmg, npc_types.attack_count, npc_types.special_abilities," + "npc_types.npc_spells_id, npc_types.npc_spells_effects_id, npc_types.d_meele_texture1," + "npc_types.d_meele_texture2, npc_types.ammo_idfile, npc_types.prim_melee_type," + "npc_types.sec_melee_type, npc_types.ranged_type, npc_types.runspeed, npc_types.findable," + "npc_types.trackable, npc_types.hp_regen_rate, npc_types.mana_regen_rate, " + "npc_types.aggroradius, npc_types.assistradius, npc_types.bodytype, npc_types.npc_faction_id, " + "npc_types.face, npc_types.luclin_hairstyle, npc_types.luclin_haircolor, " + "npc_types.luclin_eyecolor, npc_types.luclin_eyecolor2, npc_types.luclin_beardcolor," + "npc_types.luclin_beard, npc_types.drakkin_heritage, npc_types.drakkin_tattoo, " + "npc_types.drakkin_details, npc_types.armortint_id, " + "npc_types.armortint_red, npc_types.armortint_green, npc_types.armortint_blue, " + "npc_types.see_invis, npc_types.see_invis_undead, npc_types.lastname, " + "npc_types.qglobal, npc_types.AC, npc_types.npc_aggro, npc_types.spawn_limit, " + "npc_types.see_hide, npc_types.see_improved_hide, npc_types.ATK, npc_types.Accuracy, " + "npc_types.Avoidance, npc_types.slow_mitigation, npc_types.maxlevel, npc_types.scalerate, " + "npc_types.private_corpse, npc_types.unique_spawn_by_name, npc_types.underwater, " + "npc_types.emoteid, npc_types.spellscale, npc_types.healscale, npc_types.no_target_hotkey," + "npc_types.raid_target, npc_types.attack_delay FROM npc_types WHERE id = %d", id); - MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl; + return nullptr; + } - if (RunQuery(query, strlen(query), errbuf, &result)) { - // Process each row returned. - while((row = mysql_fetch_row(result))) { - NPCType *tmpNPCType; - tmpNPCType = new NPCType; - memset (tmpNPCType, 0, sizeof *tmpNPCType); - int r = 0; - tmpNPCType->npc_id = atoi(row[r++]); + for (auto row = results.begin(); row != results.end(); ++row) { + NPCType *tmpNPCType; + tmpNPCType = new NPCType; + memset (tmpNPCType, 0, sizeof *tmpNPCType); - strn0cpy(tmpNPCType->name, row[r++], 50); + tmpNPCType->npc_id = atoi(row[0]); - tmpNPCType->level = atoi(row[r++]); - tmpNPCType->race = atoi(row[r++]); - tmpNPCType->class_ = atoi(row[r++]); - tmpNPCType->max_hp = atoi(row[r++]); - tmpNPCType->cur_hp = tmpNPCType->max_hp; - tmpNPCType->Mana = atoi(row[r++]); - tmpNPCType->gender = atoi(row[r++]); - tmpNPCType->texture = atoi(row[r++]); - tmpNPCType->helmtexture = atoi(row[r++]); - tmpNPCType->size = atof(row[r++]); - tmpNPCType->loottable_id = atoi(row[r++]); - tmpNPCType->merchanttype = atoi(row[r++]); - tmpNPCType->alt_currency_type = atoi(row[r++]); - tmpNPCType->adventure_template = atoi(row[r++]); - tmpNPCType->trap_template = atoi(row[r++]); - tmpNPCType->attack_speed = atof(row[r++]); - tmpNPCType->STR = atoi(row[r++]); - tmpNPCType->STA = atoi(row[r++]); - tmpNPCType->DEX = atoi(row[r++]); - tmpNPCType->AGI = atoi(row[r++]); - tmpNPCType->INT = atoi(row[r++]); - tmpNPCType->WIS = atoi(row[r++]); - tmpNPCType->CHA = atoi(row[r++]); - tmpNPCType->MR = atoi(row[r++]); - tmpNPCType->CR = atoi(row[r++]); - tmpNPCType->DR = atoi(row[r++]); - tmpNPCType->FR = atoi(row[r++]); - tmpNPCType->PR = atoi(row[r++]); - tmpNPCType->Corrup = atoi(row[r++]); - tmpNPCType->PhR = atoi(row[r++]); - tmpNPCType->min_dmg = atoi(row[r++]); - tmpNPCType->max_dmg = atoi(row[r++]); - tmpNPCType->attack_count = atoi(row[r++]); - strn0cpy(tmpNPCType->special_abilities, row[r++], 512); - tmpNPCType->npc_spells_id = atoi(row[r++]); - tmpNPCType->npc_spells_effects_id = atoi(row[r++]); - tmpNPCType->d_meele_texture1 = atoi(row[r++]); - tmpNPCType->d_meele_texture2 = atoi(row[r++]); - strn0cpy(tmpNPCType->ammo_idfile, row[r++], 30); - tmpNPCType->prim_melee_type = atoi(row[r++]); - tmpNPCType->sec_melee_type = atoi(row[r++]); - tmpNPCType->ranged_type = atoi(row[r++]); - tmpNPCType->runspeed= atof(row[r++]); - tmpNPCType->findable = atoi(row[r++]) == 0? false : true; - tmpNPCType->trackable = atoi(row[r++]) == 0? false : true; - tmpNPCType->hp_regen = atoi(row[r++]); - tmpNPCType->mana_regen = atoi(row[r++]); + strn0cpy(tmpNPCType->name, row[1], 50); - tmpNPCType->aggroradius = (int32)atoi(row[r++]); - // set defaultvalue for aggroradius - if (tmpNPCType->aggroradius <= 0) - tmpNPCType->aggroradius = 70; - tmpNPCType->assistradius = (int32)atoi(row[r++]); - if (tmpNPCType->assistradius <= 0) - tmpNPCType->assistradius = tmpNPCType->aggroradius; + tmpNPCType->level = atoi(row[2]); + tmpNPCType->race = atoi(row[3]); + tmpNPCType->class_ = atoi(row[4]); + tmpNPCType->max_hp = atoi(row[5]); + tmpNPCType->cur_hp = tmpNPCType->max_hp; + tmpNPCType->Mana = atoi(row[6]); + tmpNPCType->gender = atoi(row[7]); + tmpNPCType->texture = atoi(row[8]); + tmpNPCType->helmtexture = atoi(row[9]); + tmpNPCType->size = atof(row[10]); + tmpNPCType->loottable_id = atoi(row[11]); + tmpNPCType->merchanttype = atoi(row[12]); + tmpNPCType->alt_currency_type = atoi(row[13]); + tmpNPCType->adventure_template = atoi(row[14]); + tmpNPCType->trap_template = atoi(row[15]); + tmpNPCType->attack_speed = atof(row[16]); + tmpNPCType->STR = atoi(row[17]); + tmpNPCType->STA = atoi(row[18]); + tmpNPCType->DEX = atoi(row[19]); + tmpNPCType->AGI = atoi(row[20]); + tmpNPCType->INT = atoi(row[21]); + tmpNPCType->WIS = atoi(row[22]); + tmpNPCType->CHA = atoi(row[23]); + tmpNPCType->MR = atoi(row[24]); + tmpNPCType->CR = atoi(row[25]); + tmpNPCType->DR = atoi(row[26]); + tmpNPCType->FR = atoi(row[27]); + tmpNPCType->PR = atoi(row[28]); + tmpNPCType->Corrup = atoi(row[29]); + tmpNPCType->PhR = atoi(row[30]); + tmpNPCType->min_dmg = atoi(row[31]); + tmpNPCType->max_dmg = atoi(row[32]); + tmpNPCType->attack_count = atoi(row[33]); - if (row[r] && strlen(row[r])) - tmpNPCType->bodytype = (uint8)atoi(row[r]); - else - tmpNPCType->bodytype = 0; - r++; + if (row[34] != nullptr) + strn0cpy(tmpNPCType->special_abilities, row[34], 512); + else + tmpNPCType->special_abilities[0] = '\0'; - tmpNPCType->npc_faction_id = atoi(row[r++]); + tmpNPCType->npc_spells_id = atoi(row[35]); + tmpNPCType->npc_spells_effects_id = atoi(row[36]); + tmpNPCType->d_meele_texture1 = atoi(row[37]); + tmpNPCType->d_meele_texture2 = atoi(row[38]); + strn0cpy(tmpNPCType->ammo_idfile, row[39], 30); + tmpNPCType->prim_melee_type = atoi(row[40]); + tmpNPCType->sec_melee_type = atoi(row[41]); + tmpNPCType->ranged_type = atoi(row[42]); + tmpNPCType->runspeed= atof(row[43]); + tmpNPCType->findable = atoi(row[44]) == 0? false : true; + tmpNPCType->trackable = atoi(row[45]) == 0? false : true; + tmpNPCType->hp_regen = atoi(row[46]); + tmpNPCType->mana_regen = atoi(row[47]); - tmpNPCType->luclinface = atoi(row[r++]); - tmpNPCType->hairstyle = atoi(row[r++]); - tmpNPCType->haircolor = atoi(row[r++]); - tmpNPCType->eyecolor1 = atoi(row[r++]); - tmpNPCType->eyecolor2 = atoi(row[r++]); - tmpNPCType->beardcolor = atoi(row[r++]); - tmpNPCType->beard = atoi(row[r++]); - tmpNPCType->drakkin_heritage = atoi(row[r++]); - tmpNPCType->drakkin_tattoo = atoi(row[r++]); - tmpNPCType->drakkin_details = atoi(row[r++]); - uint32 armor_tint_id = atoi(row[r++]); - tmpNPCType->armor_tint[0] = (atoi(row[r++]) & 0xFF) << 16; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF); - tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - - int i; - if (armor_tint_id > 0) - { - if (tmpNPCType->armor_tint[0] == 0) - { - char at_errbuf[MYSQL_ERRMSG_SIZE]; - char *at_query = nullptr; - MYSQL_RES *at_result = nullptr; - MYSQL_ROW at_row; + // set defaultvalue for aggroradius + tmpNPCType->aggroradius = (int32)atoi(row[48]); + if (tmpNPCType->aggroradius <= 0) + tmpNPCType->aggroradius = 70; - MakeAnyLenString(&at_query, - "SELECT " - "red1h,grn1h,blu1h," - "red2c,grn2c,blu2c," - "red3a,grn3a,blu3a," - "red4b,grn4b,blu4b," - "red5g,grn5g,blu5g," - "red6l,grn6l,blu6l," - "red7f,grn7f,blu7f," - "red8x,grn8x,blu8x," - "red9x,grn9x,blu9x " - "FROM npc_types_tint WHERE id=%d", armor_tint_id); + tmpNPCType->assistradius = (int32)atoi(row[49]); + if (tmpNPCType->assistradius <= 0) + tmpNPCType->assistradius = tmpNPCType->aggroradius; - if (RunQuery(at_query, strlen(at_query), at_errbuf, &at_result)) - { - if ((at_row = mysql_fetch_row(at_result))) - { - for (i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = atoi(at_row[i * 3]) << 16; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 1]) << 8; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 2]); - tmpNPCType->armor_tint[i] |= (tmpNPCType->armor_tint[i]) ? (0xFF << 24) : 0; - } - } - else - { - armor_tint_id = 0; - } - } - else - { - armor_tint_id = 0; - } + if (row[50] && strlen(row[50])) + tmpNPCType->bodytype = (uint8)atoi(row[50]); + else + tmpNPCType->bodytype = 0; - if (at_result) - { - mysql_free_result(at_result); - } + tmpNPCType->npc_faction_id = atoi(row[51]); - safe_delete_array(at_query); - } - else - { - armor_tint_id = 0; - } - } + tmpNPCType->luclinface = atoi(row[52]); + tmpNPCType->hairstyle = atoi(row[53]); + tmpNPCType->haircolor = atoi(row[54]); + tmpNPCType->eyecolor1 = atoi(row[55]); + tmpNPCType->eyecolor2 = atoi(row[56]); + tmpNPCType->beardcolor = atoi(row[57]); + tmpNPCType->beard = atoi(row[58]); + tmpNPCType->drakkin_heritage = atoi(row[59]); + tmpNPCType->drakkin_tattoo = atoi(row[60]); + tmpNPCType->drakkin_details = atoi(row[61]); - if (armor_tint_id == 0) - { - for (i = MaterialChest; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = tmpNPCType->armor_tint[0]; - } - } + uint32 armor_tint_id = atoi(row[62]); - tmpNPCType->see_invis = atoi(row[r++]); - tmpNPCType->see_invis_undead = atoi(row[r++])==0?false:true; // Set see_invis_undead flag - if (row[r] != nullptr) - strn0cpy(tmpNPCType->lastname, row[r], 32); - r++; + tmpNPCType->armor_tint[0] = (atoi(row[63]) & 0xFF) << 16; + tmpNPCType->armor_tint[0] |= (atoi(row[64]) & 0xFF) << 8; + tmpNPCType->armor_tint[0] |= (atoi(row[65]) & 0xFF); + tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - tmpNPCType->qglobal = atoi(row[r++])==0?false:true; // qglobal - tmpNPCType->AC = atoi(row[r++]); - tmpNPCType->npc_aggro = atoi(row[r++])==0?false:true; - tmpNPCType->spawn_limit = atoi(row[r++]); - tmpNPCType->see_hide = atoi(row[r++])==0?false:true; - tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true; - tmpNPCType->ATK = atoi(row[r++]); - tmpNPCType->accuracy_rating = atoi(row[r++]); - tmpNPCType->avoidance_rating = atoi(row[r++]); - tmpNPCType->slow_mitigation = atoi(row[r++]); - tmpNPCType->maxlevel = atoi(row[r++]); - tmpNPCType->scalerate = atoi(row[r++]); - tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->unique_spawn_by_name = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->underwater = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->emoteid = atoi(row[r++]); - tmpNPCType->spellscale = atoi(row[r++]); - tmpNPCType->healscale = atoi(row[r++]); - tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false; - tmpNPCType->raid_target = atoi(row[r++]) == 0 ? false : true; - - // If NPC with duplicate NPC id already in table, - // free item we attempted to add. - if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) - { - std::cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << std::endl; - delete tmpNPCType; - npc = nullptr; - } else { - zone->npctable[tmpNPCType->npc_id]=tmpNPCType; - npc = tmpNPCType; - } + if (armor_tint_id == 0) + for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++) + tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0]; + else if (tmpNPCType->armor_tint[0] == 0) + { + std::string armortint_query = StringFormat("SELECT red1h, grn1h, blu1h, " + "red2c, grn2c, blu2c, " + "red3a, grn3a, blu3a, " + "red4b, grn4b, blu4b, " + "red5g, grn5g, blu5g, " + "red6l, grn6l, blu6l, " + "red7f, grn7f, blu7f, " + "red8x, grn8x, blu8x, " + "red9x, grn9x, blu9x " + "FROM npc_types_tint WHERE id = %d", + armor_tint_id); + auto armortint_results = QueryDatabase(armortint_query); + if (!armortint_results.Success() || armortint_results.RowCount() == 0) + armor_tint_id = 0; + else { + auto armorTint_row = armortint_results.begin(); -// Sleep(0); - } + for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) { + tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]); + tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0; + } + } + } else + armor_tint_id = 0; - if (result) { - mysql_free_result(result); - } - } else - std::cerr << "Error loading NPCs from database. Bad query: " << errbuf << std::endl; - safe_delete_array(query); + tmpNPCType->see_invis = atoi(row[66]); + tmpNPCType->see_invis_undead = atoi(row[67]) == 0? false: true; // Set see_invis_undead flag + if (row[68] != nullptr) + strn0cpy(tmpNPCType->lastname, row[68], 32); + + tmpNPCType->qglobal = atoi(row[69]) == 0? false: true; // qglobal + tmpNPCType->AC = atoi(row[70]); + tmpNPCType->npc_aggro = atoi(row[71]) == 0? false: true; + tmpNPCType->spawn_limit = atoi(row[72]); + tmpNPCType->see_hide = atoi(row[73]) == 0? false: true; + tmpNPCType->see_improved_hide = atoi(row[74]) == 0? false: true; + tmpNPCType->ATK = atoi(row[75]); + tmpNPCType->accuracy_rating = atoi(row[76]); + tmpNPCType->avoidance_rating = atoi(row[77]); + tmpNPCType->slow_mitigation = atoi(row[78]); + tmpNPCType->maxlevel = atoi(row[79]); + tmpNPCType->scalerate = atoi(row[80]); + tmpNPCType->private_corpse = atoi(row[81]) == 1 ? true: false; + tmpNPCType->unique_spawn_by_name = atoi(row[82]) == 1 ? true: false; + tmpNPCType->underwater = atoi(row[83]) == 1 ? true: false; + tmpNPCType->emoteid = atoi(row[84]); + tmpNPCType->spellscale = atoi(row[85]); + tmpNPCType->healscale = atoi(row[86]); + tmpNPCType->no_target_hotkey = atoi(row[87]) == 1 ? true: false; + tmpNPCType->raid_target = atoi(row[88]) == 0 ? false: true; + tmpNPCType->attack_delay = atoi(row[89]); + + // If NPC with duplicate NPC id already in table, + // free item we attempted to add. + if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end()) { + std::cerr << "Error loading duplicate NPC " << tmpNPCType->npc_id << std::endl; + delete tmpNPCType; + return nullptr; + } + + zone->npctable[tmpNPCType->npc_id]=tmpNPCType; + npc = tmpNPCType; + } return npc; } - const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 clientlevel) { - const NPCType *npc=nullptr; - std::map::iterator itr; //need to save based on merc_npc_type & client level uint32 merc_type_id = id * 100 + clientlevel; // If NPC is already in tree, return it. - if((itr = zone->merctable.find(merc_type_id)) != zone->merctable.end()) + auto itr = zone->merctable.find(merc_type_id); + if(itr != zone->merctable.end()) return itr->second; + //If the NPC type is 0, return nullptr. (sanity check) if(id == 0) - return nullptr; + return nullptr; // Otherwise, get NPCs from database. - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + // If id is 0, load all npc_types for the current zone, + // according to spawn2. + std::string query = StringFormat("SELECT vwMercNpcTypes.merc_npc_type_id, vwMercNpcTypes.name, " + "vwMercNpcTypes.level, vwMercNpcTypes.race_id, vwMercNpcTypes.class_id, " + "vwMercNpcTypes.hp, vwMercNpcTypes.mana, vwMercNpcTypes.gender, " + "vwMercNpcTypes.texture, vwMercNpcTypes.helmtexture, vwMercNpcTypes.attack_speed, " + "vwMercNpcTypes.STR, vwMercNpcTypes.STA, vwMercNpcTypes.DEX, vwMercNpcTypes.AGI, " + "vwMercNpcTypes._INT, vwMercNpcTypes.WIS, vwMercNpcTypes.CHA, vwMercNpcTypes.MR, " + "vwMercNpcTypes.CR, vwMercNpcTypes.DR, vwMercNpcTypes.FR, vwMercNpcTypes.PR, " + "vwMercNpcTypes.Corrup, vwMercNpcTypes.mindmg, vwMercNpcTypes.maxdmg, " + "vwMercNpcTypes.attack_count, vwMercNpcTypes.special_abilities, " + "vwMercNpcTypes.d_meele_texture1, vwMercNpcTypes.d_meele_texture2, " + "vwMercNpcTypes.prim_melee_type, vwMercNpcTypes.sec_melee_type, " + "vwMercNpcTypes.runspeed, vwMercNpcTypes.hp_regen_rate, vwMercNpcTypes.mana_regen_rate, " + "vwMercNpcTypes.bodytype, vwMercNpcTypes.armortint_id, " + "vwMercNpcTypes.armortint_red, vwMercNpcTypes.armortint_green, vwMercNpcTypes.armortint_blue, " + "vwMercNpcTypes.AC, vwMercNpcTypes.ATK, vwMercNpcTypes.Accuracy, vwMercNpcTypes.spellscale, " + "vwMercNpcTypes.healscale FROM vwMercNpcTypes " + "WHERE merc_npc_type_id = %d AND clientlevel = %d AND race_id = %d", + id, clientlevel, raceid); //dual primary keys. one is ID, one is level. + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error loading NPCs from database. Bad query: " << results.ErrorMessage() << std::endl; + return nullptr; + } + const NPCType *npc; - // If id is 0, load all npc_types for the current zone, - // according to spawn2. - const char *basic_query = "SELECT " - "vwMercNpcTypes.merc_npc_type_id," - "vwMercNpcTypes.name," - //"vwMercNpcTypes.clientlevel," - "vwMercNpcTypes.level," - "vwMercNpcTypes.race_id," - "vwMercNpcTypes.class_id," - "vwMercNpcTypes.hp," - "vwMercNpcTypes.mana," - "vwMercNpcTypes.gender," - "vwMercNpcTypes.texture," - "vwMercNpcTypes.helmtexture," - //"vwMercNpcTypes.size," - // "vwMercNpcTypes.loottable_id," - // "vwMercNpcTypes.merchant_id," - // "vwMercNpcTypes.alt_currency_id," - // "vwMercNpcTypes.adventure_template_id," - // "vwMercNpcTypes.trap_template," - "vwMercNpcTypes.attack_speed," - "vwMercNpcTypes.STR," - "vwMercNpcTypes.STA," - "vwMercNpcTypes.DEX," - "vwMercNpcTypes.AGI," - "vwMercNpcTypes._INT," - "vwMercNpcTypes.WIS," - "vwMercNpcTypes.CHA," - "vwMercNpcTypes.MR," - "vwMercNpcTypes.CR," - "vwMercNpcTypes.DR," - "vwMercNpcTypes.FR," - "vwMercNpcTypes.PR," - "vwMercNpcTypes.Corrup," - "vwMercNpcTypes.mindmg," - "vwMercNpcTypes.maxdmg," - "vwMercNpcTypes.attack_count," - "vwMercNpcTypes.special_abilities," - // "vwMercNpcTypes.npc_spells_id," - "vwMercNpcTypes.d_meele_texture1," - "vwMercNpcTypes.d_meele_texture2," - "vwMercNpcTypes.prim_melee_type," - "vwMercNpcTypes.sec_melee_type," - "vwMercNpcTypes.runspeed," - // "vwMercNpcTypes.findable," - // "vwMercNpcTypes.trackable," - "vwMercNpcTypes.hp_regen_rate," - "vwMercNpcTypes.mana_regen_rate," - // "vwMercNpcTypes.aggroradius," - "vwMercNpcTypes.bodytype," - // "vwMercNpcTypes.npc_faction_id," - //"vwMercNpcTypes.face," - //"vwMercNpcTypes.luclin_hairstyle," - //"vwMercNpcTypes.luclin_haircolor," - //"vwMercNpcTypes.luclin_eyecolor," - //"vwMercNpcTypes.luclin_eyecolor2," - //"vwMercNpcTypes.luclin_beardcolor," - //"vwMercNpcTypes.luclin_beard," - //"vwMercNpcTypes.drakkin_heritage," - //"vwMercNpcTypes.drakkin_tattoo," - //"vwMercNpcTypes.drakkin_details," - "vwMercNpcTypes.armortint_id," - "vwMercNpcTypes.armortint_red," - "vwMercNpcTypes.armortint_green," - "vwMercNpcTypes.armortint_blue," - // "vwMercNpcTypes.see_invis," - // "vwMercNpcTypes.see_invis_undead," - // "vwMercNpcTypes.lastname," - // "vwMercNpcTypes.qglobal," - "vwMercNpcTypes.AC," - // "vwMercNpcTypes.npc_aggro," - // "vwMercNpcTypes.spawn_limit," - // "vwMercNpcTypes.see_hide," - // "vwMercNpcTypes.see_improved_hide," - "vwMercNpcTypes.ATK," - "vwMercNpcTypes.Accuracy," - "vwMercNpcTypes.spellscale," - "vwMercNpcTypes.healscale"; - // "vwMercNpcTypes.slow_mitigation," - // "vwMercNpcTypes.maxlevel," - // "vwMercNpcTypes.scalerate," - // "vwMercNpcTypes.private_corpse," - // "vwMercNpcTypes.unique_spawn_by_name," - // "vwMercNpcTypes.underwater," - // "vwMercNpcTypes.emoteid"; + // Process each row returned. + for (auto row = results.begin(); row != results.end(); ++row) { + NPCType *tmpNPCType; + tmpNPCType = new NPCType; + memset (tmpNPCType, 0, sizeof *tmpNPCType); - MakeAnyLenString(&query, "%s FROM vwMercNpcTypes WHERE merc_npc_type_id=%d AND clientlevel=%d AND race_id = %d", basic_query, id, clientlevel, raceid); //dual primary keys. one is ID, one is level. + tmpNPCType->npc_id = atoi(row[0]); - if (RunQuery(query, strlen(query), errbuf, &result)) { - // Process each row returned. - while((row = mysql_fetch_row(result))) { - NPCType *tmpNPCType; - tmpNPCType = new NPCType; - memset (tmpNPCType, 0, sizeof *tmpNPCType); + strn0cpy(tmpNPCType->name, row[1], 50); - int r = 0; - tmpNPCType->npc_id = atoi(row[r++]); + tmpNPCType->level = atoi(row[2]); + tmpNPCType->race = atoi(row[3]); + tmpNPCType->class_ = atoi(row[4]); + tmpNPCType->max_hp = atoi(row[5]); + tmpNPCType->cur_hp = tmpNPCType->max_hp; + tmpNPCType->Mana = atoi(row[6]); + tmpNPCType->gender = atoi(row[7]); + tmpNPCType->texture = atoi(row[8]); + tmpNPCType->helmtexture = atoi(row[9]); + tmpNPCType->attack_speed = atof(row[10]); + tmpNPCType->STR = atoi(row[11]); + tmpNPCType->STA = atoi(row[12]); + tmpNPCType->DEX = atoi(row[13]); + tmpNPCType->AGI = atoi(row[14]); + tmpNPCType->INT = atoi(row[15]); + tmpNPCType->WIS = atoi(row[16]); + tmpNPCType->CHA = atoi(row[17]); + tmpNPCType->MR = atoi(row[18]); + tmpNPCType->CR = atoi(row[19]); + tmpNPCType->DR = atoi(row[20]); + tmpNPCType->FR = atoi(row[21]); + tmpNPCType->PR = atoi(row[22]); + tmpNPCType->Corrup = atoi(row[23]); + tmpNPCType->min_dmg = atoi(row[24]); + tmpNPCType->max_dmg = atoi(row[25]); + tmpNPCType->attack_count = atoi(row[26]); - strn0cpy(tmpNPCType->name, row[r++], 50); + if (row[27] != nullptr) + strn0cpy(tmpNPCType->special_abilities, row[27], 512); + else + tmpNPCType->special_abilities[0] = '\0'; - tmpNPCType->level = atoi(row[r++]); - tmpNPCType->race = atoi(row[r++]); - tmpNPCType->class_ = atoi(row[r++]); - tmpNPCType->max_hp = atoi(row[r++]); - tmpNPCType->cur_hp = tmpNPCType->max_hp; - tmpNPCType->Mana = atoi(row[r++]); - tmpNPCType->gender = atoi(row[r++]); - tmpNPCType->texture = atoi(row[r++]); - tmpNPCType->helmtexture = atoi(row[r++]); - //tmpNPCType->size = atof(row[r++]); - //tmpNPCType->loottable_id = atoi(row[r++]); - //tmpNPCType->merchanttype = atoi(row[r++]); - //tmpNPCType->alt_currency_type = atoi(row[r++]); - //tmpNPCType->adventure_template = atoi(row[r++]); - //tmpNPCType->trap_template = atoi(row[r++]); - tmpNPCType->attack_speed = atof(row[r++]); - tmpNPCType->STR = atoi(row[r++]); - tmpNPCType->STA = atoi(row[r++]); - tmpNPCType->DEX = atoi(row[r++]); - tmpNPCType->AGI = atoi(row[r++]); - tmpNPCType->INT = atoi(row[r++]); - tmpNPCType->WIS = atoi(row[r++]); - tmpNPCType->CHA = atoi(row[r++]); - tmpNPCType->MR = atoi(row[r++]); - tmpNPCType->CR = atoi(row[r++]); - tmpNPCType->DR = atoi(row[r++]); - tmpNPCType->FR = atoi(row[r++]); - tmpNPCType->PR = atoi(row[r++]); - tmpNPCType->Corrup = atoi(row[r++]); - tmpNPCType->min_dmg = atoi(row[r++]); - tmpNPCType->max_dmg = atoi(row[r++]); - tmpNPCType->attack_count = atoi(row[r++]); - strn0cpy(tmpNPCType->special_abilities, row[r++], 512); - //tmpNPCType->npc_spells_id = atoi(row[r++]); - tmpNPCType->d_meele_texture1 = atoi(row[r++]); - tmpNPCType->d_meele_texture2 = atoi(row[r++]); - tmpNPCType->prim_melee_type = atoi(row[r++]); - tmpNPCType->sec_melee_type = atoi(row[r++]); - tmpNPCType->runspeed= atof(row[r++]); - //tmpNPCType->findable = atoi(row[r++]) == 0? false : true; - //tmpNPCType->trackable = atoi(row[r++]) == 0? false : true; - tmpNPCType->hp_regen = atoi(row[r++]); - tmpNPCType->mana_regen = atoi(row[r++]); + tmpNPCType->d_meele_texture1 = atoi(row[28]); + tmpNPCType->d_meele_texture2 = atoi(row[29]); + tmpNPCType->prim_melee_type = atoi(row[30]); + tmpNPCType->sec_melee_type = atoi(row[31]); + tmpNPCType->runspeed= atof(row[32]); - //tmpNPCType->aggroradius = (int32)atoi(row[r++]); - tmpNPCType->aggroradius = RuleI(Mercs, AggroRadius); - // set defaultvalue for aggroradius - //if (tmpNPCType->aggroradius <= 0) - // tmpNPCType->aggroradius = 70; + tmpNPCType->hp_regen = atoi(row[33]); + tmpNPCType->mana_regen = atoi(row[34]); - if (row[r] && strlen(row[r])) - tmpNPCType->bodytype = (uint8)atoi(row[r]); - else - tmpNPCType->bodytype = 1; - r++; + tmpNPCType->aggroradius = RuleI(Mercs, AggroRadius); - //tmpNPCType->npc_faction_id = atoi(row[r++]); + if (row[35] && strlen(row[35])) + tmpNPCType->bodytype = (uint8)atoi(row[35]); + else + tmpNPCType->bodytype = 1; - //tmpNPCType->luclinface = atoi(row[r++]); - //tmpNPCType->hairstyle = atoi(row[r++]); - //tmpNPCType->haircolor = atoi(row[r++]); - //tmpNPCType->eyecolor1 = atoi(row[r++]); - //tmpNPCType->eyecolor2 = atoi(row[r++]); - //tmpNPCType->beardcolor = atoi(row[r++]); - //tmpNPCType->beard = atoi(row[r++]); - //tmpNPCType->drakkin_heritage = atoi(row[r++]); - //tmpNPCType->drakkin_tattoo = atoi(row[r++]); - //tmpNPCType->drakkin_details = atoi(row[r++]); - uint32 armor_tint_id = atoi(row[r++]); - tmpNPCType->armor_tint[0] = (atoi(row[r++]) & 0xFF) << 16; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8; - tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF); - tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; + uint32 armor_tint_id = atoi(row[36]); + tmpNPCType->armor_tint[0] = (atoi(row[37]) & 0xFF) << 16; + tmpNPCType->armor_tint[0] |= (atoi(row[38]) & 0xFF) << 8; + tmpNPCType->armor_tint[0] |= (atoi(row[39]) & 0xFF); + tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0; - int i; - if (armor_tint_id > 0) - { - if (tmpNPCType->armor_tint[0] == 0) - { - char at_errbuf[MYSQL_ERRMSG_SIZE]; - char *at_query = nullptr; - MYSQL_RES *at_result = nullptr; - MYSQL_ROW at_row; + if (armor_tint_id == 0) + for (int index = MaterialChest; index <= EmuConstants::MATERIAL_END; index++) + tmpNPCType->armor_tint[index] = tmpNPCType->armor_tint[0]; + else if (tmpNPCType->armor_tint[0] == 0) { + std::string armorTint_query = StringFormat("SELECT red1h, grn1h, blu1h, " + "red2c, grn2c, blu2c, " + "red3a, grn3a, blu3a, " + "red4b, grn4b, blu4b, " + "red5g, grn5g, blu5g, " + "red6l, grn6l, blu6l, " + "red7f, grn7f, blu7f, " + "red8x, grn8x, blu8x, " + "red9x, grn9x, blu9x " + "FROM npc_types_tint WHERE id = %d", + armor_tint_id); + auto armorTint_results = QueryDatabase(armorTint_query); + if (!results.Success() || results.RowCount() == 0) + armor_tint_id = 0; + else { + auto armorTint_row = results.begin(); - MakeAnyLenString(&at_query, - "SELECT " - "red1h,grn1h,blu1h," - "red2c,grn2c,blu2c," - "red3a,grn3a,blu3a," - "red4b,grn4b,blu4b," - "red5g,grn5g,blu5g," - "red6l,grn6l,blu6l," - "red7f,grn7f,blu7f," - "red8x,grn8x,blu8x," - "red9x,grn9x,blu9x " - "FROM npc_types_tint WHERE id=%d", armor_tint_id); + for (int index = EmuConstants::MATERIAL_BEGIN; index <= EmuConstants::MATERIAL_END; index++) { + tmpNPCType->armor_tint[index] = atoi(armorTint_row[index * 3]) << 16; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 1]) << 8; + tmpNPCType->armor_tint[index] |= atoi(armorTint_row[index * 3 + 2]); + tmpNPCType->armor_tint[index] |= (tmpNPCType->armor_tint[index]) ? (0xFF << 24) : 0; + } + } + } else + armor_tint_id = 0; - if (RunQuery(at_query, strlen(at_query), at_errbuf, &at_result)) - { - if ((at_row = mysql_fetch_row(at_result))) - { - for (i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = atoi(at_row[i * 3]) << 16; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 1]) << 8; - tmpNPCType->armor_tint[i] |= atoi(at_row[i * 3 + 2]); - tmpNPCType->armor_tint[i] |= (tmpNPCType->armor_tint[i]) ? (0xFF << 24) : 0; - } - } - else - { - armor_tint_id = 0; - } - } - else - { - armor_tint_id = 0; - } + tmpNPCType->AC = atoi(row[40]); + tmpNPCType->ATK = atoi(row[41]); + tmpNPCType->accuracy_rating = atoi(row[42]); + tmpNPCType->scalerate = RuleI(Mercs, ScaleRate); + tmpNPCType->spellscale = atoi(row[43]); + tmpNPCType->healscale = atoi(row[4]); - if (at_result) - { - mysql_free_result(at_result); - } + // If NPC with duplicate NPC id already in table, + // free item we attempted to add. + if (zone->merctable.find(tmpNPCType->npc_id * 100 + clientlevel) != zone->merctable.end()) { + delete tmpNPCType; + return nullptr; + } - safe_delete_array(at_query); - } - else - { - armor_tint_id = 0; - } - } - - if (armor_tint_id == 0) - { - for (i = MaterialChest; i <= EmuConstants::MATERIAL_END; i++) - { - tmpNPCType->armor_tint[i] = tmpNPCType->armor_tint[0]; - } - } - - //tmpNPCType->see_invis = atoi(row[r++]); - //tmpNPCType->see_invis_undead = atoi(row[r++])==0?false:true; // Set see_invis_undead flag - //if (row[r] != nullptr) - // strn0cpy(tmpNPCType->lastname, row[r], 32); - //r++; - - //tmpNPCType->qglobal = atoi(row[r++])==0?false:true; // qglobal - tmpNPCType->AC = atoi(row[r++]); - //tmpNPCType->npc_aggro = atoi(row[r++])==0?false:true; - //tmpNPCType->spawn_limit = atoi(row[r++]); - //tmpNPCType->see_hide = atoi(row[r++])==0?false:true; - //tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true; - tmpNPCType->ATK = atoi(row[r++]); - tmpNPCType->accuracy_rating = atoi(row[r++]); - //tmpNPCType->slow_mitigation = atof(row[r++]); - //tmpNPCType->maxlevel = atoi(row[r++]); - tmpNPCType->scalerate = RuleI(Mercs, ScaleRate); - //tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false; - //tmpNPCType->unique_spawn_by_name = atoi(row[r++]) == 1 ? true : false; - //tmpNPCType->underwater = atoi(row[r++]) == 1 ? true : false; - //tmpNPCType->emoteid = atoi(row[r++]); - tmpNPCType->spellscale = atoi(row[r++]); - tmpNPCType->healscale = atoi(row[r++]); - - // If NPC with duplicate NPC id already in table, - // free item we attempted to add. - if (zone->merctable.find(tmpNPCType->npc_id * 100 + clientlevel) != zone->merctable.end()) - { - delete tmpNPCType; - npc = nullptr; - } else { - zone->merctable[tmpNPCType->npc_id * 100 + clientlevel]=tmpNPCType; - npc = tmpNPCType; - } - -// Sleep(0); - } - - if (result) { - mysql_free_result(result); - } - } else - std::cerr << "Error loading NPCs from database. Bad query: " << errbuf << std::endl; - safe_delete_array(query); + zone->merctable[tmpNPCType->npc_id * 100 + clientlevel]=tmpNPCType; + npc = tmpNPCType; + } return npc; } -bool ZoneDatabase::LoadMercInfo(Client *c) { - bool loaded = false; +bool ZoneDatabase::LoadMercInfo(Client *client) { - if(c->GetEPP().merc_name[0] != 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - //char name[64]; + std::string query = StringFormat("SELECT MercID, Slot, Name, TemplateID, SuspendedTime, " + "IsSuspended, TimerRemaining, Gender, MercSize, StanceID, HP, Mana, " + "Endurance, Face, LuclinHairStyle, LuclinHairColor, " + "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails " + "FROM mercs WHERE OwnerCharacterID = '%i' ORDER BY Slot", client->CharacterID()); + auto results = QueryDatabase(query); + if (!results.Success()) + return false; + + if(results.RowCount() == 0) + return false; - //CleanMobName(c->GetEPP().merc_name, name); + for (auto row = results.begin(); row != results.end(); ++row) { + uint8 slot = atoi(row[1]); - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT MercID, Slot, Name, TemplateID, SuspendedTime, IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails FROM mercs WHERE OwnerCharacterID = '%i' ORDER BY Slot", c->CharacterID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - uint8 slot = atoi(DataRow[1]); + if(slot >= MAXMERCS) + continue; - if(slot >= MAXMERCS) { - continue; - } + client->GetMercInfo(slot).mercid = atoi(row[0]); + client->GetMercInfo(slot).slot = slot; + snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[2]); + client->GetMercInfo(slot).MercTemplateID = atoi(row[3]); + client->GetMercInfo(slot).SuspendedTime = atoi(row[4]); + client->GetMercInfo(slot).IsSuspended = atoi(row[5]) == 1 ? true : false; + client->GetMercInfo(slot).MercTimerRemaining = atoi(row[6]); + client->GetMercInfo(slot).Gender = atoi(row[7]); + client->GetMercInfo(slot).MercSize = atof(row[8]); + client->GetMercInfo(slot).State = 5; + client->GetMercInfo(slot).Stance = atoi(row[9]); + client->GetMercInfo(slot).hp = atoi(row[10]); + client->GetMercInfo(slot).mana = atoi(row[11]); + client->GetMercInfo(slot).endurance = atoi(row[12]); + client->GetMercInfo(slot).face = atoi(row[13]); + client->GetMercInfo(slot).luclinHairStyle = atoi(row[14]); + client->GetMercInfo(slot).luclinHairColor = atoi(row[15]); + client->GetMercInfo(slot).luclinEyeColor = atoi(row[16]); + client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[17]); + client->GetMercInfo(slot).luclinBeardColor = atoi(row[18]); + client->GetMercInfo(slot).luclinBeard = atoi(row[19]); + client->GetMercInfo(slot).drakkinHeritage = atoi(row[20]); + client->GetMercInfo(slot).drakkinTattoo = atoi(row[21]); + client->GetMercInfo(slot).drakkinDetails = atoi(row[22]); + } - c->GetMercInfo(slot).mercid = atoi(DataRow[0]); - c->GetMercInfo(slot).slot = slot; - snprintf(c->GetMercInfo(slot).merc_name, 64, "%s", std::string(DataRow[2]).c_str()); - c->GetMercInfo(slot).MercTemplateID = atoi(DataRow[3]); - c->GetMercInfo(slot).SuspendedTime = atoi(DataRow[4]); - c->GetMercInfo(slot).IsSuspended = atoi(DataRow[5]) == 1 ? true : false; - c->GetMercInfo(slot).MercTimerRemaining = atoi(DataRow[6]); - c->GetMercInfo(slot).Gender = atoi(DataRow[7]); - c->GetMercInfo(slot).State = 5; - c->GetMercInfo(slot).Stance = atoi(DataRow[8]); - c->GetMercInfo(slot).hp = atoi(DataRow[9]); - c->GetMercInfo(slot).mana = atoi(DataRow[10]); - c->GetMercInfo(slot).endurance = atoi(DataRow[11]); - c->GetMercInfo(slot).face = atoi(DataRow[12]); - c->GetMercInfo(slot).luclinHairStyle = atoi(DataRow[13]); - c->GetMercInfo(slot).luclinHairColor = atoi(DataRow[14]); - c->GetMercInfo(slot).luclinEyeColor = atoi(DataRow[15]); - c->GetMercInfo(slot).luclinEyeColor2 = atoi(DataRow[16]); - c->GetMercInfo(slot).luclinBeardColor = atoi(DataRow[17]); - c->GetMercInfo(slot).luclinBeard = atoi(DataRow[18]); - c->GetMercInfo(slot).drakkinHeritage = atoi(DataRow[19]); - c->GetMercInfo(slot).drakkinTattoo = atoi(DataRow[20]); - c->GetMercInfo(slot).drakkinDetails = atoi(DataRow[21]); - loaded = true; - } - - mysql_free_result(DatasetResult); - } - - safe_delete_array(Query); - } - - return loaded; + return true; } -bool ZoneDatabase::LoadCurrentMerc(Client *c) { - bool loaded = false; +bool ZoneDatabase::LoadCurrentMerc(Client *client) { - if(c->GetEPP().merc_name[0] != 0) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - //char name[64]; + uint8 slot = client->GetMercSlot(); - uint8 slot = c->GetMercSlot(); + if(slot > MAXMERCS) + return false; - if(slot > MAXMERCS) { - return false; - } + std::string query = StringFormat("SELECT MercID, Name, TemplateID, SuspendedTime, " + "IsSuspended, TimerRemaining, Gender, MercSize, StanceID, HP, " + "Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, " + "LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, " + "LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails " + "FROM mercs WHERE OwnerCharacterID = '%i' AND Slot = '%u'", + client->CharacterID(), slot); + auto results = database.QueryDatabase(query); - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT MercID, Name, TemplateID, SuspendedTime, IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails FROM mercs WHERE OwnerCharacterID = '%i' AND Slot = '%u'", c->CharacterID(), slot), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - while(DataRow = mysql_fetch_row(DatasetResult)) { - c->GetMercInfo(slot).mercid = atoi(DataRow[0]); - c->GetMercInfo(slot).slot = slot; - snprintf(c->GetMercInfo(slot).merc_name, 64, "%s", std::string(DataRow[1]).c_str()); - c->GetMercInfo(slot).MercTemplateID = atoi(DataRow[2]); - c->GetMercInfo(slot).SuspendedTime = atoi(DataRow[3]); - c->GetMercInfo(slot).IsSuspended = atoi(DataRow[4]) == 1 ? true : false; - c->GetMercInfo(slot).MercTimerRemaining = atoi(DataRow[5]); - c->GetMercInfo(slot).Gender = atoi(DataRow[6]); - c->GetMercInfo(slot).State = atoi(DataRow[7]); - c->GetMercInfo(slot).hp = atoi(DataRow[8]); - c->GetMercInfo(slot).mana = atoi(DataRow[9]); - c->GetMercInfo(slot).endurance = atoi(DataRow[10]); - c->GetMercInfo(slot).face = atoi(DataRow[11]); - c->GetMercInfo(slot).luclinHairStyle = atoi(DataRow[12]); - c->GetMercInfo(slot).luclinHairColor = atoi(DataRow[13]); - c->GetMercInfo(slot).luclinEyeColor = atoi(DataRow[14]); - c->GetMercInfo(slot).luclinEyeColor2 = atoi(DataRow[15]); - c->GetMercInfo(slot).luclinBeardColor = atoi(DataRow[16]); - c->GetMercInfo(slot).luclinBeard = atoi(DataRow[17]); - c->GetMercInfo(slot).drakkinHeritage = atoi(DataRow[18]); - c->GetMercInfo(slot).drakkinTattoo = atoi(DataRow[19]); - c->GetMercInfo(slot).drakkinDetails = atoi(DataRow[20]); - loaded = true; - } + if(!results.Success()) + return false; + + if(results.RowCount() == 0) + return false; - mysql_free_result(DatasetResult); - } - safe_delete_array(Query); + for (auto row = results.begin(); row != results.end(); ++row) { + client->GetMercInfo(slot).mercid = atoi(row[0]); + client->GetMercInfo(slot).slot = slot; + snprintf(client->GetMercInfo(slot).merc_name, 64, "%s", row[1]); + client->GetMercInfo(slot).MercTemplateID = atoi(row[2]); + client->GetMercInfo(slot).SuspendedTime = atoi(row[3]); + client->GetMercInfo(slot).IsSuspended = atoi(row[4]) == 1? true: false; + client->GetMercInfo(slot).MercTimerRemaining = atoi(row[5]); + client->GetMercInfo(slot).Gender = atoi(row[6]); + client->GetMercInfo(slot).MercSize = atof(row[7]); + client->GetMercInfo(slot).State = atoi(row[8]); + client->GetMercInfo(slot).hp = atoi(row[9]); + client->GetMercInfo(slot).mana = atoi(row[10]); + client->GetMercInfo(slot).endurance = atoi(row[11]); + client->GetMercInfo(slot).face = atoi(row[12]); + client->GetMercInfo(slot).luclinHairStyle = atoi(row[13]); + client->GetMercInfo(slot).luclinHairColor = atoi(row[14]); + client->GetMercInfo(slot).luclinEyeColor = atoi(row[15]); + client->GetMercInfo(slot).luclinEyeColor2 = atoi(row[16]); + client->GetMercInfo(slot).luclinBeardColor = atoi(row[17]); + client->GetMercInfo(slot).luclinBeard = atoi(row[18]); + client->GetMercInfo(slot).drakkinHeritage = atoi(row[19]); + client->GetMercInfo(slot).drakkinTattoo = atoi(row[20]); + client->GetMercInfo(slot).drakkinDetails = atoi(row[21]); } - return loaded; + return true; } bool ZoneDatabase::SaveMerc(Merc *merc) { Client *owner = merc->GetMercOwner(); - bool Result = false; - std::string errorMessage; - if(!owner) { + if(!owner) + return false; + + if(merc->GetMercID() == 0) + { + // New merc record + std::string query = StringFormat("INSERT INTO mercs " + "(OwnerCharacterID, Slot, Name, TemplateID, " + "SuspendedTime, IsSuspended, TimerRemaining, " + "Gender, MercSize, StanceID, HP, Mana, Endurance, Face, " + "LuclinHairStyle, LuclinHairColor, LuclinEyeColor, " + "LuclinEyeColor2, LuclinBeardColor, LuclinBeard, " + "DrakkinHeritage, DrakkinTattoo, DrakkinDetails) " + "VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', " + "'%u', '%u', '%f', '%u', '%u', '%u', '%i', '%i', '%i', " + "'%i', '%i', '%i', '%i', '%i', '%i', '%i')", + merc->GetMercCharacterID(), owner->GetNumMercs(), + merc->GetCleanName(), merc->GetMercTemplateID(), + owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), + owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), + merc->GetSize(), merc->GetStance(), merc->GetHP(), + merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), + merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), + merc->GetEyeColor2(), merc->GetBeardColor(), + merc->GetBeard(), merc->GetDrakkinHeritage(), + merc->GetDrakkinTattoo(), merc->GetDrakkinDetails()); + + auto results = database.QueryDatabase(query); + if(!results.Success()) { + owner->Message(13, results.ErrorMessage().c_str()); + return false; + } else if (results.RowsAffected() != 1) { + owner->Message(13, "Unable to save merc to the database."); + return false; + } + + merc->SetMercID(results.LastInsertedID()); + merc->UpdateMercInfo(owner); + database.SaveMercBuffs(merc); + return true; + } + + // Update existing merc record + std::string query = StringFormat("UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', " + "Name = '%s', TemplateID = '%u', SuspendedTime = '%u', " + "IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', MercSize = '%f', " + "StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', " + "Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', " + "LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', " + "LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', " + "DrakkinDetails = '%i' WHERE MercID = '%u'", + merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(), + merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, + merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, + merc->GetGender(), merc->GetSize(), merc->GetStance(), merc->GetHP(), + merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), + merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), + merc->GetEyeColor2(), merc->GetBeardColor(), merc->GetBeard(), + merc->GetDrakkinHeritage(), merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), + merc->GetMercID()); + + auto results = database.QueryDatabase(query); + if (!results.Success()) { + owner->Message(13, results.ErrorMessage().c_str()); + return false; + } else if (results.RowsAffected() != 1) { + owner->Message(13, "Unable to save merc to the database."); return false; } - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - uint32 affectedRows = 0; + merc->UpdateMercInfo(owner); + database.SaveMercBuffs(merc); - if(merc->GetMercID() == 0) { - // New merc record - uint32 TempNewMercID = 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO mercs (OwnerCharacterID, Slot, Name, TemplateID, SuspendedTime, IsSuspended, TimerRemaining, Gender, StanceID, HP, Mana, Endurance, Face, LuclinHairStyle, LuclinHairColor, LuclinEyeColor, LuclinEyeColor2, LuclinBeardColor, LuclinBeard, DrakkinHeritage, DrakkinTattoo, DrakkinDetails) VALUES('%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')", merc->GetMercCharacterID(), owner->GetNumMercs(), merc->GetCleanName(), merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(), merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(), merc->GetDrakkinTattoo(), merc->GetDrakkinDetails() ), TempErrorMessageBuffer, 0, &affectedRows, &TempNewMercID)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - merc->SetMercID(TempNewMercID); - Result = true; - } - } - else { - // Update existing merc record - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE mercs SET OwnerCharacterID = '%u', Slot = '%u', Name = '%s', TemplateID = '%u', SuspendedTime = '%u', IsSuspended = '%u', TimerRemaining = '%u', Gender = '%u', StanceID = '%u', HP = '%u', Mana = '%u', Endurance = '%u', Face = '%i', LuclinHairStyle = '%i', LuclinHairColor = '%i', LuclinEyeColor = '%i', LuclinEyeColor2 = '%i', LuclinBeardColor = '%i', LuclinBeard = '%i', DrakkinHeritage = '%i', DrakkinTattoo = '%i', DrakkinDetails = '%i' WHERE MercID = '%u'", merc->GetMercCharacterID(), owner->GetMercSlot(), merc->GetCleanName(), merc->GetMercTemplateID(), owner->GetMercInfo().SuspendedTime, merc->IsSuspended(), owner->GetMercInfo().MercTimerRemaining, merc->GetGender(), merc->GetStance(), merc->GetHP(), merc->GetMana(), merc->GetEndurance(), merc->GetLuclinFace(), merc->GetHairStyle(), merc->GetHairColor(), merc->GetEyeColor1(), merc->GetEyeColor2(), merc->GetBeardColor(), merc->GetBeard(), merc->GetDrakkinHeritage(), merc->GetDrakkinTattoo(), merc->GetDrakkinDetails(), merc->GetMercID()), TempErrorMessageBuffer, 0, &affectedRows)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - Result = true; - //time(&_startTotalPlayTime); - } - } - - safe_delete_array(Query); - - if(!errorMessage.empty() || (Result && affectedRows != 1)) { - if(owner && !errorMessage.empty()) - owner->Message(13, errorMessage.c_str()); - else if(owner) - owner->Message(13, std::string("Unable to save merc to the database.").c_str()); - - Result = false; - } - else { - merc->UpdateMercInfo(owner); - database.SaveMercBuffs(merc); - //database.SaveMercStance(this); - //database.SaveMercTimers(this); - } - - return Result; + return true; } void ZoneDatabase::SaveMercBuffs(Merc *merc) { + Buffs_Struct *buffs = merc->GetBuffs(); - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - int BuffCount = 0; - int InsertCount = 0; - uint32 buff_count = merc->GetMaxBuffSlots(); - while(BuffCount < BUFF_COUNT) { - if(buffs[BuffCount].spellid > 0 && buffs[BuffCount].spellid != SPELL_UNKNOWN) { - if(InsertCount == 0) { - // Remove any existing buff saves - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - } + // Remove any existing buff saves + std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error While Deleting Merc Buffs before save: %s", results.ErrorMessage().c_str()); + return; + } - int IsPersistent = 0; + for (int buffCount = 0; buffCount <= BUFF_COUNT; buffCount++) { + if(buffs[buffCount].spellid == 0 || buffs[buffCount].spellid == SPELL_UNKNOWN) + continue; - if(buffs[BuffCount].persistant_buff) - IsPersistent = 1; - else - IsPersistent = 0; + int IsPersistent = buffs[buffCount].persistant_buff? 1: 0; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, " - "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, " - "dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance) VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", - merc->GetMercID(), buffs[BuffCount].spellid, buffs[BuffCount].casterlevel, spells[buffs[BuffCount].spellid].buffdurationformula, - buffs[BuffCount].ticsremaining, - CalculatePoisonCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCurseCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0 ? buffs[BuffCount].counters : 0, - buffs[BuffCount].numhits, buffs[BuffCount].melee_rune, buffs[BuffCount].magic_rune, - buffs[BuffCount].dot_rune, - buffs[BuffCount].caston_x, - IsPersistent, - buffs[BuffCount].caston_y, - buffs[BuffCount].caston_z, - buffs[BuffCount].ExtraDIChance), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - safe_delete(Query); - Query = 0; - break; - } - else { - safe_delete(Query); - Query = 0; - InsertCount++; - } - } - - BuffCount++; - } - - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error Saving Merc Buffs: %s", errorMessage.c_str()); + query = StringFormat("INSERT INTO merc_buffs (MercId, SpellId, CasterLevel, DurationFormula, " + "TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, " + "CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, " + "caston_x, Persistent, caston_y, caston_z, ExtraDIChance) " + "VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %u, %i, %u, %i, %i, %i);", + merc->GetMercID(), buffs[buffCount].spellid, buffs[buffCount].casterlevel, + spells[buffs[buffCount].spellid].buffdurationformula, buffs[buffCount].ticsremaining, + CalculatePoisonCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, + CalculateDiseaseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, + CalculateCurseCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, + CalculateCorruptionCounters(buffs[buffCount].spellid) > 0 ? buffs[buffCount].counters : 0, + buffs[buffCount].numhits, buffs[buffCount].melee_rune, buffs[buffCount].magic_rune, + buffs[buffCount].dot_rune, buffs[buffCount].caston_x, IsPersistent, buffs[buffCount].caston_y, + buffs[buffCount].caston_z, buffs[buffCount].ExtraDIChance); + results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Saving Merc Buffs: %s", results.ErrorMessage().c_str()); + break; + } } } void ZoneDatabase::LoadMercBuffs(Merc *merc) { Buffs_Struct *buffs = merc->GetBuffs(); uint32 max_slots = merc->GetMaxBuffSlots(); - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; + bool BuffsLoaded = false; - - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, caston_y, caston_z, ExtraDIChance FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int BuffCount = 0; - - while(DataRow = mysql_fetch_row(DatasetResult)) { - if(BuffCount == BUFF_COUNT) - break; - - buffs[BuffCount].spellid = atoi(DataRow[0]); - buffs[BuffCount].casterlevel = atoi(DataRow[1]); - buffs[BuffCount].ticsremaining = atoi(DataRow[3]); - - if(CalculatePoisonCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[4]); - } else if(CalculateDiseaseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[5]); - } else if(CalculateCurseCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[6]); - } else if(CalculateCorruptionCounters(buffs[BuffCount].spellid) > 0) { - buffs[BuffCount].counters = atoi(DataRow[7]); - } - buffs[BuffCount].numhits = atoi(DataRow[8]); - buffs[BuffCount].melee_rune = atoi(DataRow[9]); - buffs[BuffCount].magic_rune = atoi(DataRow[10]); - buffs[BuffCount].dot_rune = atoi(DataRow[11]); - buffs[BuffCount].caston_x = atoi(DataRow[12]); - buffs[BuffCount].casterid = 0; - - bool IsPersistent = false; - - if(atoi(DataRow[13])) - IsPersistent = true; - - buffs[BuffCount].caston_y = atoi(DataRow[13]); - buffs[BuffCount].caston_z = atoi(DataRow[14]); - buffs[BuffCount].ExtraDIChance = atoi(DataRow[15]); - - buffs[BuffCount].persistant_buff = IsPersistent; - - BuffCount++; - } - - mysql_free_result(DatasetResult); - - BuffsLoaded = true; + std::string query = StringFormat("SELECT SpellId, CasterLevel, DurationFormula, TicsRemaining, " + "PoisonCounters, DiseaseCounters, CurseCounters, CorruptionCounters, " + "HitCount, MeleeRune, MagicRune, dot_rune, caston_x, Persistent, " + "caston_y, caston_z, ExtraDIChance FROM merc_buffs WHERE MercId = %u", + merc->GetMercID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); + return; } - safe_delete_array(Query); + int buffCount = 0; + for (auto row = results.begin(); row != results.end(); ++row, ++buffCount) { + if(buffCount == BUFF_COUNT) + break; - if(errorMessage.empty() && BuffsLoaded) { - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - safe_delete_array(Query); - } + buffs[buffCount].spellid = atoi(row[0]); + buffs[buffCount].casterlevel = atoi(row[1]); + buffs[buffCount].ticsremaining = atoi(row[3]); + + if(CalculatePoisonCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[4]); + + if(CalculateDiseaseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[5]); + + if(CalculateCurseCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[6]); + + if(CalculateCorruptionCounters(buffs[buffCount].spellid) > 0) + buffs[buffCount].counters = atoi(row[7]); + + buffs[buffCount].numhits = atoi(row[8]); + buffs[buffCount].melee_rune = atoi(row[9]); + buffs[buffCount].magic_rune = atoi(row[10]); + buffs[buffCount].dot_rune = atoi(row[11]); + buffs[buffCount].caston_x = atoi(row[12]); + buffs[buffCount].casterid = 0; + + bool IsPersistent = atoi(row[13])? true: false; + + buffs[buffCount].caston_y = atoi(row[13]); + buffs[buffCount].caston_z = atoi(row[14]); + buffs[buffCount].ExtraDIChance = atoi(row[15]); + + buffs[buffCount].persistant_buff = IsPersistent; + + } + + query = StringFormat("DELETE FROM merc_buffs WHERE MercId = %u", merc->GetMercID()); + results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", results.ErrorMessage().c_str()); - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error Loading Merc Buffs: %s", errorMessage.c_str()); - } } bool ZoneDatabase::DeleteMerc(uint32 merc_id) { - std::string errorMessage; - bool Result = false; - int TempCounter = 0; - if(merc_id > 0) { - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; + if(merc_id == 0) + return false; - // TODO: These queries need to be ran together as a transaction.. ie, if one or more fail then they all will fail to commit to the database. - - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM merc_buffs WHERE MercID = '%u'", merc_id), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; - - safe_delete_array(Query); - - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "DELETE FROM mercs WHERE MercID = '%u'", merc_id), TempErrorMessageBuffer)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else - TempCounter++; - - safe_delete_array(Query); - - if(TempCounter == 2) - Result = true; + // 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. + // ...Not all mercs will have buffs, so why is it required that both deletes succeed? + std::string query = StringFormat("DELETE FROM merc_buffs WHERE MercId = '%u'", merc_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error Deleting Merc Buffs: %s", results.ErrorMessage().c_str()); } - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", errorMessage.c_str()); + query = StringFormat("DELETE FROM mercs WHERE MercID = '%u'", merc_id); + results = database.QueryDatabase(query); + if(!results.Success()) + { + LogFile->write(EQEMuLog::Error, "Error Deleting Merc: %s", results.ErrorMessage().c_str()); + return false; } - return Result; + return true; } void ZoneDatabase::LoadMercEquipment(Merc *merc) { - std::string errorMessage; - char* Query = 0; - char TempErrorMessageBuffer[MYSQL_ERRMSG_SIZE]; - MYSQL_RES* DatasetResult; - MYSQL_ROW DataRow; - if(!database.RunQuery(Query, MakeAnyLenString(&Query, "SELECT item_id FROM merc_inventory WHERE merc_subtype_id = (SELECT merc_subtype_id FROM merc_subtypes WHERE class_id = '%u' AND tier_id = '%u') AND min_level <= %u AND max_level >= %u", merc->GetClass(), merc->GetTierID(), merc->GetLevel(), merc->GetLevel()), TempErrorMessageBuffer, &DatasetResult)) { - errorMessage = std::string(TempErrorMessageBuffer); - } - else { - int itemCount = 0; - - while(DataRow = mysql_fetch_row(DatasetResult)) { - if (itemCount == EmuConstants::EQUIPMENT_SIZE) - break; - - if(atoi(DataRow[0]) > 0) { - merc->AddItem(itemCount, atoi(DataRow[0])); - - itemCount++; - } - } - - mysql_free_result(DatasetResult); + std::string query = StringFormat("SELECT item_id FROM merc_inventory " + "WHERE merc_subtype_id = (" + "SELECT merc_subtype_id FROM merc_subtypes " + "WHERE class_id = '%u' AND tier_id = '%u') " + "AND min_level <= %u AND max_level >= %u", + merc->GetClass(), merc->GetTierID(), + merc->GetLevel(), merc->GetLevel()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error Loading Merc Inventory: %s", results.ErrorMessage().c_str()); + return; } - safe_delete_array(Query); - Query = 0; + int itemCount = 0; + for(auto row = results.begin(); row != results.end(); ++row) { + if (itemCount == EmuConstants::EQUIPMENT_SIZE) + break; - if(!errorMessage.empty()) { - LogFile->write(EQEMuLog::Error, "Error Loading Merc Inventory: %s", errorMessage.c_str()); - } + if(atoi(row[0]) == 0) + continue; + + merc->AddItem(itemCount, atoi(row[0])); + itemCount++; + } } uint8 ZoneDatabase::GetGridType(uint32 grid, uint32 zoneid ) { - char *query = 0; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - int type = 0; - if (RunQuery(query, MakeAnyLenString(&query,"SELECT type from grid where id = %i and zoneid = %i",grid,zoneid),errbuf,&result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - type = atoi( row[0] ); - } - mysql_free_result(result); - } else { - std::cerr << "Error in GetGridType query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + + std::string query = StringFormat("SELECT type FROM grid WHERE id = %i AND zoneid = %i", grid, zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetGridType query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; } - return type; + + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } - - void ZoneDatabase::SaveMerchantTemp(uint32 npcid, uint32 slot, uint32 item, uint32 charges){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "replace into merchantlist_temp (npcid,slot,itemid,charges) values(%d,%d,%d,%d)", npcid, slot, item, charges), errbuf)) { - std::cerr << "Error in SaveMerchantTemp query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("REPLACE INTO merchantlist_temp (npcid, slot, itemid, charges) " + "VALUES(%d, %d, %d, %d)", npcid, slot, item, charges); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in SaveMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; } + void ZoneDatabase::DeleteMerchantTemp(uint32 npcid, uint32 slot){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "delete from merchantlist_temp where npcid=%d and slot=%d", npcid, slot), errbuf)) { - std::cerr << "Error in DeleteMerchantTemp query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM merchantlist_temp WHERE npcid=%d AND slot=%d", npcid, slot); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in DeleteMerchantTemp query '" << query << "' " << results.ErrorMessage() << std::endl; + } - bool ZoneDatabase::UpdateZoneSafeCoords(const char* zonename, float x=0, float y=0, float z=0) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE zone SET safe_x='%f', safe_y='%f', safe_z='%f' WHERE short_name='%s';", x, y, z, zonename), errbuf, 0, &affected_rows)) { - safe_delete_array(query); + std::string query = StringFormat("UPDATE zone SET safe_x='%f', safe_y='%f', safe_z='%f' " + "WHERE short_name='%s';", x, y, z, zonename); + auto results = QueryDatabase(query); + if (!results.Success() || results.RowsAffected() == 0) return false; - } - safe_delete_array(query); - - if (affected_rows == 0) - { - return false; - } return true; } - uint8 ZoneDatabase::GetUseCFGSafeCoords() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT value FROM variables WHERE varname='UseCFGSafeCoords'"), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - - uint8 usecoords = atoi(row[0]); - mysql_free_result(result); - return usecoords; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - - std::cerr << "Error in GetUseCFGSafeCoords query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return 0; - -} - - -uint32 ZoneDatabase::GetServerFilters(char* name, ServerSideFilters_Struct *ssfs) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - - MYSQL_ROW row; - - - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT serverfilters FROM account WHERE name='%s'", name), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(ServerSideFilters_Struct)) { - memcpy(ssfs, row[0], sizeof(ServerSideFilters_Struct)); - } - else { - std::cerr << "Player profile length mismatch in ServerSideFilters" << std::endl; - mysql_free_result(result); - return 0; - } - } - else { - mysql_free_result(result); - return 0; - - } - uint32 len = lengths[0]; - mysql_free_result(result); - return len; - } - else { - std::cerr << "Error in ServerSideFilters query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + const std::string query = "SELECT value FROM variables WHERE varname='UseCFGSafeCoords'"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetUseCFGSafeCoords query '" << query << "' " << results.ErrorMessage() << std::endl; return 0; } - return 0; + if (results.RowCount() != 1) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } -bool ZoneDatabase::SetServerFilters(char* name, ServerSideFilters_Struct *ssfs) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char query[256+sizeof(ServerSideFilters_Struct)*2+1]; - char* end = query; - - //if (strlen(name) > 15) - // return false; - - /*for (int i=0; i 'z') && - (name[i] < 'A' || name[i] > 'Z') && - (name[i] < '0' || name[i] > '9')) - return 0; -}*/ - - - end += sprintf(end, "UPDATE account SET serverfilters="); - *end++ = '\''; - end += DoEscapeString(end, (char*)ssfs, sizeof(ServerSideFilters_Struct)); - *end++ = '\''; - end += sprintf(end," WHERE name='%s'", name); - - uint32 affected_rows = 0; - if (!RunQuery(query, (uint32) (end - query), errbuf, 0, &affected_rows)) { - std::cerr << "Error in SetServerSideFilters query " << errbuf << std::endl; - return false; - } - - if (affected_rows == 0) { - return false; - } - - return true; -} - - //New functions for timezone uint32 ZoneDatabase::GetZoneTZ(uint32 zoneid, uint32 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT timezone FROM zone WHERE zoneidnumber=%i AND (version=%i OR version=0) ORDER BY version DESC", zoneid, version), errbuf, &result)) - { - safe_delete_array(query); - if (mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - uint32 tmp = atoi(row[0]); - mysql_free_result(result); - return tmp; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetZoneTZ query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return 0; + std::string query = StringFormat("SELECT timezone FROM zone WHERE zoneidnumber = %i " + "AND (version = %i OR version = 0) ORDER BY version DESC", + zoneid, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl; + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return atoi(row[0]); } bool ZoneDatabase::SetZoneTZ(uint32 zoneid, uint32 version, uint32 tz) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE zone SET timezone=%i WHERE zoneidnumber=%i AND version=%i", tz, zoneid, version), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - - if (affected_rows == 1) - return true; - else - return false; - } - else { - std::cerr << "Error in SetZoneTZ query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("UPDATE zone SET timezone = %i " + "WHERE zoneidnumber = %i AND version = %i", + tz, zoneid, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetZoneTZ query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } + } - return false; -} -//End new timezone functions. - -/* - solar: this is never actually called, client_process starts an async query - instead and uses GetAccountInfoForLogin_result to process it.. - */ -bool ZoneDatabase::GetAccountInfoForLogin(uint32 account_id, int16* admin, char* account_name, uint32* lsaccountid, uint8* gmspeed, bool* revoked,bool* gmhideme) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT status, name, lsaccount_id, gmspeed, revoked, hideme FROM account WHERE id=%i", account_id), errbuf, &result)) { - safe_delete_array(query); - bool ret = GetAccountInfoForLogin_result(result, admin, account_name, lsaccountid, gmspeed, revoked,gmhideme); - mysql_free_result(result); - return ret; - } - else - { - std::cerr << "Error in GetAccountInfoForLogin query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return false; + return results.RowsAffected() == 1; } -void ZoneDatabase::RefreshGroupFromDB(Client *c){ - if(!c){ +void ZoneDatabase::RefreshGroupFromDB(Client *client){ + if(!client) return; - } - Group *g = c->GetGroup(); + Group *group = client->GetGroup(); - if(!g){ + if(!group) return; - } EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate2_Struct)); GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer; gu->action = groupActUpdate; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - strcpy(gu->yourname, c->GetName()); - GetGroupLeadershipInfo(g->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); - gu->NPCMarkerID = g->GetNPCMarkerID(); + strcpy(gu->yourname, client->GetName()); + GetGroupLeadershipInfo(group->GetID(), gu->leadersname, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, &gu->leader_aas); + gu->NPCMarkerID = group->GetNPCMarkerID(); int index = 0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT name from group_id where groupid=%d", g->GetID()), errbuf, &result)) { - while((row = mysql_fetch_row(result))){ - if(index < 6){ - if(strcmp(c->GetName(), row[0]) != 0){ - strcpy(gu->membername[index], row[0]); - index++; - } - } - } - mysql_free_result(result); + + std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %d", group->GetID()); + auto results = QueryDatabase(query); + if (!results.Success()) + { + printf("Error in group update query: %s\n", results.ErrorMessage().c_str()); } else { - printf("Error in group update query: %s\n", errbuf); - } - safe_delete_array(query); + for (auto row = results.begin(); row != results.end(); ++row) { + if(index >= 6) + continue; - c->QueuePacket(outapp); + if(strcmp(client->GetName(), row[0]) == 0) + continue; + + strcpy(gu->membername[index], row[0]); + index++; + } + } + + client->QueuePacket(outapp); safe_delete(outapp); - if(c->GetClientVersion() >= EQClientSoD) { - g->NotifyMainTank(c, 1); - g->NotifyPuller(c, 1); + if(client->GetClientVersion() >= EQClientSoD) { + group->NotifyMainTank(client, 1); + group->NotifyPuller(client, 1); } - g->NotifyMainAssist(c, 1); - - g->NotifyMarkNPC(c); - g->NotifyAssistTarget(c); - g->NotifyTankTarget(c); - g->NotifyPullerTarget(c); - g->SendMarkedNPCsToMember(c); + group->NotifyMainAssist(client, 1); + group->NotifyMarkNPC(client); + group->NotifyAssistTarget(client); + group->NotifyTankTarget(client); + group->NotifyPullerTarget(client); + group->SendMarkedNPCsToMember(client); } -uint8 ZoneDatabase::GroupCount(uint32 groupid){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint8 count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(charid) FROM group_id WHERE groupid=%d", groupid), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr) - count = atoi(row[0]); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::GroupCount query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; +uint8 ZoneDatabase::GroupCount(uint32 groupid) { + + std::string query = StringFormat("SELECT count(charid) FROM group_id WHERE groupid = %d", groupid); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::GroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } - uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) - { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint8 count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(charid) FROM raid_members WHERE raidid=%d AND groupid=%d;", raidid, groupid), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr) - count = atoi(row[0]); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::RaidGroupCount query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; +uint8 ZoneDatabase::RaidGroupCount(uint32 raidid, uint32 groupid) { + + std::string query = StringFormat("SELECT count(charid) FROM raid_members " + "WHERE raidid = %d AND groupid = %d;", raidid, groupid); + auto results = QueryDatabase(query); + + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::RaidGroupCount query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return 0; + } + + if (results.RowCount() == 0) + return 0; + + auto row = results.begin(); + + return atoi(row[0]); } int32 ZoneDatabase::GetBlockedSpellsCount(uint32 zoneid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - sprintf(query, "SELECT count(*) FROM blocked_spells WHERE zoneid=%d", zoneid); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row != nullptr && row[0] != 0) { - int32 ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetBlockedSpellsCount query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT count(*) FROM blocked_spells WHERE zoneid = %d", zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in GetBlockedSpellsCount query '" << query << "' " << results.ErrorMessage() << std::endl; return -1; } - return -1; + if (results.RowCount() == 0) + return -1; + + auto row = results.begin(); + + return atoi(row[0]); } bool ZoneDatabase::LoadBlockedSpells(int32 blockedSpellsCount, ZoneSpellsBlocked* into, uint32 zoneid) { LogFile->write(EQEMuLog::Status, "Loading Blocked Spells from database..."); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - MakeAnyLenString(&query, "SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message " - "FROM blocked_spells WHERE zoneid=%d ORDER BY id asc", zoneid); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - int32 r; - for(r = 0; (row = mysql_fetch_row(result)); r++) { - if(r >= blockedSpellsCount) { - std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl; - break; - } - memset(&into[r], 0, sizeof(ZoneSpellsBlocked)); - if(row){ - into[r].spellid = atoi(row[1]); - into[r].type = atoi(row[2]); - into[r].x = atof(row[3]); - into[r].y = atof(row[4]); - into[r].z = atof(row[5]); - into[r].xdiff = atof(row[6]); - into[r].ydiff = atof(row[7]); - into[r].zdiff = atof(row[8]); - strn0cpy(into[r].message, row[9], 255); - } - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in LoadBlockedSpells query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("SELECT id, spellid, type, x, y, z, x_diff, y_diff, z_diff, message " + "FROM blocked_spells WHERE zoneid = %d ORDER BY id ASC", zoneid); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in LoadBlockedSpells query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } + } + + if (results.RowCount() == 0) + return true; + + int32 index = 0; + for(auto row = results.begin(); row != results.end(); ++row, ++index) { + if(index >= blockedSpellsCount) { + std::cerr << "Error, Blocked Spells Count of " << blockedSpellsCount << " exceeded." << std::endl; + break; + } + + memset(&into[index], 0, sizeof(ZoneSpellsBlocked)); + into[index].spellid = atoi(row[1]); + into[index].type = atoi(row[2]); + into[index].x = atof(row[3]); + into[index].y = atof(row[4]); + into[index].z = atof(row[5]); + into[index].xdiff = atof(row[6]); + into[index].ydiff = atof(row[7]); + into[index].zdiff = atof(row[8]); + strn0cpy(into[index].message, row[9], 255); + } + return true; } int ZoneDatabase::getZoneShutDownDelay(uint32 zoneID, uint32 version) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT shutdowndelay FROM zone " + "WHERE zoneidnumber = %i AND (version=%i OR version=0) " + "ORDER BY version DESC", zoneID, version); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in getZoneShutDownDelay query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; + return (RuleI(Zone, AutoShutdownDelay)); + } - if (RunQuery(query, MakeAnyLenString(&query, "SELECT shutdowndelay FROM zone WHERE zoneidnumber=%i AND (version=%i OR version=0) ORDER BY version DESC", zoneID, version), errbuf, &result)) - { - if (mysql_num_rows(result) > 0) { - row = mysql_fetch_row(result); - int retVal = atoi(row[0]); + if (results.RowCount() == 0) { + std::cerr << "Error in getZoneShutDownDelay no result '" << query << "' " << std::endl; + return (RuleI(Zone, AutoShutdownDelay)); + } - mysql_free_result(result); - safe_delete_array(query); - return (retVal); - } - else { - std::cerr << "Error in getZoneShutDownDelay no result '" << query << "' " << errbuf << std::endl; - mysql_free_result(result); - safe_delete_array(query); - return (RuleI(Zone, AutoShutdownDelay)); - } - } - else { - std::cerr << "Error in getZoneShutDownDelay query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } - return (RuleI(Zone, AutoShutdownDelay)); + auto row = results.begin(); + + return atoi(row[0]); } uint32 ZoneDatabase::GetKarma(uint32 acct_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 ret_val = 0; - - if (!RunQuery(query,MakeAnyLenString(&query, "select `karma` from `account` where `id`='%i' limit 1", - acct_id),errbuf,&result)) - { - safe_delete_array(query); + std::string query = StringFormat("SELECT `karma` FROM `account` WHERE `id` = '%i' LIMIT 1", acct_id); + auto results = QueryDatabase(query); + if (!results.Success()) return 0; - } - safe_delete_array(query); - row = mysql_fetch_row(result); + auto row = results.begin(); - ret_val = atoi(row[0]); - - mysql_free_result(result); - - return ret_val; + return atoi(row[0]); } void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; + std::string query = StringFormat("UPDATE account SET karma = %i WHERE id = %i", amount, acct_id); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in UpdateKarma query '" << query << "' " << results.ErrorMessage().c_str() << std::endl; - if (RunQuery(query, MakeAnyLenString(&query, "UPDATE account set karma=%i where id=%i", amount, acct_id), errbuf, 0, &affected_rows)){ - safe_delete_array(query);} - else { - std::cerr << "Error in UpdateKarma query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } } -void ZoneDatabase::ListAllInstances(Client* c, uint32 charid) +void ZoneDatabase::ListAllInstances(Client* client, uint32 charid) { - if(!c) + if(!client) return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; + std::string query = StringFormat("SELECT instance_list.id, zone, version " + "FROM instance_list JOIN instance_list_player " + "ON instance_list.id = instance_list_player.id " + "WHERE instance_list_player.charid = %lu", + (unsigned long)charid); + auto results = QueryDatabase(query); + if (!results.Success()) + return; + char name[64]; + database.GetCharName(charid, name); + client->Message(0, "%s is part of the following instances:", name); - if (RunQuery(query,MakeAnyLenString(&query, "SELECT instance_list.id, zone, version FROM instance_list JOIN" - " instance_list_player ON instance_list.id = instance_list_player.id" - " WHERE instance_list_player.charid=%lu", (unsigned long)charid),errbuf,&result)) - { - safe_delete_array(query); - - char name[64]; - database.GetCharName(charid, name); - c->Message(0, "%s is part of the following instances:", name); - while(row = mysql_fetch_row(result)) - { - c->Message(0, "%s - id: %lu, version: %lu", database.GetZoneName(atoi(row[1])), + for (auto row = results.begin(); row != results.end(); ++row) { + client->Message(0, "%s - id: %lu, version: %lu", database.GetZoneName(atoi(row[1])), (unsigned long)atoi(row[0]), (unsigned long)atoi(row[2])); - } - - mysql_free_result(result); - } - else - { - safe_delete_array(query); - } + } } void ZoneDatabase::QGlobalPurge() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM quest_globals WHERE expdate < UNIX_TIMESTAMP()"), - errbuf); - safe_delete_array(query); + const std::string query = "DELETE FROM quest_globals WHERE expdate < UNIX_TIMESTAMP()"; + database.QueryDatabase(query); } void ZoneDatabase::InsertDoor(uint32 ddoordbid, uint16 ddoorid, const char* ddoor_name, float dxpos, float dypos, float dzpos, float dheading, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - if (!RunQuery(query, MakeAnyLenString(&query, "replace into doors (id, doorid,zone,version,name,pos_x,pos_y,pos_z,heading,opentype,guild,lockpick,keyitem,door_param,invert_state,incline,size) values('%i','%i','%s','%i', '%s','%f','%f','%f','%f','%i','%i','%i', '%i','%i','%i','%i','%i')", ddoordbid ,ddoorid ,zone->GetShortName(), zone->GetInstanceVersion(), ddoor_name, dxpos, dypos, dzpos, dheading, dopentype, dguildid, dlockpick, dkeyitem, ddoor_param, dinvert, dincline, dsize), errbuf)) { - std::cerr << "Error in InsertDoor" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); + + std::string query = StringFormat("REPLACE INTO doors (id, doorid, zone, version, name, " + "pos_x, pos_y, pos_z, heading, opentype, guild, lockpick, " + "keyitem, door_param, invert_state, incline, size) " + "VALUES('%i', '%i', '%s', '%i', '%s', '%f', '%f', " + "'%f', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')", + ddoordbid, ddoorid, zone->GetShortName(), zone->GetInstanceVersion(), + ddoor_name, dxpos, dypos, dzpos, dheading, dopentype, dguildid, + dlockpick, dkeyitem, ddoor_param, dinvert, dincline, dsize); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in InsertDoor" << query << "' " << results.ErrorMessage() << std::endl; } void ZoneDatabase::LoadAltCurrencyValues(uint32 char_id, std::map ¤cy) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT currency_id, amount FROM character_alt_currency where char_id='%u'", char_id), errbuf, &result)) { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - currency[atoi(row[0])] = atoi(row[1]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadAltCurrencyValues query '%s': %s", query, errbuf); - safe_delete_array(query); - } + std::string query = StringFormat("SELECT currency_id, amount " + "FROM character_alt_currency " + "WHERE char_id = '%u'", char_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadAltCurrencyValues query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) + currency[atoi(row[0])] = atoi(row[1]); + } void ZoneDatabase::UpdateAltCurrencyValue(uint32 char_id, uint32 currency_id, uint32 value) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - database.RunQuery(query, MakeAnyLenString(&query, "REPLACE INTO character_alt_currency (char_id, currency_id, amount)" - " VALUES('%u', '%u', '%u')", char_id, currency_id, value), - errbuf); - safe_delete_array(query); + + std::string query = StringFormat("REPLACE INTO character_alt_currency (char_id, currency_id, amount) " + "VALUES('%u', '%u', '%u')", char_id, currency_id, value); + database.QueryDatabase(query); + } -void ZoneDatabase::SaveBuffs(Client *c) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; +void ZoneDatabase::SaveBuffs(Client *client) { - database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_buffs` WHERE `character_id`='%u'", c->CharacterID()), - errbuf); + std::string query = StringFormat("DELETE FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID()); + database.QueryDatabase(query); + + uint32 buff_count = client->GetMaxBuffSlots(); + Buffs_Struct *buffs = client->GetBuffs(); + + for (int index = 0; index < buff_count; index++) { + if(buffs[index].spellid == SPELL_UNKNOWN) + continue; + + query = StringFormat("INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " + "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, " + "magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance) " + "VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', " + "'%i', '%i', '%i', '%i')", client->CharacterID(), index, buffs[index].spellid, + buffs[index].casterlevel, buffs[index].caster_name, buffs[index].ticsremaining, + buffs[index].counters, buffs[index].numhits, buffs[index].melee_rune, + buffs[index].magic_rune, buffs[index].persistant_buff, buffs[index].dot_rune, + buffs[index].caston_x, buffs[index].caston_y, buffs[index].caston_z, + buffs[index].ExtraDIChance); + auto results = QueryDatabase(query); + if (!results.Success()) + LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); - uint32 buff_count = c->GetMaxBuffSlots(); - Buffs_Struct *buffs = c->GetBuffs(); - for (int i = 0; i < buff_count; i++) { - if(buffs[i].spellid != SPELL_UNKNOWN) { - if(!database.RunQuery(query, MakeAnyLenString(&query, "INSERT INTO `character_buffs` (character_id, slot_id, spell_id, " - "caster_level, caster_name, ticsremaining, counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " - "caston_x, caston_y, caston_z, ExtraDIChance) VALUES('%u', '%u', '%u', '%u', '%s', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%i', '%i', '%i', '%i')", - c->CharacterID(), i, buffs[i].spellid, buffs[i].casterlevel, buffs[i].caster_name, buffs[i].ticsremaining, - buffs[i].counters, buffs[i].numhits, buffs[i].melee_rune, buffs[i].magic_rune, buffs[i].persistant_buff, - buffs[i].dot_rune, buffs[i].caston_x, buffs[i].caston_y, buffs[i].caston_z, buffs[i].ExtraDIChance), - errbuf)) { - LogFile->write(EQEMuLog::Error, "Error in SaveBuffs query '%s': %s", query, errbuf); - } - } } - safe_delete_array(query); } -void ZoneDatabase::LoadBuffs(Client *c) { - Buffs_Struct *buffs = c->GetBuffs(); - uint32 max_slots = c->GetMaxBuffSlots(); - for(int i = 0; i < max_slots; ++i) { - buffs[i].spellid = SPELL_UNKNOWN; - } +void ZoneDatabase::LoadBuffs(Client *client) { + Buffs_Struct *buffs = client->GetBuffs(); + uint32 max_slots = client->GetMaxBuffSlots(); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, counters, " - "numhits, melee_rune, magic_rune, persistent, dot_rune, caston_x, caston_y, caston_z, ExtraDIChance FROM `character_buffs` WHERE " - "`character_id`='%u'", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - uint32 slot_id = atoul(row[1]); - if(slot_id >= c->GetMaxBuffSlots()) { - continue; - } + for(int index = 0; index < max_slots; ++index) + buffs[index].spellid = SPELL_UNKNOWN; - uint32 spell_id = atoul(row[0]); - if(!IsValidSpell(spell_id)) { - continue; - } - - Client *caster = entity_list.GetClientByName(row[3]); - uint32 caster_level = atoi(row[2]); - uint32 ticsremaining = atoul(row[4]); - uint32 counters = atoul(row[5]); - uint32 numhits = atoul(row[6]); - uint32 melee_rune = atoul(row[7]); - uint32 magic_rune = atoul(row[8]); - uint8 persistent = atoul(row[9]); - uint32 dot_rune = atoul(row[10]); - int32 caston_x = atoul(row[11]); - int32 caston_y = atoul(row[12]); - int32 caston_z = atoul(row[13]); - int32 ExtraDIChance = atoul(row[14]); - - buffs[slot_id].spellid = spell_id; - buffs[slot_id].casterlevel = caster_level; - if(caster) { - buffs[slot_id].casterid = caster->GetID(); - strcpy(buffs[slot_id].caster_name, caster->GetName()); - buffs[slot_id].client = true; - } else { - buffs[slot_id].casterid = 0; - strcpy(buffs[slot_id].caster_name, ""); - buffs[slot_id].client = false; - } - - buffs[slot_id].ticsremaining = ticsremaining; - buffs[slot_id].counters = counters; - buffs[slot_id].numhits = numhits; - buffs[slot_id].melee_rune = melee_rune; - buffs[slot_id].magic_rune = magic_rune; - buffs[slot_id].persistant_buff = persistent ? true : false; - buffs[slot_id].dot_rune = dot_rune; - buffs[slot_id].caston_x = caston_x; - buffs[slot_id].caston_y = caston_y; - buffs[slot_id].caston_z = caston_z; - buffs[slot_id].ExtraDIChance = ExtraDIChance; - buffs[slot_id].RootBreakChance = 0; - buffs[slot_id].UpdateClient = false; - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadBuffs query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT spell_id, slot_id, caster_level, caster_name, ticsremaining, " + "counters, numhits, melee_rune, magic_rune, persistent, dot_rune, " + "caston_x, caston_y, caston_z, ExtraDIChance " + "FROM `character_buffs` WHERE `character_id` = '%u'", client->CharacterID()); + auto results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadBuffs query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; - } + } - max_slots = c->GetMaxBuffSlots(); - for(int i = 0; i < max_slots; ++i) { - if(!IsValidSpell(buffs[i].spellid)) { + for (auto row = results.begin(); row != results.end(); ++row) { + uint32 slot_id = atoul(row[1]); + if(slot_id >= client->GetMaxBuffSlots()) continue; - } - for(int j = 0; j < 12; ++j) { - bool cont = false; - switch(spells[buffs[i].spellid].effectid[j]) { - case SE_Charm: - buffs[i].spellid = SPELL_UNKNOWN; - cont = true; - break; - case SE_Illusion: - if(!buffs[i].persistant_buff) { - buffs[i].spellid = SPELL_UNKNOWN; - cont = true; - } - break; - } + uint32 spell_id = atoul(row[0]); + if(!IsValidSpell(spell_id)) + continue; - if(cont) { + Client *caster = entity_list.GetClientByName(row[3]); + uint32 caster_level = atoi(row[2]); + uint32 ticsremaining = atoul(row[4]); + uint32 counters = atoul(row[5]); + uint32 numhits = atoul(row[6]); + uint32 melee_rune = atoul(row[7]); + uint32 magic_rune = atoul(row[8]); + uint8 persistent = atoul(row[9]); + uint32 dot_rune = atoul(row[10]); + int32 caston_x = atoul(row[11]); + int32 caston_y = atoul(row[12]); + int32 caston_z = atoul(row[13]); + int32 ExtraDIChance = atoul(row[14]); + + buffs[slot_id].spellid = spell_id; + buffs[slot_id].casterlevel = caster_level; + + if(caster) { + buffs[slot_id].casterid = caster->GetID(); + strcpy(buffs[slot_id].caster_name, caster->GetName()); + buffs[slot_id].client = true; + } else { + buffs[slot_id].casterid = 0; + strcpy(buffs[slot_id].caster_name, ""); + buffs[slot_id].client = false; + } + + buffs[slot_id].ticsremaining = ticsremaining; + buffs[slot_id].counters = counters; + buffs[slot_id].numhits = numhits; + buffs[slot_id].melee_rune = melee_rune; + buffs[slot_id].magic_rune = magic_rune; + buffs[slot_id].persistant_buff = persistent? true: false; + buffs[slot_id].dot_rune = dot_rune; + buffs[slot_id].caston_x = caston_x; + buffs[slot_id].caston_y = caston_y; + buffs[slot_id].caston_z = caston_z; + buffs[slot_id].ExtraDIChance = ExtraDIChance; + buffs[slot_id].RootBreakChance = 0; + buffs[slot_id].UpdateClient = false; + + } + + max_slots = client->GetMaxBuffSlots(); + for(int index = 0; index < max_slots; ++index) { + if(!IsValidSpell(buffs[index].spellid)) + continue; + + for(int effectIndex = 0; effectIndex < 12; ++effectIndex) { + + if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) { + buffs[index].spellid = SPELL_UNKNOWN; + break; + } + + if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Illusion) { + if(buffs[index].persistant_buff) + break; + + buffs[index].spellid = SPELL_UNKNOWN; break; } } } } -void ZoneDatabase::SavePetInfo(Client *c) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - int i = 0; - PetInfo *petinfo = c->GetPetInfo(0); - PetInfo *suspended = c->GetPetInfo(1); +void ZoneDatabase::SavePetInfo(Client *client) +{ + PetInfo *petinfo = nullptr; - if(!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_pet_buffs` WHERE `char_id`=%u", c->CharacterID()), - errbuf)) { - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM `character_pet_buffs` WHERE `char_id` = %u", client->CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) return; - } - safe_delete_array(query); - if (!database.RunQuery(query, MakeAnyLenString(&query, "DELETE FROM `character_pet_inventory` WHERE `char_id`=%u", c->CharacterID()), - errbuf)) { - safe_delete_array(query); - // error report + + query = StringFormat("DELETE FROM `character_pet_inventory` WHERE `char_id` = %u", client->CharacterID()); + results = database.QueryDatabase(query); + if (!results.Success()) return; - } - safe_delete_array(query); - if(!database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_info` (`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " - "values (%u, 0, '%s', %i, %u, %u, %u, %f) " - "ON DUPLICATE KEY UPDATE `petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u, `size`=%f", - c->CharacterID(), petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size, - petinfo->Name, petinfo->petpower, petinfo->SpellID, petinfo->HP, petinfo->Mana, petinfo->size), - errbuf)) - { - safe_delete_array(query); - return; - } - safe_delete_array(query); + for (int pet = 0; pet < 2; pet++) { + petinfo = client->GetPetInfo(pet); + if (!petinfo) + continue; - for(i=0; i < RuleI(Spells, MaxTotalSlotsPET); i++) { - if (petinfo->Buffs[i].spellid != SPELL_UNKNOWN && petinfo->Buffs[i].spellid != 0) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_buffs` (`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " - "`ticsremaining`, `counters`) values " - "(%u, 0, %u, %u, %u, %u, %d)", - c->CharacterID(), i, petinfo->Buffs[i].spellid, petinfo->Buffs[i].level, petinfo->Buffs[i].duration, - petinfo->Buffs[i].counters), - errbuf); - safe_delete_array(query); + 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(); + + // 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); } - if (suspended->Buffs[i].spellid != SPELL_UNKNOWN && suspended->Buffs[i].spellid != 0) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_buffs` (`char_id`, `pet`, `slot`, `spell_id`, `caster_level`, " - "`ticsremaining`, `counters`) values " - "(%u, 1, %u, %u, %u, %u, %d)", - c->CharacterID(), i, suspended->Buffs[i].spellid, suspended->Buffs[i].level, suspended->Buffs[i].duration, - suspended->Buffs[i].counters), - errbuf); - safe_delete_array(query); + database.QueryDatabase(query); + query.clear(); + + // pet inventory! + for (int index = EmuConstants::EQUIPMENT_BEGIN; index <= EmuConstants::EQUIPMENT_END; index++) { + if (!petinfo->Items[index]) + continue; + + 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 (i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) { - if(petinfo->Items[i]) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_inventory` (`char_id`, `pet`, `slot`, `item_id`) values (%u, 0, %u, %u)", - c->CharacterID(), i, petinfo->Items[i]), errbuf); - // should check for errors - safe_delete_array(query); - } - } +void ZoneDatabase::RemoveTempFactions(Client *client) { - - if(!database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_info` (`char_id`, `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size`) " - "values (%u, 1, '%s', %u, %u, %u, %u, %f) " - "ON DUPLICATE KEY UPDATE `petname`='%s', `petpower`=%i, `spell_id`=%u, `hp`=%u, `mana`=%u, `size`=%f", - c->CharacterID(), suspended->Name, suspended->petpower, suspended->SpellID, suspended->HP, suspended->Mana, suspended->size, - suspended->Name, suspended->petpower, suspended->SpellID, suspended->HP, suspended->Mana, suspended->size), - errbuf)) - { - safe_delete_array(query); - return; - } - safe_delete_array(query); - - for (i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; i++) { - if(suspended->Items[i]) { - database.RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO `character_pet_inventory` (`char_id`, `pet`, `slot`, `item_id`) values (%u, 1, %u, %u)", - c->CharacterID(), i, suspended->Items[i]), errbuf); - // should check for errors - safe_delete_array(query); - } - } + std::string query = StringFormat("DELETE FROM faction_values " + "WHERE temp = 1 AND char_id = %u", + client->CharacterID()); + auto results = QueryDatabase(query); + if (!results.Success()) + std::cerr << "Error in RemoveTempFactions query '" << query << "' " << results.ErrorMessage() << std::endl; } -void ZoneDatabase::RemoveTempFactions(Client *c){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; +void ZoneDatabase::LoadPetInfo(Client *client) { - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM faction_values WHERE temp = 1 AND char_id=%u", c->CharacterID()), errbuf)) { - std::cerr << "Error in RemoveTempFactions query '" << query << "' " << errbuf << std::endl; - } - safe_delete_array(query); -} - -void ZoneDatabase::LoadPetInfo(Client *c) { // Load current pet and suspended pet - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - PetInfo *petinfo = c->GetPetInfo(0); - PetInfo *suspended = c->GetPetInfo(1); - PetInfo *pi; - uint16 pet; + PetInfo *petinfo = client->GetPetInfo(0); + PetInfo *suspended = client->GetPetInfo(1); memset(petinfo, 0, sizeof(PetInfo)); memset(suspended, 0, sizeof(PetInfo)); - if(database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `pet`, `petname`, `petpower`, `spell_id`, `hp`, `mana`, `size` from `character_pet_info` where `char_id`=%u", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) { - pet = atoi(row[0]); - if (pet == 0) - pi = petinfo; - else if (pet == 1) - pi = suspended; - else - continue; - - strncpy(pi->Name,row[1],64); - pi->petpower = atoi(row[2]); - pi->SpellID = atoi(row[3]); - pi->HP = atoul(row[4]); - pi->Mana = atoul(row[5]); - pi->size = atof(row[6]); - } - mysql_free_result(result); - } - else - { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); - safe_delete_array(query); + std::string query = StringFormat("SELECT `pet`, `petname`, `petpower`, `spell_id`, " + "`hp`, `mana`, `size` FROM `character_pet_info` " + "WHERE `char_id` = %u", client->CharacterID()); + auto results = database.QueryDatabase(query); + if(!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } + PetInfo *pi; + for (auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, " - "`ticsremaining`, `counters` FROM `character_pet_buffs` " - "WHERE `char_id`=%u", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while ((row = mysql_fetch_row(result))) - { - pet = atoi(row[0]); - if (pet == 0) - pi = petinfo; - else if (pet == 1) - pi = suspended; - else - continue; + if (pet == 0) + pi = petinfo; + else if (pet == 1) + pi = suspended; + else + continue; - uint32 slot_id = atoul(row[1]); - if(slot_id >= RuleI(Spells, MaxTotalSlotsPET)) { - continue; - } - - uint32 spell_id = atoul(row[2]); - if(!IsValidSpell(spell_id)) { - continue; - } - uint32 caster_level = atoi(row[3]); - int caster_id = 0; - // The castername field is currently unused - //Client *caster = entity_list.GetClientByName(row[4]); - //if (caster) { caster_id = caster->GetID(); } - uint32 ticsremaining = atoul(row[5]); - uint32 counters = atoul(row[6]); - - pi->Buffs[slot_id].spellid = spell_id; - pi->Buffs[slot_id].level = caster_level; - pi->Buffs[slot_id].player_id = caster_id; - pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs - - pi->Buffs[slot_id].duration = ticsremaining; - pi->Buffs[slot_id].counters = counters; - } - mysql_free_result(result); + strncpy(pi->Name,row[1],64); + pi->petpower = atoi(row[2]); + pi->SpellID = atoi(row[3]); + pi->HP = atoul(row[4]); + pi->Mana = atoul(row[5]); + pi->size = atof(row[6]); } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); - safe_delete_array(query); + + query = StringFormat("SELECT `pet`, `slot`, `spell_id`, `caster_level`, `castername`, " + "`ticsremaining`, `counters` FROM `character_pet_buffs` " + "WHERE `char_id` = %u", client->CharacterID()); + results = QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); + return; + } + + for (auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); + if (pet == 0) + pi = petinfo; + else if (pet == 1) + pi = suspended; + else + continue; + + uint32 slot_id = atoul(row[1]); + if(slot_id >= RuleI(Spells, MaxTotalSlotsPET)) + continue; + + uint32 spell_id = atoul(row[2]); + if(!IsValidSpell(spell_id)) + continue; + + uint32 caster_level = atoi(row[3]); + int caster_id = 0; + // The castername field is currently unused + uint32 ticsremaining = atoul(row[5]); + uint32 counters = atoul(row[6]); + + pi->Buffs[slot_id].spellid = spell_id; + pi->Buffs[slot_id].level = caster_level; + pi->Buffs[slot_id].player_id = caster_id; + pi->Buffs[slot_id].slotid = 2; // Always 2 in buffs struct for real buffs + + pi->Buffs[slot_id].duration = ticsremaining; + pi->Buffs[slot_id].counters = counters; + } + + query = StringFormat("SELECT `pet`, `slot`, `item_id` " + "FROM `character_pet_inventory` " + "WHERE `char_id`=%u",client->CharacterID()); + results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query.c_str(), results.ErrorMessage().c_str()); return; } - if (database.RunQuery(query, MakeAnyLenString(&query, - "SELECT `pet`, `slot`, `item_id` FROM `character_pet_inventory` WHERE `char_id`=%u", - c->CharacterID()), errbuf, &result)) - { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - pet = atoi(row[0]); - if (pet == 0) - pi = petinfo; - else if (pet == 1) - pi = suspended; - else - continue; - - int slot = atoi(row[1]); - if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END) - continue; + for(auto row = results.begin(); row != results.end(); ++row) { + uint16 pet = atoi(row[0]); + if (pet == 0) + pi = petinfo; + else if (pet == 1) + pi = suspended; + else + continue; + + int slot = atoi(row[1]); + if (slot < EmuConstants::EQUIPMENT_BEGIN || slot > EmuConstants::EQUIPMENT_END) + continue; + + pi->Items[slot] = atoul(row[2]); + } - pi->Items[slot] = atoul(row[2]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadPetInfo query '%s': %s", query, errbuf); - safe_delete_array(query); - return; - } } bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race_mod, uint32 deity_mod, int32 faction_id) { @@ -3002,33 +3150,8 @@ 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 +//| Name: GetFactionName; Dec. 16 //o-------------------------------------------------------------- //| Notes: Retrieves the name of the specified faction .Returns false on failure. //o-------------------------------------------------------------- @@ -3044,7 +3167,7 @@ bool ZoneDatabase::GetFactionName(int32 faction_id, char* name, uint32 buflen) { } //o-------------------------------------------------------------- -//| Name: GetNPCFactionList; rembrant, Dec. 16, 2001 +//| Name: GetNPCFactionList; Dec. 16, 2001 //o-------------------------------------------------------------- //| Purpose: Gets a list of faction_id's and values bound to the npc_id. Returns false on failure. //o-------------------------------------------------------------- @@ -3068,29 +3191,24 @@ bool ZoneDatabase::GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, in } //o-------------------------------------------------------------- -//| Name: SetCharacterFactionLevel; rembrant, Dec. 20, 2001 +//| Name: SetCharacterFactionLevel; Dec. 20, 2001 //o-------------------------------------------------------------- //| Purpose: Update characters faction level with specified faction_id to specified value. Returns false on failure. //o-------------------------------------------------------------- bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - if (!RunQuery(query, MakeAnyLenString(&query, - "DELETE FROM faction_values WHERE char_id=%i AND faction_id = %i", - char_id, faction_id), errbuf)) { - std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = StringFormat("DELETE FROM faction_values " + "WHERE char_id=%i AND faction_id = %i", + char_id, faction_id); + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << results.ErrorMessage() << std::endl; return false; - } + } if(value == 0) - { - safe_delete_array(query); return true; - } if(temp == 2) temp = 0; @@ -3098,89 +3216,62 @@ bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, in if(temp == 3) temp = 1; - if (!RunQuery(query, MakeAnyLenString(&query, - "INSERT INTO faction_values (char_id,faction_id,current_value,temp) VALUES (%i,%i,%i,%i)", - char_id, faction_id,value,temp), errbuf, 0, &affected_rows)) { - std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + query = StringFormat("INSERT INTO faction_values (char_id, faction_id, current_value, temp) " + "VALUES (%i, %i, %i, %i)", char_id, faction_id, value, temp); + results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in SetCharacterFactionLevel query '" << query << "' " << results.ErrorMessage() << std::endl; return false; } - safe_delete_array(query); - - if (affected_rows == 0) - { + if (results.RowsAffected() == 0) return false; - } val_list[faction_id] = value; - return(true); + return true; } bool ZoneDatabase::LoadFactionData() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - strcpy(query, "SELECT MAX(id) FROM faction_list"); - - - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - if (row && row[0]) - { - max_faction = atoi(row[0]); - faction_array = new Faction*[max_faction+1]; - for(unsigned int i=0; iname, row[1], 50); - faction_array[index]->base = atoi(row[2]); - - char sec_errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *sec_result; - MYSQL_ROW sec_row; - MakeAnyLenString(&query, "SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id=%u", index); - if (RunQuery(query, strlen(query), sec_errbuf, &sec_result)) { - while((sec_row = mysql_fetch_row(sec_result))) - { - faction_array[index]->mods[sec_row[1]] = atoi(sec_row[0]); - } - mysql_free_result(sec_result); - } - safe_delete_array(query); - } - mysql_free_result(result); - } - else { - std::cerr << "Error in LoadFactionData '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - } - else { - mysql_free_result(result); - } - } - else { - std::cerr << "Error in LoadFactionData '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); + std::string query = "SELECT MAX(id) FROM faction_list"; + auto results = QueryDatabase(query); + if (!results.Success()) { + std::cerr << "Error in LoadFactionData '" << query << "' " << results.ErrorMessage() << std::endl; return false; } + + if (results.RowCount() == 0) + return false; + + auto row = results.begin(); + + max_faction = atoi(row[0]); + faction_array = new Faction*[max_faction+1]; + for(unsigned int index=0; indexname, row[1], 50); + faction_array[index]->base = atoi(row[2]); + + query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index); + auto modResults = QueryDatabase(query); + if (!modResults.Success()) + continue; + + for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow) + faction_array[index]->mods[modRow[1]] = atoi(modRow[0]); + } + return true; } @@ -3226,3 +3317,645 @@ bool ZoneDatabase::GetFactionIdsForNPC(uint32 nfl_id, std::listlocked, + dbpc->exp, + dbpc->size, + dbpc->level, + dbpc->race, + dbpc->gender, + dbpc->class_, + dbpc->deity, + dbpc->texture, + dbpc->helmtexture, + dbpc->copper, + dbpc->silver, + dbpc->gold, + dbpc->plat, + dbpc->haircolor, + dbpc->beardcolor, + dbpc->eyecolor1, + dbpc->eyecolor2, + dbpc->hairstyle, + dbpc->face, + dbpc->beard, + dbpc->drakkin_heritage, + dbpc->drakkin_tattoo, + dbpc->drakkin_details, + dbpc->item_tint[0].color, + dbpc->item_tint[1].color, + dbpc->item_tint[2].color, + dbpc->item_tint[3].color, + dbpc->item_tint[4].color, + dbpc->item_tint[5].color, + dbpc->item_tint[6].color, + dbpc->item_tint[7].color, + dbpc->item_tint[8].color, + db_id + ); + auto results = QueryDatabase(query); + + return db_id; +} + +void ZoneDatabase::MarkCorpseAsRezzed(uint32 db_id) { + std::string query = StringFormat("UPDATE `character_corpses` SET `is_rezzed` = 1 WHERE `id` = %i", db_id); + auto results = QueryDatabase(query); +} + +uint32 ZoneDatabase::SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, float x, float y, float z, float heading) { + /* Dump Basic Corpse Data */ + std::string query = StringFormat("INSERT INTO `character_corpses` SET \n" + "`charname` = '%s',\n" + "`zone_id` = %u,\n" + "`instance_id` = %u,\n" + "`charid` = %d,\n" + "`x` = %1.1f,\n" + "`y` = %1.1f,\n" + "`z` = %1.1f,\n" + "`heading` = %1.1f,\n" + "`time_of_death` = NOW(),\n" + "`is_buried` = 0," + "`is_locked` = %d,\n" + "`exp` = %u,\n" + "`size` = %f,\n" + "`level` = %u,\n" + "`race` = %u,\n" + "`gender` = %u,\n" + "`class` = %u,\n" + "`deity` = %u,\n" + "`texture` = %u,\n" + "`helm_texture` = %u,\n" + "`copper` = %u,\n" + "`silver` = %u,\n" + "`gold` = %u,\n" + "`platinum` = %u,\n" + "`hair_color` = %u,\n" + "`beard_color` = %u,\n" + "`eye_color_1` = %u,\n" + "`eye_color_2` = %u,\n" + "`hair_style` = %u,\n" + "`face` = %u,\n" + "`beard` = %u,\n" + "`drakkin_heritage` = %u,\n" + "`drakkin_tattoo` = %u,\n" + "`drakkin_details` = %u,\n" + "`wc_1` = %u,\n" + "`wc_2` = %u,\n" + "`wc_3` = %u,\n" + "`wc_4` = %u,\n" + "`wc_5` = %u,\n" + "`wc_6` = %u,\n" + "`wc_7` = %u,\n" + "`wc_8` = %u,\n" + "`wc_9` = %u \n", + EscapeString(charname).c_str(), + zoneid, + instanceid, + charid, + x, + y, + z, + heading, + dbpc->locked, + dbpc->exp, + dbpc->size, + dbpc->level, + dbpc->race, + dbpc->gender, + dbpc->class_, + dbpc->deity, + dbpc->texture, + dbpc->helmtexture, + dbpc->copper, + dbpc->silver, + dbpc->gold, + dbpc->plat, + dbpc->haircolor, + dbpc->beardcolor, + dbpc->eyecolor1, + dbpc->eyecolor2, + dbpc->hairstyle, + dbpc->face, + dbpc->beard, + dbpc->drakkin_heritage, + dbpc->drakkin_tattoo, + dbpc->drakkin_details, + dbpc->item_tint[0].color, + dbpc->item_tint[1].color, + dbpc->item_tint[2].color, + dbpc->item_tint[3].color, + dbpc->item_tint[4].color, + dbpc->item_tint[5].color, + dbpc->item_tint[6].color, + dbpc->item_tint[7].color, + dbpc->item_tint[8].color + ); + auto results = QueryDatabase(query); + uint32 last_insert_id = results.LastInsertedID(); + + /* Dump Items from Inventory */ + uint8 first_entry = 0; + for (unsigned int i = 0; i < dbpc->itemcount; i++) { + if (first_entry != 1){ + query = StringFormat("REPLACE INTO `character_corpse_items` \n" + " (corpse_id, equip_slot, item_id, charges, aug_1, aug_2, aug_3, aug_4, aug_5, attuned) \n" + " VALUES (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", + last_insert_id, + dbpc->items[i].equip_slot, + dbpc->items[i].item_id, + dbpc->items[i].charges, + dbpc->items[i].aug_1, + dbpc->items[i].aug_2, + dbpc->items[i].aug_3, + dbpc->items[i].aug_4, + dbpc->items[i].aug_5 + ); + first_entry = 1; + } + else{ + query = query + StringFormat(", (%u, %u, %u, %u, %u, %u, %u, %u, %u, 0) \n", + last_insert_id, + dbpc->items[i].equip_slot, + dbpc->items[i].item_id, + dbpc->items[i].charges, + dbpc->items[i].aug_1, + dbpc->items[i].aug_2, + dbpc->items[i].aug_3, + dbpc->items[i].aug_4, + dbpc->items[i].aug_5 + ); + } + } + auto sc_results = QueryDatabase(query); + return last_insert_id; +} + +uint32 ZoneDatabase::GetCharacterBuriedCorpseCount(uint32 char_id) { + std::string query = StringFormat("SELECT COUNT(*) FROM `character_corpses` WHERE `charid` = '%u' AND `is_buried` = 1", char_id); + auto results = QueryDatabase(query); + + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } + return 0; +} + +uint32 ZoneDatabase::GetCharacterCorpseCount(uint32 char_id) { + std::string query = StringFormat("SELECT COUNT(*) FROM `character_corpses` WHERE `charid` = '%u'", char_id); + auto results = QueryDatabase(query); + + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } + return 0; +} + +uint32 ZoneDatabase::GetCharacterCorpseID(uint32 char_id, uint8 corpse) { + std::string query = StringFormat("SELECT `id` FROM `character_corpses` WHERE `charid` = '%u'", char_id); + auto results = QueryDatabase(query); + + for (auto row = results.begin(); row != results.end(); ++row) { + for (int i = 0; i < corpse; i++) { + return atoul(row[0]); + } + } + return 0; +} + +uint32 ZoneDatabase::GetCharacterCorpseItemCount(uint32 corpse_id){ + std::string query = StringFormat("SELECT COUNT(*) FROM character_corpse_items WHERE `corpse_id` = %u", + corpse_id + ); + auto results = QueryDatabase(query); + auto row = results.begin(); + if (results.Success() && results.RowsAffected() != 0){ + return atoi(row[0]); + } + return 0; +} + +uint32 ZoneDatabase::GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slotid) { + Corpse* tmp = LoadCharacterCorpse(corpse_id); + uint32 itemid = 0; + + if (tmp) { + itemid = tmp->GetWornItem(slotid); + tmp->DepopPlayerCorpse(); + } + return itemid; +} + +bool ZoneDatabase::LoadCharacterCorpseData(uint32 corpse_id, PlayerCorpse_Struct* pcs){ + std::string query = StringFormat( + "SELECT \n" + "is_locked, \n" + "exp, \n" + "size, \n" + "`level`, \n" + "race, \n" + "gender, \n" + "class, \n" + "deity, \n" + "texture, \n" + "helm_texture, \n" + "copper, \n" + "silver, \n" + "gold, \n" + "platinum, \n" + "hair_color, \n" + "beard_color, \n" + "eye_color_1, \n" + "eye_color_2, \n" + "hair_style, \n" + "face, \n" + "beard, \n" + "drakkin_heritage,\n" + "drakkin_tattoo, \n" + "drakkin_details, \n" + "wc_1, \n" + "wc_2, \n" + "wc_3, \n" + "wc_4, \n" + "wc_5, \n" + "wc_6, \n" + "wc_7, \n" + "wc_8, \n" + "wc_9 \n" + "FROM \n" + "character_corpses\n" + "WHERE `id` = %u LIMIT 1\n", + corpse_id + ); + auto results = QueryDatabase(query); + uint16 i = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + pcs->locked = atoi(row[i++]); // is_locked, + pcs->exp = atoul(row[i++]); // exp, + pcs->size = atoi(row[i++]); // size, + pcs->level = atoi(row[i++]); // `level`, + pcs->race = atoi(row[i++]); // race, + pcs->gender = atoi(row[i++]); // gender, + pcs->class_ = atoi(row[i++]); // class, + pcs->deity = atoi(row[i++]); // deity, + pcs->texture = atoi(row[i++]); // texture, + pcs->helmtexture = atoi(row[i++]); // helm_texture, + pcs->copper = atoul(row[i++]); // copper, + pcs->silver = atoul(row[i++]); // silver, + pcs->gold = atoul(row[i++]); // gold, + pcs->plat = atoul(row[i++]); // platinum, + pcs->haircolor = atoi(row[i++]); // hair_color, + pcs->beardcolor = atoi(row[i++]); // beard_color, + pcs->eyecolor1 = atoi(row[i++]); // eye_color_1, + pcs->eyecolor2 = atoi(row[i++]); // eye_color_2, + pcs->hairstyle = atoi(row[i++]); // hair_style, + pcs->face = atoi(row[i++]); // face, + pcs->beard = atoi(row[i++]); // beard, + pcs->drakkin_heritage = atoul(row[i++]); // drakkin_heritage, + pcs->drakkin_tattoo = atoul(row[i++]); // drakkin_tattoo, + pcs->drakkin_details = atoul(row[i++]); // drakkin_details, + pcs->item_tint[0].color = atoul(row[i++]); // wc_1, + pcs->item_tint[1].color = atoul(row[i++]); // wc_2, + pcs->item_tint[2].color = atoul(row[i++]); // wc_3, + pcs->item_tint[3].color = atoul(row[i++]); // wc_4, + pcs->item_tint[4].color = atoul(row[i++]); // wc_5, + pcs->item_tint[5].color = atoul(row[i++]); // wc_6, + pcs->item_tint[6].color = atoul(row[i++]); // wc_7, + pcs->item_tint[7].color = atoul(row[i++]); // wc_8, + pcs->item_tint[8].color = atoul(row[i++]); // wc_9 + } + query = StringFormat( + "SELECT \n" + "equip_slot, \n" + "item_id, \n" + "charges, \n" + "aug_1, \n" + "aug_2, \n" + "aug_3, \n" + "aug_4, \n" + "aug_5, \n" + "attuned \n" + "FROM \n" + "character_corpse_items \n" + "WHERE `corpse_id` = %u\n" + , + corpse_id + ); + results = QueryDatabase(query); + + i = 0; + pcs->itemcount = results.RowCount(); + uint16 r = 0; + for (auto row = results.begin(); row != results.end(); ++row) { + memset(&pcs->items[i], 0, sizeof (player_lootitem::ServerLootItem_Struct)); + pcs->items[i].equip_slot = atoi(row[r++]); // equip_slot, + pcs->items[i].item_id = atoul(row[r++]); // item_id, + pcs->items[i].charges = atoi(row[r++]); // charges, + pcs->items[i].aug_1 = atoi(row[r++]); // aug_1, + pcs->items[i].aug_2 = atoi(row[r++]); // aug_2, + pcs->items[i].aug_3 = atoi(row[r++]); // aug_3, + pcs->items[i].aug_4 = atoi(row[r++]); // aug_4, + pcs->items[i].aug_5 = atoi(row[r++]); // aug_5, + r = 0; + i++; + } + + return true; +} + +Corpse* ZoneDatabase::SummonBuriedCharacterCorpses(uint32 char_id, uint32 dest_zone_id, uint16 dest_instance_id, float dest_x, float dest_y, float dest_z, float dest_heading) { + Corpse* NewCorpse = 0; + std::string query = StringFormat( + "SELECT `id`, `charname`, `time_of_death`, `is_rezzed` FROM `character_corpses` WHERE `charid` = '%u' AND `is_buried` = 1 ORDER BY `time_of_death` LIMIT 1", + char_id + ); + auto results = QueryDatabase(query); + + for (auto row = results.begin(); row != results.end(); ++row) { + NewCorpse = Corpse::LoadCharacterCorpseEntity( + atoul(row[0]), // uint32 in_dbid + char_id, // uint32 in_charid + row[1], // char* in_charname + dest_x, // float in_x + dest_y, // float in_y + dest_z, // float in_z + dest_heading, // float in_heading + row[2], // char* time_of_death + atoi(row[3]) == 1, // bool rezzed + false // bool was_at_graveyard + ); + if (NewCorpse) { + entity_list.AddCorpse(NewCorpse); + NewCorpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); + NewCorpse->Spawn(); + if (!UnburyCharacterCorpse(NewCorpse->GetCorpseDBID(), dest_zone_id, dest_instance_id, dest_x, dest_y, dest_z, dest_heading)) + LogFile->write(EQEMuLog::Error, "Unable to unbury a summoned player corpse for character id %u.", char_id); + } + } + + return NewCorpse; +} + +bool ZoneDatabase::SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zone_id, uint16 dest_instance_id, float dest_x, float dest_y, float dest_z, float dest_heading) { + Corpse* NewCorpse = 0; + int CorpseCount = 0; + + std::string query = StringFormat( + "UPDATE character_corpses SET zone_id = %i, instance_id = %i, x = %f, y = %f, z = %f, heading = %f, is_buried = 0, was_at_graveyard = 0 WHERE charid = %i", + dest_zone_id, dest_instance_id, dest_x, dest_y, dest_z, dest_heading, char_id + ); + auto results = QueryDatabase(query); + + query = StringFormat( + "SELECT `id`, `charname`, `time_of_death`, `is_rezzed` FROM `character_corpses` WHERE `charid` = '%u'" + "ORDER BY time_of_death", + char_id + ); + results = QueryDatabase(query); + + for (auto row = results.begin(); row != results.end(); ++row) { + NewCorpse = Corpse::LoadCharacterCorpseEntity( + atoul(row[0]), + char_id, + row[1], + dest_x, + dest_y, + dest_z, + dest_heading, + row[2], + atoi(row[3]) == 1, + false); + if (NewCorpse) { + entity_list.AddCorpse(NewCorpse); + NewCorpse->SetDecayTimer(RuleI(Character, CorpseDecayTimeMS)); + NewCorpse->Spawn(); + ++CorpseCount; + } + else{ + LogFile->write(EQEMuLog::Error, "Unable to construct a player corpse for character id %u.", char_id); + } + } + + return (CorpseCount > 0); +} + +bool ZoneDatabase::UnburyCharacterCorpse(uint32 db_id, uint32 new_zone_id, uint16 new_instance_id, float new_x, float new_y, float new_z, float new_heading) { + std::string query = StringFormat( + "UPDATE `character_corpses` SET `is_buried` = 0, `zone_id` = %u, `instance_id` = %u, `x` = %f, `y` = %f, `z` = %f, `heading` = %f, `time_of_death` = Now(), `was_at_graveyard` = 0 WHERE `id` = %u", + new_zone_id, new_instance_id, new_x, new_y, new_z, new_heading, db_id + ); + auto results = QueryDatabase(query); + if (results.Success() && results.RowsAffected() != 0){ + return true; + } + return false; +} + +Corpse* ZoneDatabase::LoadCharacterCorpse(uint32 player_corpse_id) { + Corpse* NewCorpse = 0; + std::string query = StringFormat( + "SELECT `id`, `charid`, `charname`, `x`, `y`, `z`, `heading`, `time_of_death`, `is_rezzed`, `was_at_graveyard` FROM `character_corpses` WHERE `id` = '%u' LIMIT 1", + player_corpse_id + ); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + NewCorpse = Corpse::LoadCharacterCorpseEntity( + atoul(row[0]), // id uint32 in_dbid + atoul(row[1]), // charid uint32 in_charid + row[2], // char_name + atof(row[3]), // x float in_x + atof(row[4]), // y float in_y + atof(row[5]), // z float in_z + atof(row[6]), // heading float in_heading + row[7], // time_of_death char* time_of_death + atoi(row[8]) == 1, // is_rezzed bool rezzed + atoi(row[9]) // was_at_graveyard bool was_at_graveyard + ); + entity_list.AddCorpse(NewCorpse); + } + return NewCorpse; +} + +bool ZoneDatabase::LoadCharacterCorpses(uint32 zone_id, uint16 instance_id) { + std::string query; + if (!RuleB(Zone, EnableShadowrest)){ + query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, was_at_graveyard FROM character_corpses WHERE zone_id='%u' AND instance_id='%u'", zone_id, instance_id); + } + else{ + query = StringFormat("SELECT id, charid, charname, x, y, z, heading, time_of_death, is_rezzed, 0 as was_at_graveyard FROM character_corpses WHERE zone_id='%u' AND instance_id='%u' AND is_buried=0", zone_id, instance_id); + } + + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + entity_list.AddCorpse( + Corpse::LoadCharacterCorpseEntity( + atoul(row[0]), // id uint32 in_dbid + atoul(row[1]), // charid uint32 in_charid + row[2], // char_name + atof(row[3]), // x float in_x + atof(row[4]), // y float in_y + atof(row[5]), // z float in_z + atof(row[6]), // heading float in_heading + row[7], // time_of_death char* time_of_death + atoi(row[8]) == 1, // is_rezzed bool rezzed + atoi(row[9])) + ); + } + + return true; +} + +uint32 ZoneDatabase::GetFirstCorpseID(uint32 char_id) { + std::string query = StringFormat("SELECT `id` FROM `character_corpses` WHERE `charid` = '%u' AND `is_buried` = 0 ORDER BY `time_of_death` LIMIT 1", char_id); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + return atoi(row[0]); + } + return 0; +} + +bool ZoneDatabase::DeleteItemOffCharacterCorpse(uint32 db_id, uint32 equip_slot, uint32 item_id){ + std::string query = StringFormat("DELETE FROM `character_corpse_items` WHERE `corpse_id` = %u AND equip_slot = %u AND item_id = %u", db_id, equip_slot, item_id); + auto results = QueryDatabase(query); + if (results.Success() && results.RowsAffected() != 0){ + return true; + } + return false; +} + +bool ZoneDatabase::BuryCharacterCorpse(uint32 db_id) { + std::string query = StringFormat("UPDATE `character_corpses` SET `is_buried` = 1 WHERE `id` = %u", db_id); + auto results = QueryDatabase(query); + if (results.Success() && results.RowsAffected() != 0){ + return true; + } + return false; +} + +bool ZoneDatabase::BuryAllCharacterCorpses(uint32 char_id) { + std::string query = StringFormat("SELECT `id` FROM `character_corpses` WHERE `charid` = %u", char_id); + auto results = QueryDatabase(query); + for (auto row = results.begin(); row != results.end(); ++row) { + BuryCharacterCorpse(atoi(row[0])); + return true; + } + return false; +} + +bool ZoneDatabase::DeleteCharacterCorpse(uint32 db_id) { + std::string query = StringFormat("DELETE FROM `character_corpses` WHERE `id` = %d", db_id); + auto results = QueryDatabase(query); + if (results.Success() && results.RowsAffected() != 0){ + return true; + } + return false; +} diff --git a/zone/zonedb.h b/zone/zonedb.h index 22aa0eba4..1c103074a 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -3,9 +3,25 @@ #include "../common/shareddb.h" #include "../common/eq_packet_structs.h" -#include "../common/loottable.h" -#include "zonedump.h" #include "../common/faction.h" + +class Client; +class Corpse; +class Merc; +class NPC; +class Petition; +class Spawn2; +class SpawnGroupList; +class ItemInst; +struct CharacterEventLog_Struct; +struct Door; +struct ExtendedProfile_Struct; +struct NPCType; +struct PlayerCorpse_Struct; +struct ZonePoint; +struct npcDecayTimes_Struct; +template class LinkedList; + //#include "doors.h" struct wplist { @@ -165,6 +181,7 @@ struct MercInfo { bool IsSuspended; uint32 MercTimerRemaining; uint8 Gender; + float MercSize; int32 State; uint32 Stance; int32 hp; @@ -199,12 +216,6 @@ struct ClientMercEntry { uint32 npcid; }; -class ItemInst; -struct FactionMods; -struct FactionValue; -struct LootTable_Struct; - - class ZoneDatabase : public SharedDatabase { typedef std::list ItemList; public: @@ -212,9 +223,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,168 +232,183 @@ 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); - ItemInst* LoadSingleTraderItem(uint32 char_id, int uniqueid); + void UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice); void DeleteTraderItem(uint32 char_id); void DeleteTraderItem(uint32 char_id,uint16 slot_id); + + ItemInst* LoadSingleTraderItem(uint32 char_id, int uniqueid); 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 - */ + /* General Character Related Stuff */ bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs); uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs); - bool GetAccountInfoForLogin(uint32 account_id, int16* admin = 0, char* account_name = 0, - uint32* lsaccountid = 0, uint8* gmspeed = 0, bool* revoked = 0, bool* gmhideme = 0); - 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); - bool GetCharacterInfoForLogin(const char* name, 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); + 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 - */ - 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); - uint32 UpdatePlayerCorpse(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, uchar* data, uint32 datasize, float x, float y, float z, float heading, bool rezzed = false); - void MarkCorpseAsRezzed(uint32 dbid); - bool BuryPlayerCorpse(uint32 dbid); - bool BuryAllPlayerCorpses(uint32 charid); - bool DeletePlayerCorpse(uint32 dbid); - uint32 GetPlayerBurriedCorpseCount(uint32 char_id); - Corpse* SummonBurriedPlayerCorpse(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, float dest_x, float dest_y, float dest_z, float dest_heading); - bool SummonAllPlayerCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, float dest_x, float dest_y, float dest_z, float dest_heading); - bool SummonAllGraveyardCorpses(uint32 cur_zoneid, uint32 dest_zoneid, uint16 dest_instanceid, float dest_x, float dest_y, float dest_z, float dest_heading); - Corpse* LoadPlayerCorpse(uint32 player_corpse_id); - bool UnburyPlayerCorpse(uint32 dbid, uint32 new_zoneid, uint16 dest_instanceid, float new_x, float new_y, float new_z, float new_heading); - bool LoadPlayerCorpses(uint32 iZoneID, uint16 iInstanceID); - uint32 GraveyardPlayerCorpse(uint32 dbid, uint32 zoneid, uint16 instanceid, float x, float y, float z, float heading); - uint32 NewGraveyardRecord(uint32 graveyard_zoneid, float graveyard_x, float graveyard_y, float graveyard_z, float graveyard_heading); - uint32 AddGraveyardIDToZone(uint32 zone_id, uint32 graveyard_id); - bool DeleteGraveyard(uint32 zone_id, uint32 graveyard_id); - uint32 GetFirstCorpseID(uint32 char_id); - uint32 GetPlayerCorpseCount(uint32 char_id); - uint32 GetPlayerCorpseID(uint32 char_id, uint8 corpse); - uint32 GetPlayerCorpseItemAt(uint32 corpse_id, uint16 slotid); - uint32 GetPlayerCorpseTimeLeft(uint8 corpse, uint8 type); + /* Corpses */ + bool DeleteItemOffCharacterCorpse(uint32 db_id, uint32 equip_slot, uint32 item_id); + uint32 GetCharacterCorpseItemCount(uint32 corpse_id); + bool LoadCharacterCorpseData(uint32 corpse_id, PlayerCorpse_Struct* pcs); + Corpse* LoadCharacterCorpse(uint32 player_corpse_id); + Corpse* SummonBuriedCharacterCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, float dest_x, float dest_y, float dest_z, float dest_heading); + void MarkCorpseAsRezzed(uint32 dbid); + bool GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes); + bool BuryCharacterCorpse(uint32 dbid); + bool BuryAllCharacterCorpses(uint32 charid); + bool DeleteCharacterCorpse(uint32 dbid); + bool SummonAllCharacterCorpses(uint32 char_id, uint32 dest_zoneid, uint16 dest_instanceid, float dest_x, float dest_y, float dest_z, float dest_heading); + bool SummonAllGraveyardCorpses(uint32 cur_zoneid, uint32 dest_zoneid, uint16 dest_instanceid, float dest_x, float dest_y, float dest_z, float dest_heading); + bool UnburyCharacterCorpse(uint32 dbid, uint32 new_zoneid, uint16 dest_instanceid, float new_x, float new_y, float new_z, float new_heading); + bool LoadCharacterCorpses(uint32 iZoneID, uint16 iInstanceID); + bool DeleteGraveyard(uint32 zone_id, uint32 graveyard_id); + uint32 GetCharacterCorpseDecayTimer(uint32 corpse_db_id); + uint32 GetCharacterBuriedCorpseCount(uint32 char_id); + uint32 SendCharacterCorpseToGraveyard(uint32 dbid, uint32 zoneid, uint16 instanceid, float x, float y, float z, float heading); + uint32 CreateGraveyardRecord(uint32 graveyard_zoneid, float graveyard_x, float graveyard_y, float graveyard_z, float graveyard_heading); + uint32 AddGraveyardIDToZone(uint32 zone_id, uint32 graveyard_id); + uint32 SaveCharacterCorpse(uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, float x, float y, float z, float heading); + uint32 UpdateCharacterCorpse(uint32 dbid, uint32 charid, const char* charname, uint32 zoneid, uint16 instanceid, PlayerCorpse_Struct* dbpc, float x, float y, float z, float heading, bool rezzed = false); + uint32 GetFirstCorpseID(uint32 char_id); + uint32 GetCharacterCorpseCount(uint32 char_id); + uint32 GetCharacterCorpseID(uint32 char_id, uint8 corpse); + uint32 GetCharacterCorpseItemAt(uint32 corpse_id, uint16 slotid); + uint32 GetPlayerCorpseTimeLeft(uint8 corpse, uint8 type); - /* - * 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); + /* 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); //needed for factions Dec, 16 2001 + bool GetFactionName(int32 faction_id, char* name, uint32 buflen); // needed for factions Dec, 16 2001 + bool GetFactionIdsForNPC(uint32 nfl_id, std::list *faction_list, int32* primary_faction = 0); // improve faction handling + bool SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list); // needed for factions Dec, 16 2001 + bool LoadFactionData(); - /* - * AAs - */ - bool LoadAAEffects(); - bool LoadAAEffects2(); - bool LoadSwarmSpells(); - SendAA_Struct* GetAASkillVars(uint32 skill_id); - uint8 GetTotalAALevels(uint32 skill_id); - uint32 GetSizeAA(); - uint32 CountAAs(); - void LoadAAs(SendAA_Struct **load); - uint32 CountAAEffects(); - void FillAAEffects(SendAA_Struct* aa_struct); + /* AAs */ + bool LoadAAEffects(); + bool LoadAAEffects2(); + bool LoadSwarmSpells(); + SendAA_Struct*GetAASkillVars(uint32 skill_id); + uint8 GetTotalAALevels(uint32 skill_id); + uint32 GetSizeAA(); + uint32 CountAAs(); + void LoadAAs(SendAA_Struct **load); + uint32 CountAAEffects(); + void FillAAEffects(SendAA_Struct* aa_struct); - /* - * 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); - bool UpdateZoneSafeCoords(const char* zonename, float x, float y, float z); - uint8 GetUseCFGSafeCoords(); - int getZoneShutDownDelay(uint32 zoneID, uint32 version); + /* 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); + bool UpdateZoneSafeCoords(const char* zonename, float x, float y, float z); + uint8 GetUseCFGSafeCoords(); + int getZoneShutDownDelay(uint32 zoneID, uint32 version); - /* - * 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); - Spawn2* LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft); - bool CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, float heading, float x, float y, float z, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value); - void UpdateSpawn2Timeleft(uint32 id, uint16 instance_id,uint32 timeleft); - uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id); - void UpdateSpawn2Status(uint32 id, uint8 new_status); + /* 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); + Spawn2* LoadSpawn2(LinkedList &spawn2_list, uint32 spawn2id, uint32 timeleft); + bool CreateSpawn2(Client *c, uint32 spawngroup, const char* zone, float heading, float x, float y, float z, uint32 respawn, uint32 variance, uint16 condition, int16 cond_value); + void UpdateSpawn2Timeleft(uint32 id, uint16 instance_id,uint32 timeleft); + uint32 GetSpawnTimeLeft(uint32 id, uint16 instance_id); + void UpdateSpawn2Status(uint32 id, uint8 new_status); - /* - * 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); -// uint32 AddWP(Client *c, uint32 sg2, uint16 grid_num, uint8 wp_num, float xpos, float ypos, float zpos, uint32 pause, float xpos1, float ypos1, float zpos1, int type1, int type2,uint16 zoneid); - void AddWP(Client *c, uint32 gridid, uint32 wpnum, float xpos, float ypos, float zpos, uint32 pause, uint16 zoneid, float heading); - uint32 AddWPForSpawn(Client *c, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading); - void ModifyGrid(Client *c, bool remove, uint32 id, uint8 type = 0, uint8 type2 = 0,uint16 zoneid = 0); - void ModifyWP(Client *c, uint32 grid_id, uint32 wp_num, float xpos, float ypos, float zpos, uint32 script=0,uint16 zoneid =0); - uint8 GetGridType(uint32 grid,uint32 zoneid); - uint8 GetGridType2(uint32 grid, uint16 zoneid); - bool GetWaypoints(uint32 grid, uint16 zoneid, uint32 num, wplist* wp); - void AssignGrid(Client *client, float x, float y, uint32 id); - int GetHighestGrid(uint32 zoneid); - int GetHighestWaypoint(uint32 zoneid, uint32 gridid); + /* 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); + void AddWP(Client *c, uint32 gridid, uint32 wpnum, float xpos, float ypos, float zpos, uint32 pause, uint16 zoneid, float heading); + uint32 AddWPForSpawn(Client *c, uint32 spawn2id, float xpos, float ypos, float zpos, uint32 pause, int type1, int type2, uint16 zoneid, float heading); + void ModifyGrid(Client *c, bool remove, uint32 id, uint8 type = 0, uint8 type2 = 0, uint16 zoneid = 0); + void ModifyWP(Client *c, uint32 grid_id, uint32 wp_num, float xpos, float ypos, float zpos, uint32 script = 0, uint16 zoneid = 0); + uint8 GetGridType(uint32 grid, uint32 zoneid); + uint8 GetGridType2(uint32 grid, uint16 zoneid); + bool GetWaypoints(uint32 grid, uint16 zoneid, uint32 num, wplist* wp); + void AssignGrid(Client *client, float x, float y, uint32 id); + int GetHighestGrid(uint32 zoneid); + int GetHighestWaypoint(uint32 zoneid, uint32 gridid); - /* - * 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 - 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); - bool GetBasePetItems(int32 equipmentset, uint32 *items); - void AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat); - void AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop); - uint32 GetMaxNPCSpellsID(); - uint32 GetMaxNPCSpellsEffectsID(); - DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); - DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID); + /* NPCs */ + + 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); + bool GetBasePetItems(int32 equipmentset, uint32 *items); + void AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat); + void AddLootDropToNPC(NPC* npc, uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop); + uint32 GetMaxNPCSpellsID(); + uint32 GetMaxNPCSpellsEffectsID(); - /* - * Mercs - */ + DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID); + DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID); + const NPCType* GetNPCType(uint32 id); + + /* Mercs */ const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel); void LoadMercEquipment(Merc *merc); void SaveMercBuffs(Merc *merc); @@ -393,12 +417,8 @@ public: bool LoadCurrentMerc(Client *c); bool SaveMerc(Merc *merc); bool DeleteMerc(uint32 merc_id); - //void LoadMercTypesForMercMerchant(NPC *merchant); - //void LoadMercsForMercMerchant(NPC *merchant); - /* - * Petitions - */ + /* Petitions */ void UpdateBug(BugStruct* bug); void UpdateBug(PetitionBug_Struct* bug); void DeletePetitionFromDB(Petition* wpet); @@ -406,32 +426,23 @@ 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 */ uint32 GetZoneFishing(uint32 ZoneID, uint8 skill, uint32 &npc_id, uint8 &npc_chance); void UpdateRecipeMadecount(uint32 recipe_id, uint32 char_id, uint32 madecount); - bool EnableRecipe(uint32 recipe_id); - bool DisableRecipe(uint32 recipe_id); + 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); @@ -442,66 +453,48 @@ public: int32 GetDoorsCount(uint32* oMaxID, const char *zone_name, int16 version); int32 GetDoorsCountPlusOne(const char *zone_name, int16 version); 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 - */ + 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 */ 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 4d1bf8491..000000000 --- a/zone/zonedbasync.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "../common/debug.h" -#include -#include "entity.h" -#include "masterentity.h" -#include "../common/StringUtil.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/zonedump.h b/zone/zonedump.h index 98841630a..6d7e67b04 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -28,18 +28,16 @@ spawn2 mediumblob, npcs mediumblob, npc_loot mediumblob, gmspawntype mediumblob, #define ZONEDUMP_H #include "../common/faction.h" #include "../common/eq_packet_structs.h" -#include "../common/Item.h" +#include "../common/item.h" #pragma pack(1) struct NPCType { char name[64]; - char lastname[70]; - + char lastname[70]; int32 cur_hp; - int32 max_hp; - + int32 max_hp; float size; float runspeed; uint8 gender; @@ -60,23 +58,23 @@ struct NPCType uint32 adventure_template; uint32 trap_template; uint8 light; //not loaded from DB - uint16 AC; + uint32 AC; uint32 Mana; //not loaded from DB - uint16 ATK; //not loaded from DB - uint16 STR; - uint16 STA; - uint16 DEX; - uint16 AGI; - uint16 INT; - uint16 WIS; - uint16 CHA; - int16 MR; - int16 FR; - int16 CR; - int16 PR; - int16 DR; - int16 Corrup; - int16 PhR; + uint32 ATK; //not loaded from DB + uint32 STR; + uint32 STA; + uint32 DEX; + uint32 AGI; + uint32 INT; + uint32 WIS; + uint32 CHA; + int32 MR; + int32 FR; + int32 CR; + int32 PR; + int32 DR; + int32 Corrup; + int32 PhR; uint8 haircolor; uint8 beardcolor; uint8 eyecolor1; // the eyecolors always seem to be the same, maybe left and right eye? @@ -91,7 +89,7 @@ struct NPCType uint32 min_dmg; uint32 max_dmg; int16 attack_count; - char special_abilities[512]; + char special_abilities[512]; uint16 d_meele_texture1; uint16 d_meele_texture2; char ammo_idfile[30]; @@ -111,6 +109,7 @@ struct NPCType uint8 spawn_limit; //only this many may be in zone at a time (0=no limit) uint8 mount_color; //only used by horse class float attack_speed; //%+- on attack delay of the mob. + uint8 attack_delay; //delay between attacks in 10ths of a second int accuracy_rating; //10 = 1% accuracy int avoidance_rating; //10 = 1% avoidance bool findable; //can be found with find command @@ -126,40 +125,33 @@ struct NPCType float healscale; bool no_target_hotkey; bool raid_target; + uint8 probability; }; -/* -Below are the blob structures for saving player corpses to the database --Quagmire - -create table player_corpses (id int(11) unsigned not null auto_increment primary key, charid int(11) unsigned not null, -charname varchar(30) not null, zonename varchar(16)not null, x float not null, y float not null, z float not null, -heading float not null, data blob not null, time timestamp(14), index zonename (zonename)); -*/ - -namespace player_lootitem -{ +namespace player_lootitem { struct ServerLootItem_Struct { - uint32 item_id; - int16 equipSlot; - uint8 charges; - uint16 lootslot; - uint32 aug1; - uint32 aug2; - uint32 aug3; - uint32 aug4; - uint32 aug5; + uint32 item_id; + int16 equip_slot; + uint16 charges; + uint16 lootslot; + uint32 aug_1; + uint32 aug_2; + uint32 aug_3; + uint32 aug_4; + uint32 aug_5; + uint8 min_level; // + uint8 max_level; // }; } -struct DBPlayerCorpse_Struct { +struct PlayerCorpse_Struct { uint32 crc; bool locked; uint32 itemcount; uint32 exp; float size; uint8 level; - uint8 race; + uint32 race; uint8 gender; uint8 class_; uint8 deity; @@ -181,39 +173,9 @@ struct DBPlayerCorpse_Struct { uint32 drakkin_tattoo; uint32 drakkin_details; player_lootitem::ServerLootItem_Struct items[0]; + //std::list items; }; -namespace classic_db -{ - struct DBPlayerCorpse_Struct { - uint32 crc; - bool locked; - uint32 itemcount; - uint32 exp; - float size; - uint8 level; - uint8 race; - uint8 gender; - uint8 class_; - uint8 deity; - uint8 texture; - uint8 helmtexture; - uint32 copper; - uint32 silver; - uint32 gold; - uint32 plat; - Color_Struct item_tint[9]; - uint8 haircolor; - uint8 beardcolor; - uint8 eyecolor1; - uint8 eyecolor2; - uint8 hairstyle; - uint8 face; - uint8 beard; - player_lootitem::ServerLootItem_Struct items[0]; - }; -} - struct Door { uint32 db_id; uint8 door_id; @@ -226,7 +188,7 @@ struct Door { int incline; uint8 opentype; uint32 guild_id; - uint16 lockpick; + uint16 lock_pick; uint32 keyitem; uint8 nokeyring; uint8 trigger_door; diff --git a/zone/zoning.cpp b/zone/zoning.cpp index f69598ce5..71ea65dc8 100644 --- a/zone/zoning.cpp +++ b/zone/zoning.cpp @@ -15,17 +15,17 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "../common/debug.h" - -#include "zone.h" -#include "worldserver.h" -#include "masterentity.h" -#include "../common/packet_dump.h" #include "../common/rulesys.h" -#include "../common/StringUtil.h" -#include "StringIDs.h" -#include "QuestParserCollection.h" +#include "../common/string_util.h" +#include "queryserv.h" +#include "quest_parser_collection.h" +#include "string_ids.h" +#include "worldserver.h" +#include "zone.h" +extern QueryServ* QServ; extern WorldServer worldserver; extern Zone* zone; @@ -69,9 +69,11 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { break; case GateToBindPoint: target_zone_id = m_pp.binds[0].zoneId; + target_instance_id = m_pp.binds[0].instance_id; break; case ZoneToBindPoint: target_zone_id = m_pp.binds[0].zoneId; + target_instance_id = m_pp.binds[0].instance_id; break; case ZoneSolicited: //we told the client to zone somewhere, so we know where they are going. target_zone_id = zonesummon_id; @@ -147,7 +149,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { } } - //make sure its a valid zone. + /* Check for Valid Zone */ const char *target_zone_name = database.GetZoneName(target_zone_id); if(target_zone_name == nullptr) { //invalid zone... @@ -157,7 +159,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) { return; } - //load up the safe coords, restrictions, and verify the zone name + /* Load up the Safe Coordinates, restrictions and verify the zone name*/ float safe_x, safe_y, safe_z; int16 minstatus = 0; uint8 minlevel = 0; @@ -327,15 +329,19 @@ void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instanc SendLogoutPackets(); - //dont clear aggro until the zone is successful + /* QS: PlayerLogZone */ + if (RuleB(QueryServ, PlayerLogZone)){ + std::string event_desc = StringFormat("Zoning :: zoneid:%u instid:%u x:%4.2f y:%4.2f z:%4.2f h:%4.2f zonemode:%d from zoneid:%u instid:%i", zone_id, instance_id, dest_x, dest_y, dest_z, dest_h, zone_mode, this->GetZoneID(), this->GetInstanceID()); + QServ->PlayerLogEvent(Player_Log_Zoning, this->CharacterID(), event_desc); + } + + /* Dont clear aggro until the zone is successful */ entity_list.RemoveFromHateLists(this); if(this->GetPet()) entity_list.RemoveFromHateLists(this->GetPet()); - LogFile->write(EQEMuLog::Status, "Zoning '%s' to: %s (%i) - (%i) x=%f, y=%f, z=%f", - m_pp.name, database.GetZoneName(zone_id), zone_id, instance_id, - dest_x, dest_y, dest_z); + LogFile->write(EQEMuLog::Status, "Zoning '%s' to: %s (%i) - (%i) x=%f, y=%f, z=%f", m_pp.name, database.GetZoneName(zone_id), zone_id, instance_id, dest_x, dest_y, dest_z); //set the player's coordinates in the new zone so they have them //when they zone into it @@ -348,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 @@ -543,9 +549,35 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z break; } - if(ReadyToZone) { + if (ReadyToZone) + { + //if client is looting, we need to send an end loot + if (IsLooting()) + { + Entity* entity = entity_list.GetID(entity_id_being_looted); + if (entity == 0) + { + Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)"); + if (GetClientVersion() >= EQClientSoD) + Corpse::SendEndLootErrorPacket(this); + else + Corpse::SendLootReqErrorPacket(this); + } + else if (!entity->IsCorpse()) + { + Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())"); + Corpse::SendLootReqErrorPacket(this); + } + else + { + Corpse::SendEndLootErrorPacket(this); + entity->CastToCorpse()->EndLoot(this, nullptr); + } + SetLooting(0); + } + zone_mode = zm; - if(zm == ZoneToBindPoint) { + if (zm == ZoneToBindPoint) { EQApplicationPacket* outapp = new EQApplicationPacket(OP_ZonePlayerToBind, sizeof(ZonePlayerToBind_Struct) + iZoneNameLength); ZonePlayerToBind_Struct* gmg = (ZonePlayerToBind_Struct*) outapp->pBuffer; @@ -685,19 +717,22 @@ void NPC::Gate() { Mob::Gate(); } -void Client::SetBindPoint(int to_zone, float new_x, float new_y, float new_z) { +void Client::SetBindPoint(int to_zone, int to_instance, float new_x, float new_y, float new_z) { if (to_zone == -1) { m_pp.binds[0].zoneId = zone->GetZoneID(); + m_pp.binds[0].instance_id = (zone->GetInstanceID() != 0 && zone->IsInstancePersistent()) ? zone->GetInstanceID() : 0; m_pp.binds[0].x = x_pos; m_pp.binds[0].y = y_pos; m_pp.binds[0].z = z_pos; } else { m_pp.binds[0].zoneId = to_zone; + m_pp.binds[0].instance_id = to_instance; m_pp.binds[0].x = new_x; m_pp.binds[0].y = new_y; m_pp.binds[0].z = new_z; } + database.SaveCharacterBindPoint(this->CharacterID(), m_pp.binds[0].zoneId, m_pp.binds[0].instance_id, m_pp.binds[0].x, m_pp.binds[0].y, m_pp.binds[0].z, 0, 0); } void Client::GoToBind(uint8 bindnum) { @@ -708,13 +743,13 @@ void Client::GoToBind(uint8 bindnum) { // move the client, which will zone them if needed. // ignore restrictions on the zone request..? if(bindnum == 0) - MovePC(m_pp.binds[0].zoneId, 0.0f, 0.0f, 0.0f, 0.0f, 1, GateToBindPoint); + MovePC(m_pp.binds[0].zoneId, m_pp.binds[0].instance_id, 0.0f, 0.0f, 0.0f, 0.0f, 1, GateToBindPoint); else - MovePC(m_pp.binds[bindnum].zoneId, m_pp.binds[bindnum].x, m_pp.binds[bindnum].y, m_pp.binds[bindnum].z, m_pp.binds[bindnum].heading, 1); + MovePC(m_pp.binds[bindnum].zoneId, m_pp.binds[bindnum].instance_id, m_pp.binds[bindnum].x, m_pp.binds[bindnum].y, m_pp.binds[bindnum].z, m_pp.binds[bindnum].heading, 1); } void Client::GoToDeath() { - MovePC(m_pp.binds[0].zoneId, 0.0f, 0.0f, 0.0f, 0.0f, 1, ZoneToBindPoint); + MovePC(m_pp.binds[0].zoneId, m_pp.binds[0].instance_id, 0.0f, 0.0f, 0.0f, 0.0f, 1, ZoneToBindPoint); } void Client::SetZoneFlag(uint32 zone_id) { @@ -723,16 +758,11 @@ void Client::SetZoneFlag(uint32 zone_id) { zone_flags.insert(zone_id); - //update the DB - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - // Retrieve all waypoints for this grid - if(!database.RunQuery(query,MakeAnyLenString(&query, - "INSERT INTO zone_flags (charID,zoneID) VALUES(%d,%d)", - CharacterID(),zone_id),errbuf)) { - LogFile->write(EQEMuLog::Error, "MySQL Error while trying to set zone flag for %s: %s", GetName(), errbuf); - } + std::string query = StringFormat("INSERT INTO zone_flags (charID,zoneID) VALUES(%d,%d)", CharacterID(), zone_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "MySQL Error while trying to set zone flag for %s: %s", GetName(), results.ErrorMessage().c_str()); } void Client::ClearZoneFlag(uint32 zone_id) { @@ -741,39 +771,26 @@ void Client::ClearZoneFlag(uint32 zone_id) { zone_flags.erase(zone_id); - //update the DB - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - // Retrieve all waypoints for this grid - if(!database.RunQuery(query,MakeAnyLenString(&query, - "DELETE FROM zone_flags WHERE charID=%d AND zoneID=%d", - CharacterID(),zone_id),errbuf)) { - LogFile->write(EQEMuLog::Error, "MySQL Error while trying to clear zone flag for %s: %s", GetName(), errbuf); - } + std::string query = StringFormat("DELETE FROM zone_flags WHERE charID=%d AND zoneID=%d", CharacterID(), zone_id); + auto results = database.QueryDatabase(query); + if(!results.Success()) + LogFile->write(EQEMuLog::Error, "MySQL Error while trying to clear zone flag for %s: %s", GetName(), results.ErrorMessage().c_str()); + } void Client::LoadZoneFlags() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; // Retrieve all waypoints for this grid - if(database.RunQuery(query,MakeAnyLenString(&query, - "SELECT zoneID from zone_flags WHERE charID=%d", - CharacterID()),errbuf,&result)) - { - while((row = mysql_fetch_row(result))) { - zone_flags.insert(atoi(row[0])); - } - mysql_free_result(result); - } - else // DB query error! - { - LogFile->write(EQEMuLog::Error, "MySQL Error while trying to load zone flags for %s: %s", GetName(), errbuf); - } - safe_delete_array(query); + std::string query = StringFormat("SELECT zoneID from zone_flags WHERE charID=%d", CharacterID()); + auto results = database.QueryDatabase(query); + if (!results.Success()) { + LogFile->write(EQEMuLog::Error, "MySQL Error while trying to load zone flags for %s: %s", GetName(), results.ErrorMessage().c_str()); + return; + } + + for(auto row = results.begin(); row != results.end(); ++row) + zone_flags.insert(atoi(row[0])); } bool Client::HasZoneFlag(uint32 zone_id) const {